@title{GNU $classpath Hacker's Guide} @author{Aaron M. Renn} @author{John Keiser} Copyright (C) 1998 Free Software Foundation, Inc.
@sp2 Permission is granted to make and distribute verbatim copies of this document provided the copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.
Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by the Free Software Foundation.
The $classpath Project is a dedicated to providing a 100% free, clean room implementation of the standard Java class libraries. Because there is currently no free implementation of the Java environment, no free operating system can ship with Java included. Parts of a free Java implementation have already been written, including free Java virtual machines (JVM's) such as Kaffe and Japhar, and Java compilers such as Guavac. However, there is currently no free replacement for Sun's proprietary libraries. This $classpath project aims to correct this problem by supplying a free class library implementation that will allow a 100% free Java platform to be distributed. Note that Kaffe now ships with a partial class library that is also free, so there is more than one group working towards a common goal.
Although $classpath is following an open development model where input from developers is welcome, there are certain base requirements that need to be met by anyone who wants to contribute code to this project. They are mostly unfortunately dictated by legal requirements and are not arbitrary restrictions chosen by the $classpath team.
You will need to adhere to the following things if you want to donate code to the $classpath project:
The $classpath project needs volunteers to help us out. People are needed to write unimplemented Java packages, to test $classpath on various platforms, and to port it to platforms that are currently unsupported.
While pretty much all contributions are welcome (but see see section Requirements) it is always preferable that volunteers do the whole job when volunteering for a task. So when you volunteer to write a Java package, please be willing to do the following:
Nobody likes to write documentation and test cases, but they are vital to a complete and robust product. Writing them as you go is much easier than going back at the end and adding them.
The goal of the $classpath project is to produce a free implementation of the standard class library for Java. However, there are other more specific goals as to which platforms should be supported.
$classpath is targeted to support the following operating systems:
While free operating systems are the top priority, the other priorities can shift depending on whether or not there is a volunteer to port $classpath to those platforms and to test releases.
Eventually we hope the $classpath will support all JVM's that provide JNI support. However, the top priority is free JVM's. The JVM support priority list is:
As with OS platform support, this priority list could change if a volunteer comes forward to port, maintain, and test releases for a particular JVM. Kaffe is now developing its own class library, so the priority of supporting that platform is not nearly as high as for Japhar.
The initial target version for $classpath is Java 1.1. Java 1.2 can be implemented if desired, but please do not create classes that depend on 1.2 features in other packages.
If you want to hack on $classpath, you should download, install, and familiarize yourself with the following tools:
All of these tools are available from prep.ai.mit.edu via anonymous ftp. With the exception of perl, they are fully documented with texinfo manuals. Texinfo can be browsed with the Emacs editor, or with the text editor of your choice.
Here is a brief description of the purpose of those tools.
For C code, follow the GNU Coding Standards. The standards also specify various things like the install directory structure. These should be followed if possible.
For Java code, please follow the Sun programming standards. As an exception, do not feel obligated to following their bracket and indentation style if you consider it wrong as I do.
When you write code for $classpath, write with three things in mind, and in the following order: portability, robustness, and efficiency.
If efficiency breaks portability or robustness, then don't do it the efficient way. If robustness breaks portability, then bye-bye robust code. Of course, as a programmer you would probably like to find sneaky ways to get around the issue so that your code can be all three ... the following chapters will give some hints on how to do this.
The ultimate portability goal would be to create:
With Java code, this is no problem. You end up delegating VM- or platform-specific stuff to native code anyway. Unfortunately, it is impossible to write native code that works out of the box on multiple VMs, even on a given platform. The APIs Sun has created just do not give the flexibility required to do that if you are implementing the java.* hierarchy.
The native libraries we use in $classpath are JNI, JVMDI, and VMI. JNI you should be familiar with; introduced in Java 1.1, it is the basis for our native code. JVMDI is a 1.2 concoction, and thus we can only support it on 1.1 VMs that we have source access to (i.e. Japhar and Kaffe). VMI is our own invention entirely; it is where we push the VM-specific functionality that JNI and JVMDI do not support.
However, using JVMDI and VMI in 1.1 breaks the "ideal goal" for the project. With these two in the mix, $classpath's portability becomes:
When writing, therefore, you should always keep shy of anything VM-specific yourself, and talk to the VM using the VMI. If you need a VM-specific function that is not supported by JNI or JVMDI, write a new VMI function!
Note that the preferred method is not to use the JVMDI or VMI at all, but to use only JNI. If you can write it without the VMI, your code will be quicker to port to new VMs.
There is another issue, however, and that is the efficiency issue. Some things can be done using JNI only, but they are a ton slower than they would be if you used the VMI or talked directly to the VM.
In these cases, the preferred method in $classpath is to create a library. Hide the implementation of the functions from the caller. If the library can be written using only JNI, no matter how inefficient, you should write a version of it for that. Portability, remember, is the ultimate goal, and the closer we are to it, the better off we are.
Native code is very easy to make non-robust. (That's one reason Java is so much better!) Here are a few hints to make your native code more robust.
Always check return values for standard functions. It's sometimes easy to forget to check that malloc() return for an error. Don't make that mistake. (In fact, use JCL_malloc() in the jcl library instead--it will check the return value and throw an exception if necessary.)
Always check the return values of JNI functions, or call ExceptionOccurred
to check whether an error occurred. You must do this after every JNI call.
JNI does not work well when an exception has been raised, and can have unpredictable
behavior.
Throw exceptions using JCL_ThrowException. This guarantees that if something is seriously wrong, the exception text will at least get out somewhere (even if it is stderr).
Check for null values of jclasses before you send them to JNI functions. JNI does not behave nicely when you pass a null class to it: it terminates Java with a "JNI Panic."
In general, try to use functions in native/lib/jcl.h. They check exceptions and return values and throw appropriate exceptions.
For methods which explicitly throw a NullPointerException when an argument is passed which is null, per a Sun specification, do not write code like:
int strlen(String foo) throws NullPointerException {
if (foo == null)
throw new NullPointerException("foo is null");
return foo.length();
}
Instead, the code should be written as:
int strlen(String foo) throws NullPointerException {
return foo.length();
}
Explicitly comparing foo to null is unnecessary, as the virtual machine will throw a NullPointerException when length() is invoked. $classpath is designed to be as fast as possible -- every optimization, no matter how small, is important.
You might think that using native methods all over the place would give our implementation of Java speed, speed, blinding speed. You'd be thinking wrong. Would you believe me if I told you that an interpreted Java method is typically about three and a half times faster than the equivalent native method? This is true even for a totally blank method.
Bottom line: JNI is overhead incarnate. In Sun's implementation, even the JNI functions you use once you get into Java are slow.
A final problem is efficiency of native code when it comes to things like method calls, fields, finding classes, etc. Generally you should cache things like that in static C variables if you're going to use them over and over again. GetMethodID(), GetFieldID(), and FindClass() are *slow*.
Here are a few tips on writing native code efficiently:
Make as few native method calls as possible. Note that this is not the same thing as doing less in native method calls; it just means that, if given the choice between calling two native methods and writing a single native method that does the job of both, it will usually be better to write the single native method. You can even call the other two native methods directly from your native code and not incur the overhead of a method call from Java to C.
Cache methodIDs and fieldIDs wherever you can. String lookups are expensive. The best way to do this is to use the native/lib/jnilink.h library. It will ensure that jmethodIDs are always valid, even if the class is unloaded at some point. In 1.1, jnilink simply caches a NewGlobalRef() to the method's underlying class; however, when 1.2 comes along, it will use a weak reference to allow the class to be unloaded and then re-resolve the jmethodID the next time it is used.
Cache classes that you need to access often. jnilink will help with this as well. The issue here is the same as the methodID and fieldID issue--how to make certain the class reference remains valid.
If you need to associate native C data with your class, use Paul Fisher's native_state library (NSA). It will allow you to get and set state fairly efficiently. Note that there is no built-in mechanism to associate C data with instances of a class. This is a library Paul built from scratch.
There are a number of specification sources to use when working on $classpath. In general, the only place you'll find your classes specified is in the JavaDoc documentation or possibly in the corresponding white paper. In the case of java.lang, java.io and java.util, you should look at the Java Language Specification.
Here, however, is a list of specs, in order of canonicality:
You'll notice that in this document, white papers and specification papers are more canonical than the JavaDoc documentation. This is true in general.
The $classpath directory structure is laid out in the following manner:
jcl
|
|---->java
| |
| |-->awt
| |-->io
| |-->lang
| |-->util
| | |
| | |--->zip
| | |--->jar
| |-->net
| |-->etc
|
|---->gnu
| |
| |-->java
| |
| |-->awt
| |-->lang
| |-->util
| | |
| | |-->zip
| |-->etc
|
|---->native
| |
| |-->java.io
| |-->java.lang
| |-->java.net
| |-->java.util.jar
| |-->etc
|
|---->test
| |
| |-->java.io
| |-->java.lang
| |-->etc
|
|---->compat
|
|-->java.io
|-->java.lang
|-->etc
Here is a brief description of the toplevel directories and their contents.
Each person working on a package get's his or her own "directory space" underneath each of the toplevel directories. In addition to the general guidelines above, the following standards should be followed:
This document was generated on 14 July 1998 using the texi2html translator version 1.52.