Search

Categories

Links

Referers

Browsing source code online

Sep 19 2006, 05:22:47 PM PDT »Java
Kohsuke has been playing with all the new compiler API (JSR 199, JSR 269, and the Tree API). He created a cool webbased source code browser. For more details on the project, see the project page.

Good books on compiler design or implementation

Aug 31 2006, 09:06:50 PM PDT »General Comments [2]
Herr Helfert sent me a question asking me if I can recommend any good books on compiler design or implementation.

He already mentioned the Dragon Book which is a must.

I also understand that Appel's Modern Compiler Implementation series is popular but I have never read them myself. The C version looked a bit thin compared to the Dragon Book (but some may argue that one has too much detail).

That's about all the books I know on compilers. If you're interested in compilers, I would also recommend that you contact your local university or college. Many univerities work with tech companies to offer some form of training.

A book that is not really about compilers but that I have found really useful is the GOF book on Design Patterns. I cannot live without Visitor(331).

If you are interested in Java™ programming language compilers or the like, the puzzlers book by Joshua Bloch and Neal Gafter has a number of interesting comments for language designers.

I like books on general software engineering best practices and can especially recommend:

Finally, don't forget to read the mirror paper by Gilad Bracha and David Ungar.

Non-local return and lexical scope

Aug 21 2006, 11:22:59 PM PDT »Java»Language
I posted a proposal on closures a few days ago and the comments can so far be partitioned in two categories: "I like closures" and "I don't understand closures".

The strange thing is that I haven't seen any comments from people that understands closures saying they don't like them. So this makes you think if closures is just a matter of understanding them or not.

This must be what Neal is thinking because he tries to answer the question: what is the point of closures?

I would also recommend that you play a little with Smalltalk or Ruby.

I'm not allowing comments on this one as you can comment on my previous entry on closures or on Neal's blog.

Guide to javac

Aug 18 2006, 07:48:33 PM PDT »Java»Compiler Comments [4]
We have started writing a guide for javac, similar to the guide for javadoc. The plans are to describe JSR 269 (Pluggable Annotation Processing API), JSR 199 (Java™ Compiler API), and the Tree API.

If there is anything you would like to see in such a guide, please let me know within the next few days.

UPDATE: unfortunately, it only became clear to me very recently that I would have to write the guide and it was almost too late for JDK 6. I have added a minimal version that includes links to the manual pages and all the relevant API (this should make it easier to locate the Tree API docs). We hope to improve the guide in the coming month so it is ready for the first update release to JDK 6. The good news is that that gives us more time to incorporate your ideas and I have opened the comments again.

Full Disclosure

Aug 17 2006, 09:38:43 PM PDT »Java»Language Comments [42]
Some clever guys have written up a proposal on closures and been kind enough to put my name on it. I was just sitting in the room trying my best not to look too stupid ;-)

A closure is an anonymous function (aka lambda expression) in which all free variables can be accessed (even if they are not final). Furthermore, in the tradition of Smalltalk, the closure should be able to return directly from the method enclosing the closure definition (aka non-local return).

In the Java™ programming language, closures can be simulated to some extent with anonymous classes but these cannot access non-final local variables and have no way to exit the method containing the definition besides throwing an exception (which should not be used for control flow).

Closures have been a favorite of most Smalltalk freaks and has gotten renewed interest in languages such as Ruby.

Anonymous classes, although useful, can be a tad verbose and tiresome to type so this proposal must address the proper closure of free variables and non-local return as well as suggest a consice syntax.

b92-b96 compiler fixes

Aug 17 2006, 07:35:06 PM PDT »General
Back from vacation: read on for an update on the compiler [Read More]

HotSpot and other compilers

Aug 03 2006, 02:36:07 PM PDT »Java Comments [10]
In a comment on a previous blog entry, Damon Hart-Davis asked if HotSpot is better with classes generated by javac and if it is true that some static transformations (optimization or obfuscation) can impact the performance of your application?

The short answer is that HotSpot is not targeted to javac code but some static transformation can adversely affect the performance of your application. I'll give some more details below.

Damon also mentioned that there had been some fuss about a VM crash caused by some code generated by jikes. I know that all the HotSpot engineers treat VM crashes with utmost urgency and I checked with Ken Russell. Ken remembers one particular crash where jikes used the invokeinterface bytecode to call methods declared in java.lang.Object. This crash was treated with high priority and has since been fixed.

HotSpot is just a piece of software and may contain bugs but that does not mean javac is getting a better deal. However, there are certain factors which can play a role in how good a job HotSpot can do on your application.

Some of the industry benchmarks are based on the combination of compiler and JVM so if HotSpot seems biased towards javac, that may be the reason but not intentional.

On the other hand, many static optimizers will automatically inline method calls. The fundamental problem with inlining method calls is that if the inlined method was never called by method into which it was inlined it will just make the resulting method bigger (and perhaps prevent it from being inlined in other methods). So although a static optimizer think it is optimizing it has no data about how the program behaves at runtime and may make mistakes.

This is a fundamental problem that will make your programs run slower on HotSpot. The name HotSpot comes from the VM's ability to recognize hotspots in the program based on statistics collected while the program is running. By using this data, HotSpot can generally make better decisions than a static optimizer.

Besides hurting HotSpots ability to inline based on live data, big methods resulting from premature inlining can have other negative effects on performance. HotSpot will compile hot methods to native machine code. This code is sensitive to CPU level optimizations such as code caches and branch prediction. I don't know if this is a real life problem.

I checked with Ken and Ross Knippel to see if there could be other problems and they mentioned that obfuscators sometimes makes some very radical transformations of the control flow of your application. This can cause problems for HotSpots ability to detect loops and optimize them. A similar problem could occur if the compiler generated code that looked very different from what javac and similar compilers generate. However, in Mustang, HotSpot will apply a technique called loop rotation to properly place a loop exit that is testing an induction variable. Ross provided this example:

int i = 0;
while (true) {
    stmt1;
    i++;
    if (i >= n) break;
    stmt2;
}

is transformed into

int i = 0;
stmt1;
i++;
if (i < n) {
    do {
        stmt2;
        stmt1;
        i++;
    } while (i < n)
}

b91 compiler fixes

Jun 26 2006, 07:50:41 PM PDT »Java»Compiler Comments [15]
Read on for a list of compiler fixes in b91 [Read More]

b89 compiler fixes

Jun 20 2006, 02:18:19 PM PDT »Java»Compiler Comments [1]
Read on for a list of compiler fixes in b89 and an update on JSR 199 and 269 [Read More]

b87 compiler fixes

Jun 01 2006, 01:28:23 AM PDT »General Comments [3]
I guess JavaOne had an impact after all ;-)
There are no compiler related fixes in b87. Stay tuned for b89.

JSR 269 @ JavaOne

May 30 2006, 03:50:55 AM PDT »Java
photo: JSR 269 expert group
A few members and hangarounds of the JSR 269 expert group (annotation processing) got together at JavaOne in San Francisco. From the left: Bruce, Joe, Peter, Jess, Scott, and Jon. Bruce had brought delicious Kiwi chocolate all the way from New Zealand.

b86 compiler fixes

May 25 2006, 01:39:46 AM PDT »Java»Compiler Comments [14]
Read on for a list of compiler fixes in b86. This includes changes to @Override and that new Sun proprietary API will no longer be available at compile time. Who said that a great week in San Francisco at JavaOne would stop us from bringing you more bug fixes? [Read More]

JavaOne 2006 Slides

May 22 2006, 03:00:00 PM PDT »Java Comments [19]
I have uploaded the slides from the BOF Joe and I did on Best Practices With Generics and Other Java™ Platform 5.0 Language Features (BOF-0160)

JavaOne: questions

May 15 2006, 02:18:47 PM PDT »Java Comments [11]
If there are any topics you would like me to cover in my BOFs on Wednesday, feel free to add a comment below. If I am not able to cover it on Wednesday, I'll follow up on the blog.

JavaOne 2006 BOF

May 13 2006, 07:30:56 PM PDT »Java Comments [2]
I co-host two BOFs at JavaOne this year and be available at the Ask The Experts pod:
Best Practices With Generics and Other Java™ Platform 5.0 Language Features BOF-0160 (Wed 8:30 - 9:20 pm)
This session is a follow-up to BOF-9225 of the 2005 JavaOne conference.

Generic types (generics) were added to the Java™ programming language in the JDK™ 5.0 release (code-named Tiger).

Generic types in the language were designed to allow an unsurpassed level of compatibility, but it is still possible to break compatibility. This presentation examines the underlying implementation technique (erasure) and discusses best practices and how to avoid common pitfalls when "generifying" existing code.
Java™ Programming Language and Compiler Issues for the Java Platform BOF-0159 (Wed 9:30 - 10:20 pm)
A recurring event at the JavaOne conference:

This session discusses Java™ programming language issues and issues involving javac, the compiler for the language.

Any language and compiler issue can be discussed:
  • Java Platform, Standard Edition (Java SE) 5 features
  • Old, well-known features
  • Proposed features for the upcoming Java platform releases (code-named Mustang and Dolphin)
  • Wouldn't it be nice...?
Ask The Experts: Java™ Compiler & Language at the Java SE Community booth (Thu 1 - 2 pm)
Come by if you have any questions on the compiler, the language, JSR 199, or JSR 269.

Joe has posted about JSR 269 events on his blog. I may join him at his booth slot on Tuesday.

@Override

May 10 2006, 08:57:26 PM PDT »Java»Language Comments [20]
I'm currently looking at how javac handles @Override. Consider the current specification of @Override:
Indicates that a method declaration is intended to override a method declaration in a superclass. If a method is annotated with this annotation type but does not override a superclass method, compilers are required to generate an error message.

There are a few subtle points that may escape the casual reader:

  • it says superclass not supertype
  • override and implements are not mutually exclusive

There are a few surprising consequences of this:

    abstract class C1 {
        public abstract void m();
    }
 
    class C2 extends C1 {
        @Override
        public void m() {}
    }

Although the method C2.m implements the method C1.m, it also overrides it. So this is OK according to the current specification and javac accepts it. The next example is even more convoluted:

    public interface Test {
        void m();
    }

    abstract class A implements Test {
        // m() is inherited from Test
    }

    class B extends A {
        @Override
        public void m() {}
    }

This is also legal since m is a member of the abstract class A, B.m is overriding (and implementing) a method in a superclass. However, javac fails to accept this program.

So thought about it and came up with this specification:

Indicates that a method declaration is intended to override a method declaration in a supertype. If a method is annotated with this annotation type but does not override a supertype method, compilers are required to generate an error message

However, this doesn't work as you would expect: although override and implements are not mutually exclusive, override is not a "superset" of implements:

    interface A {
        void m();
    }

    class B implements A {
        public void m() {} // implements but doesn't override
    }

Whereas:

    interface A {
        void m();
    }

    interface B extends A {
        void m(); // overrides but doesn't implement
    }

Confused? Then you're getting there ;-)
So we tried this one:

Indicates that a method declaration is intended to override a method declaration in a supertype. If a method is annotated with this annotation type but does not override or implement a supertype method, compilers are required to generate an error message

However, what about (currently accepted by javac):

    interface Foo {
        @Override
        String toString();
    }

So does Foo.toString override or implement a method in a supertype? Not really, the JLS is a bit convoluted here but basically says that although Object is a direct supertype of Foo, Foo does not inherit Object.toString because Foo itself declares a toString method...

Now what? We thought about it and came up with:

Indicates that a method declaration is intended to override a method declaration in a supertype. If a method is annotated with this annotation type but is not in fact override-equivalent to any method declared in a supertype, compilers are required to generate an error message.

So how is this different? Rather than relying on the definition of override or implements in the JLS we simply say what we intended to say all along: if a similar method exists in a supertype, then you may use @Override.

So then Eugene says: what about visibility. Oh boy:

Indicates that a method declaration is intended to override a method declaration in a supertype. If a method is annotated with this annotation type compilers are required to generate an error message unless either the method is override-equivalent to a [update: public or protected] method in Object, or the method does override or implement a method declared in a supertype.

I hope to have this ready for b86.

b85 compiler fixes

May 08 2006, 11:01:04 PM PDT »Java»Compiler Comments [8]
Read on for a list of compiler fixes in b85 as well as an update on Joe's updates to JSR 269 in b83 [Read More]

Update on b83

May 04 2006, 08:30:00 PM PDT »General Comments [6]
Unfortunately, something went wrong in a merge and a few fixes were lost (it has been corrected). This means that the change from -target 6 to -target 5 missed b83. However, Joe managed to get an updated version of JSR 269 API added to b83 after our normal dead-line. I'll mention these changes when I blog about b85. Just to be clear: it wasn't Joe that accidentally deleted the -target change.

This should demonstrate the value of writing tests for even the most trivial of changes. Did somebody recently mention something about testing...? ;-)

Real Java Script

Apr 30 2006, 10:31:25 PM PDT »Java Comments [6]
A. Sundararajan have used JSR 199 to create a scripting engine for Java. It works by compiling the script (a Java program) in memory using the facilities of javax.tools. The scripting engine can be used in any Java program via JSR 223.

Imagine writing s StarOffice macro in Java :-)

Contributing to javac

Apr 28 2006, 02:32:28 PM PDT »Java»Compiler Comments [1]
We are very happy to see a growing interest in contributing to javac from the members of java.net. However, we are also beginning to see a number of submissions which we have to turn down. Read on for some simple advice that can help you getting your fix accepted [Read More]

b83 compiler fixes

Apr 28 2006, 09:03:32 AM PDT »Java»Compiler Comments [1]
In Mustang b83, these compiler (related) fixes will be included:

6341023: (javac) Tree API: Tree.Kind should have mapping to interface
6341177: (JSR 269) Add IDE toolability API to JSR 269
6402506: (JSR 269) Add ProcessingEnvironment.getSourceVersion
6403466: (javac) javac TaskListener should be informed when annotation processing occurs
6406212: (JSR 269) Syntax of options should be better verified
6406771: (javac) Tree API clients need access to the compiler's line number table
6407257: (javac) javac locks up when encountering cyclic inheritance

Thanks to Joe, Jon, and Wei.

Also, the JDK will be build using -target 5 from now on. Previously, we shipped with -target 6 to allow thorough testing of the new verifier. However, we always planned to ship with -target 5 in Mustang RC. The reason for this is very simple: we want people to upgrade to Mustang as soon as possible. One problem we identified in JDK 5.0 was that customers running any kind of app server will run into a problem if the app server has a built-in compiler that doesn't understand the new class file format. Fortunately, there is a very simple solution to this for Mustang: use the old class file format.

b81 compiler fixes

Apr 16 2006, 04:03:30 PM PDT »Java»Compiler Comments [5]
In Mustang b81, these compiler (related) fixes will be included:

6340951: (javac) Tree API: some literals are identifiers
6365980: (JSR 269) FilerException(null) doesn't throw NPE
6395981: (javac) JSR 199: JavaCompilerTool and Tool must specify version of JLS and JVMS
6395983: (javac) JSR 199: improve documentation of JavaCompilerTool
6396397: (javac) JSR 199: JavaFileManager should extend Flushable
6397044: (javac) JCModifiers.getModifiers() returns incorrect Modifiers set.
6397097: (javac) JSR 199: JavaCompilerTool.CompilationTask is clumsy with respect to "delayed"
6397104: (javac) JSR 199: JavaFileManager.getFileForOutput should have sibling argument
6397286: (javac) TaskListener calls are not protected agains user exceptions
6397348: (javac) JSR 199: supported options
6399894: (javac) JSR 199: WrapperJavaFileObject should extend new class WrapperFileObject
6400204: (javac) JSR 199: StandardJavaFileManager.getJavaObjectsFrom* should throw exception if argument is directory
6400205: (javac) JSR 199: getClassLoader(location) returns null if getLocation(location) returns null
6400207: (javac) JSR 199: JavaFileManager.list and unset location
6400208: (javac) JSR 199: failure mode for inferBinaryName
6400225: (javac) JSR 199: StandardJavaFileManager.getEffectiveLocation
6400267: (javac) JSR 199: specify the exact requirements for relative URIs
6401107: (javac) JSR 199: all methods must return non-null values unless explicitly allowed
6401277: (javac) JSR 199: javax.tools.ToolProvider.getSystemToolClassLoader throws UnsupportedOperationException
6401906: (javac) JSR 199: rename Wrapper* to Forwarding*
6401994: (javac) JSR 199: JavaCompilerTool.CompilationTask.run should not copy Runnable spec
6406164: (JSR 269) ElementKindIterator.hasNext() throws NPE when ElementFilter Iterable methods are used.
6407066: (javac) Paths code should not discard non-existent directories
6410184: (javac) JSR 199: javax.Tools.WrapperJavaFileManager and WrapperJavaFileObject are not reflexive
6410297: (javac) JSR 199: javax.tools.SimpleJavaFileObject.isNameCompatible doesn't work or has wrong spec
6410653: (javac) REGRESSION: javac crashes if -d or -s argument is a file
6411327: (JSR 269) Some typeUtils methods can result in an IllegalArgumentException and this is not documented.

Thanks to Joe, Jon, and Scott.

b79 compiler fixes

Apr 16 2006, 03:48:56 PM PDT »Java»Compiler
In Mustang b79, these compiler (related) fixes will be included:

6345812: (JSR 269) annotation processing breaks with assertion, in Types.isAssignable() method.
6370653: (javac) javac does not use iinc opcode for postfix decrement of local variable
6376083: (JSR 269) should check annotation processor for compatability with specified source version
6376084: (JSR 269) Annotation processor infrastructure should check for unsupported options
6391649: (JSR 269) javax.lang.model.util.Elements.getAllMembers() does not return initializers
6392818: (JSR 269) Add isDeprecated to javax.lang.model.util.Elements
6397298: (JSR 269) RoundEnvironment.getElementsAnnotatedWith(anno) returns null, no empty set
6400986: (JSR 269) RoundEnvironment.getElementsAnnotatedWith() misses some elements
6401264: (JSR 269) Link Character.isIdentifier{Start, Part} to javax.lang.model.SourceVersion.isIdentifier
6407011: (javac) javac crashes in b78 with NPE in DefaultFileManager:293

Thanks to Wei, Scott, Joe, and java.net contributor dmytro_sheyko.

A very special thanks to Tim for doing an extra integration that allowed me to fix 6407011 in less than a week.

Tor on Jackpot

Mar 30 2006, 11:25:22 PM PST »NetBeans
Check out Tor's screenshots of Jackpot. Right now the deadline for Mustang is very close so I don't dare to upgrade to a development version of NetBeans.

Update on b78

Mar 30 2006, 11:19:25 PM PST »Java»Compiler Comments [5]
I just received word that our changes planned for b77 made it into b78.

It was bound to happen

Mar 24 2006, 12:09:52 AM PST »Java»Compiler
My colleagues told me it was a bad idea to post messages about what fixes are going into a build before we have gone through the entire release cycle...

Earlier today Xiomara discovered a nasty problem with Windows and mixed case filenames. We had to rewrite the part of javac that handles files because of an upcoming revision to JSR 199. Good news: we caught the problem before the bits were pushed to java.net. Bad news: we didn't catch it soon enough so we had to back it out in the last minute.

If we're lucky, the changes we had planned for b77 will make it to b78.

Regardless, I'll keep posting what we are putting into the various builds as soon as we have completed our part of the release cycle. I think it provides an interesting view into our daily work.

Jackpot is here

Mar 16 2006, 02:01:00 PM PST »NetBeans
Tom Ball's cool Jackpot project is available for early access. Tom has provided the javac team with a lot of valuable feedback on the Tree API and JSR 269. Jackpot builds on top of javac through these API components and allows you to write your own refactorings. It also makes it very easy for the NetBeans team to provide a whole suite of new refactorings and other cool stuff.

I can't wait for NetBeans 6.0 to go beta...

b77 compiler fixes

Mar 15 2006, 11:04:44 PM PST »Java»Compiler
In Mustang b77, these compiler (related) fixes will be included:

5024091: (javac) (crash) AssertionError when StringBuilder is used with cldc.jar as bootclasspath
6236704: (javac) bad jar files ignored in extension and endorsed dirs
6295519: (javac) javac throws ZipException when you have invalid files in classpath
6306967: (javac) This code compiles but gives VerifyError when run
6327885: (javac) JSR 199: open issues
6346249: (javac) javac Tree API missing functionality
6346453: (JSR 269) Types.directSupertypes implementation returns an invalid list.
6346506: (JSR 269) Elements.getTypeElement(String s) will throw NPE for a unknown class.
6346973: (JSR 269) directSupertypes(Mirror m) causes java.lang.StackOverflowError
6348193: (javac) AS8.1 UR2 BAT test failure with "javac"
6350057: (JSR 269) visitVariableAsLocalVariable is called on enum's parameter of valueOf(String name)
6358024: (javac) TaskListener should be propagated between processing rounds
6358166: (JSR 269) -verbose reports absurd times when annotation processing involved
6361619: (javac) AssertionError from ClassReader
6366196: (javac) regression in CharacterRangeTable format
6392058: (javac) rename internal class Diagnostic to JCDiagnostic
6392118: (javac) mismatch between JavacTaskImpl.context and JSR 269
6394563: (javac) REGRESSION: javac ignores -nowarn switch in 1.5.0_06 for deprecation warnings
6395269: (javac) Paths should use List<File> or List<URI> instead of List<String> to represent a path
6395974: (javac) files are parsed even after failure to find annotation processor is reported

Thanks to Joe, Jon, Neal, Nishant, Scott, and Wei. Especially a big thank you to Jon for helping me getting JSR 199 ready for b77. We pulled some later hours on that one... Unfortunately, this left the wildcard bug I mentioned two weeks ago unfixed.

For those of you using jcov, send happy thoughts to Wei for fixing the javac regressions that broke jcov.

Throwing unchecked exceptions

Mar 02 2006, 07:13:48 PM PST »Java Comments [11]
I'm doing spring cleaning of the compiler API (JSR 199, javax.tools). Yury suggested that while documenting which unchecked exceptions are thrown, perhaps they should not be listed in throws clauses.

So while this is good style:

/** * ... * @throws java.lang.RuntimeException if an unrecoverable error * occurred in a user supplied component. The cause will be the * error in user code. */

This is not:

void run() throws RuntimeException;

So how can I reliably find all unchecked exceptions? Very easily if I use an annotation processor. I used this annotation processor:

@SupportedAnnotationTypes("*") @SupportedOptions("summary") public class FindRuntimeException extends AbstractProcessor { // For summary of unchecked exceptions used in throws clauses final Set<TypeMirror> uncheckedExceptions = new LinkedHashSet<TypeMirror>(); public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { // Get various utility classes final Elements elements = processingEnv.getElementUtils(); final Types types = processingEnv.getTypeUtils(); final Messager log = processingEnv.getMessager(); // Get the types (type mirrors) of RuntimeException and Error final TypeMirror runtimeException = elements.getTypeElement("java.lang.RuntimeException").asType(); final TypeMirror error = elements.getTypeElement("java.lang.Error").asType(); // Scanner for traversing the structure of a class, interface, etc. ElementScanner6<Void,Void> scan = new ElementScanner6<Void,Void>() { @Override public Void visitExecutable(ExecutableElement e, Void p) { // Executable means method or constructor, this method is called for // every method or constructor. So we just have to look at the throws // declaration... for (TypeMirror t :e.getThrownTypes()) // ...and see if any of the thrown types are subtypes of // RuntimeException or Error. if (types.isSubtype(t, runtimeException) || types.isSubtype(t, error)) { uncheckedExceptions.add(t); // print a warning so you can use your favorite editor to jump // to the problematic method log.printMessage(WARNING, "Throwing unchecked exception " + t, e); } return null; } }; // examine all the classes provided on the command line for (TypeElement e : roundEnvironment.getSpecifiedTypeElements()) scan.scan(e); // print a summary of found unchecked exceptions if (roundEnvironment.processingOver() && processingEnv.getOptions().containsKey("summary")) { System.out.println("Summary of unchecked exceptions found in throws clauses:"); for (TypeMirror t : uncheckedExceptions) System.out.println("\t" + t); } return true; } }

UPDATE: I have updated the code with comments and colors (using htmlize.el by Hrvoje Niksic)

Then I compiled the annotation processor:

javac -classpath /usr/java/jdk1.6.0/lib/tools.jar FindRuntimeException.java

You need a fairly recent Mustang snapshot to compile this. Also, from b74 (Thanks Kelly!), you won't have to put tools.jar on the classpath anymore. Then run the annotation processor:

javac -Asummary -proc:only -processor FindRuntimeException src/share/classes/javax/tools/*.java

Enjoy...

Somebody noticed JSR 199

Feb 27 2006, 07:33:35 PM PST »Java»Compiler
I hadn't expected regular developers to use JSR 199, the new compiler API in Mustang. However, Eric Bruno caught me by surprise and mentions JSR 199 on DevX.
Java is a trademark of Sun Microsystems, Inc.
Copyright © 2006,2007 Peter von der Ahé