Kaffe:

    Start from main().


main(): main.c

  • Set up environment variable.
  • initialise the virtual machine (JNI_CreateJavaVM).
  • Call main2.


  • JNI_CreateJavaVM(): jni.c

  • Setup the machine (initialiseKaffe).
  • Setup JNI for main thread.
  • Setup the JNI Exception handler.
  • Return the VM and JNI we're using.


  • initialiseKaffe(): baseClasses.c

    Initialise the machine.
     
  • Initialise the, native, threading system(*Kaffe_ThreadInterface.init =  Tinit).
  • Setup CLASSPATH (initClasspath).
  • Init native support (initNative).
  • Create the initialise and finalize names and signatures, here need a unicode string to  Utf8 constant string (makeUtf8ConstFixed).
  • Read in base classes (initBaseClasses).
  • Setup exceptions (initExceptions).
  • Init thread support (initThreads).


  • Tinit: systems/unix-jthreads/internal.c

  • Initialise a thread system (jthread_init).
  • Add a presistent reference to the main thread (gc_add_ref = gcAddRef).


  • jthread_init:  systems/unix-jthreads/jthread.c

    Initialize the threading system.
     
  • Create a threaded file descriptor (jthreadedFileDescriptor)
  • Setup signal handlers (catchSignal)
  • Allocate thread queue header and tail object (allocator = thread_malloc)
  • Allocate a new thread context with stack size 0 (newThreadCtx) and initialise it.
  • Resume the thread (resumeThread)


  • jthreadedFileDescriptor: systems/unix-jthreads/jthread.c

    Create a threaded file descriptor

    catchSignal: exception.c

     Setup a signal handler

    thread_malloc: systems/unix-jthreads/internal.c

  • Allocate a new object (gc_malloc = gcMalloc)


  • gcMalloc: mem/gc-incremental.c

    Allocate a new object.  The object is attached to the white queue. After allocation, if incremental collection is active we peform a little garbage collection.  If we finish it, we wakeup the garbage collector.
     
  • Initialise GC (initGc), if not done yet.
  • Allocate a piece of memory (gc_heap_malloc)
  • Update the GC list (OBJECTSTATSADD = objectStatsChange), i.e. an object just be creadted.
  • Determine whether we need to finalise or not
  • If object is fixed, we give it the fixed colour and do not attach it to any lists.  This object is not part of the GC regieme and must be freed explicitly
  • Note that as soon as we put the object on the white list, the gc might come along and free the object if it can't find any references to it.  This is why we need to keep a reference in `mem'.  Note that keeping a reference in `unit' will not do because markObject performs a UTOUNIT()!


  • initGc: mem/gc-incremental.c

  • Initalise the Garbage Collection system


  • gc_heap_malloc: mem/gc-mem.c

  • Initialise GC heap first time in (gc_heap_initialise)- we must assume single threaded operation here so we can do the lock initialising (initStaticLock = __initLock)
  • Lock the lock (lockStaticMutex = __lockMutex)
  • Allocate memory depend on object size (gc_small_block) and (gc_large_block) which might use different GC algorithms
  • If failed to find space in any freelists. Must try to get the memory from somewhere, including
    1. The first try: Try invoking GC (invokeGC), but only if we've got some heap and the GC is available and it's worth doing. Notice that it needs unlock the lock first (unlockStaticMutex = __unlockMutex) and relock (lockStaticMutex = __lockMutex) after GC.
    2. The second try: Get from the system (gc_system_alloc), place block into the freelist for subsequent use, attach block to object hash, then free block into the system (gc_primitive_free)
    3. The third try: Throw a OutOfMemoryException, i.e. if we fail to allocate memory for it, all is lost. Then unlock the lock (unlockStaticMutex = __unlockMutex)
  • If success, unlock the lock (unlockStaticMutex = __unlockMutex) before return


  • gc_heap_initialise: mem/gc-mem.c

    Initialise allocator

    __initLock: locks.c

    Initialise a new lock
  • Initialise a new lock (*Kaffe_LockInterface.ini = Linit)


  • Linit: systems/unix-jthreads/internal.c

    Implementation of the locking subsystem based on jlocks.. Also keep track of lk->holder, and its type is void
     
  • The first lock init is for the memory manager - so we can't use it yet.  Allocate from static space. For locks later created need to dynamically allocate memory (thread_malloc)
  • Initialise the locking subsystem (jmutex_initialise), (jcondvar_initialise)


  • jmutex_initialise: systems/unix-jthreads/jthread.c

    Initialise the mutex (jmutex) by setting its member data, holder and waiting to NULL

    jcondvar_initialise: systems/unix-jthreads/jthread.c

    Initialise the condition variable (jcondvar)

    __lockMutex: locks.c

    Lock the given lock
  • Check if lock is hold by currentNative; if yes, increment lock count; if not,  lock the lock (*Kaffe_LockInterface.lock = Llock)


  • Llock: systems/unix-jthreads/internal.c

    Lock a given lock
     
  • lock the lock mutex (jmutex_lock)
  • Set lock is hold by current thread (jthread_current)


  • jmutex_lock: systems/unix-jthreads/jthread.c

    Lock the mutex of a given lock
     
  • Need to disable interrupts (intsDisable) first, then restore interrupts (intsRestore) after finished
  • Suspend the current thread to the queue (suspendOnQThread), while someone is holding the mutex.
  • Set mutex is hold by current thread (jthread_current) until the current thread is permitted to hold the mutex.


  • intsDisable: systems/unix-jthreads/jthread.c

    disable interrupts
     
  •  Instead of blocking signals, we increment a counter.
  • If a signal comes in while the counter is non-zero, we set a pending flag and mark the signal as pending.
  • intsDisable may be invoked recursively. (is that really a good idea? - gb)


  • intsRestore: systems/unix-jthreads/jthread.c

     restore interrupts
     
  • If interrupts are about to be reenabled, execute the handlers for all signals that are pending (processSignals)
  • Reschedule if necessary (reschedule)


  • jthread_current: systems/unix-jthreads/jthread.h

     return the current thread

    gc_small_block: mem/gc-mem.c

    Allocate a new block of GC'ed memory.  The block will contain one or many same size objects.
     
  • Allocate a block of memory with a page size (gc_primitive_alloc)
  • Calculate number of objects in this block
  • Setup the meta-data for the block
  • Build the objects into a free list


  • gc_primitive_alloc: mem/gc-mem.c

    Allocate a block of memory from the free list or, failing that, the system pool.

    gc_system_alloc: mem/gc-mem.c

    Allocate a block of memory from the system heap
     
  • If the needed size pass the heap boundary, return 0 to indicate that run out of heap; if not allocate a block of page (pagealloc)


  • pagealloc: mem/gc-mem.c

    Actual implementation of allocate a block of memory from the system heap

    gc_primitive_free: mem/gc-mem.c

     Return a block of memory to the free list

    __unlockMutex: locks.c

     Release a given mutex
     
  • decrement the lock count; if 0, unlock the lock (*Kaffe_LockInterface.unlock = Lunlock)


  • Lunlock: systems/unix-jthreads/internal.c

    Unlock a given lock
     
  • Set lock holder to 0, then unlock the lock mutex (jmutex_unlock)


  • jmutex_unlock: systems/unix-jthreads/jthread.c

    Unlock a given mutex
     
  • Need to disable interrupts (intsDisable) first, then restore interrupts (intsRestore) after finished
  • Set mutex is hold by NULL. If some thread is waiting for the mutex, let the next thread in the queue hold the mutex and resume the thread (resumeThread).


  • objectStatsChange: mem/gc-incremental.c

    Update the gcList for statistics gathering for GC.

    newThreadCtx: systems/unix-jthreads/jthread.c

    Allocate a new thread context and stack.
     
  • Allocate a piece of memory with size being size of jthread + stackSize (allocator) and initialise it.


  • resumeThread: systems/unix-jthreads/jthread.c

    Resume a thread running. This routine has to be called only from locations which ensure run / block queue consistency. There is no check for illegal resume conditions (like explicitly resuming an IO blocked thread)
     
  • Need to disable interrupts (intsDisable) first, then restore interrupts (intsRestore) after finished
  • Remove from alarmQ or lockQ, if necessary.
  • Place thread on the end of its queue


  • gcAddRef: mem/gc-incremental.c

     Add a presistent reference to an object for GC
     
  • If found the reference, just increase reference; if not found - create a new one (gcMalloc)


  • initClasspath: findInJar.c

    Initialise class path
     
  • If class path is not empty, build classpathEntries from it (makeClasspath); if empty, discover all available jar and zip files in the home location and build a classpath from them. (discoverClasspath).


  • makeClasspath: findInJar.c

    Build classpathEntries from the given classpath.
     
  • Add the given entry into the Classpath (addClasspath).
  • Allocat a piece of memory (gc_malloc_fixed = gcMalloc) and put all Classpath entries in it.


  • addClasspath: findInJar.c

    Add an entry in the Classpath dynamically.
     
  • Allocate memory if the entry need to be build (gc_malloc_fixed = gcMalloc); then initialise it.


  • initNative: external.c

    Initialise native support, i.e. method calls to other languages
     
  • Allocat a piece of memory (gc_malloc_fixed = gcMalloc) and put librariy path entries in it.
  • Find the default library and load it (loadNativeLibrary).


  • loadNativeLibrary: external.c

  • Find a library handle.  If we find the library has already been loaded, don't bother to get it again, just increase the reference count. If not, load the library and allocat a piece of memory (gc_malloc_fixed = gcMalloc) for the library name.


  • makeUtf8ConstFixed: string.c

     Create a Utf8 string object. The diffenence with makeUtf8Const is that it ensure system doesn't GC the Utf8 string.
     
  •  Create a Utf8 string (makeUtf8Const).
  • Add a presistent reference to the created Utf8 string (gc_add_ref = gcAddRef), so system doesn't GC classes.


  • makeUtf8Const: string.c

     Create a Utf8 string object. The diffenence with makeUtf8ConstFixed is that it does not ensure system doesn't GC the Utf8 string.
     
  • Allocat necessary memory (gc_malloc = gcMalloc) for the Utf8 string and put the unicode content in it.
  • Calculate its hash value (hashUtf8String).


  • hashUtf8String: string.c

    Calculate a hash value for a string encoded in Utf8 format. This returns the same hash value as specified or java.lang.String.hashCode.
     
  • Count the number of Unicode chars (strLengthUtf8).
  • Calculate its hash value.


  • strLengthUtf8: string.c

    Count the number of Unicode chars encoded in a given Ut8 string.

    initBaseClasses: baseClasses.c

    Need to use certain classes in the internal machine so we better get them in now in a known way so we can refer back to them. Currently we need java/lang/Object, java/lang/Class, java/lang/String and java/lang/System.
     
  • Initialise the primitive types (initTypes).
  • Load the base types, basic types classes and Exception handling types (loadStaticClass).
  • Fixup primitive types (finishTypes).


  • initTypes: itypes.c

    Intialise the internal types.
     
  • Initialise primitive classes (initPrimClass), like void, byte, int, etc.


  • initPrimClass: itypes.c

  • Create a new class object (newClass).
  • Initialise the class as primity type; also need to convert class name to Utf8 string (makeUtf8Const).


  • newClass: object.c

    Allocate a new class object.
     
  • Allocate memory (gc_malloc = gcMalloc).
  • We don't GC classes at the moment so secure this one (gc_add_ref = gcAddRef);


  • loadStaticClass: classMethod.c

  • Create a new object (newClass).
  • Find class entry by its name (lookupClassEntry).
  • Lock the entry (lockMutex = _lockMutex) to see if the class has been loaded, i.e. by the class entry. If yes, need to find it in a directory or JAR file (findClass). Then unlock the entry (unlockMutex = _unlockMutex).
  • Initialise the class (processClass) with state Cstate_Linked.


  • lookupClassEntry: classMethod.c

    Find its class entry by given class name. If not exist, allocate memory and add an entry.
     
  • If the class table, classHashLock, has not been initialized, i.e. chlinit == false, initialize it (initStaticLock = __initLock).
  • If failed to find class entry - create a new one, i.e. need to allocate memory (gc_malloc_fixed = gcMalloc). Then, lock the class table (lockStaticMutex = __lockMutex) and insert entry into it, but, if someone else added it - discard ours (gc_free_fixed) and return the new one. If no one added it, add ours to end of hash and also keep an extra reference to the utf8 name so it won't be GCed (gc_add_ref = gcAddRef). After unlock the class table (unlockStaticMutex = __unlockMutex).


  • _lockMutex: locks.c

    Lock a mutex by using the address to find a lock.
     
  • Retrieve the lock associated with the given address.  If one isn't found, allocate it (newLock).
  • lock the found lock (__lockMutex).


  • newLock: locks.c

    Retrieve a machine specific, possibly, locking structure associated with the given address.  If one isn't found, allocate it.
     
  • Get the hash list header from the lock table, lock the hash list (*Kaffe_LockInterface.spinon = Tspinon).
  • Try to find the lock with the given address; if found, simply increment reference count; if not but found a free lock, i.e. no reference, get the free lock.
  • Allocate a new lock structure, if necessary - use a free one if we found it, or need to allocate memory (gc_malloc = gcMalloc) and initialise it (*Kaffe_LockInterface.init = Linit).
  • Unlock the hash list (*Kaffe_LockInterface.spinoff = Tspinoff).


  • Tspinon: systems/unix-jthreads/internal.c

    Using internal system to support lock; in Linux, it simply disable interrupt (intsDisable).

    Tspinoff: systems/unix-jthreads/internal.c

    Using internal system to support unlock; in Linux, it simply enable interrupt (intsRestore).

    findClass: findInJar.c

    Find the named class in a directory or JAR file
     
  • Locate the class by name in the CLASSPATH (findInJar).
  • Depend on class file type, try to new (newClass) and read class (readClass) if zip file, or register class (registerClass) if shared library.
  • Certain classes, like  java/lang/ClassNotFoundException or java/lang/Object, are essential.  If we don't find them then abort.


  • findInJar: findInJar.c

    Locate the given class name in the CLASSPATH.
     
  • Initialise a lock (initStaticLock = __initLock) on first use. Here we allow one into the jar at once (lockStaticMutex = __lockMutex).
  • If class file type is zip, need to open the jar file (openJarFile), find if the given calss name is in the jar file (lookupJarFile) (getDataJarFile).
  • If class file type isDIR,
  • If class file type is shared lib, need to (loadNativeLibrary), (generateMangledName), (loadNativeLibrarySym).


  • lookupJarFile: jar.c

    Simply compare the given class name with each jar entry in the jar file.

    openJarFile: jar.c (unfinished)

    Open the jar file.

    processClass: classMethod.c

    Process all the stage of a classes initialisation.  We can provide a state to aim for (so we don't have to do this all at once).  This is called by various parts of the machine in order to load, link and initialise the class.  Putting it all together here makes it a damn sight easier to understand what's happening.
     
  • Initialise a lock (initStaticLock = __initLock) on first use. For the moment we only allow one thread to initialise any classes at once.  This is because we need to check circular class dependencies (lockStaticMutex = __lockMutex).
  • Allocate any static space required by class and initialise the space with any constant values.  This isn't necessaryfor pre-loaded classes (allocStaticFields).
  • Load (getClass) and link the super class (processClass).
  • Load all the implemented interfaces. If class is an interface, include the superclass as well.
  • We build a list of *all* interfaces this class can use.
  • (resolveObjectFields).
  • (resolveStaticFields).
  • Build dispatch table (buildDispatchTable).  We must handle interfaces a little differently (buildInterfaceDispatchTable) since they only have a <clinit> method.
  • Second stage verification - check the class format is okay (verify2).
  • Third stage verification - check the bytecode is okay (verify3).
  • If init is in progress return.  This must be the same thread because we lock the class when we come in here.
  • Initialise the constants (resolveConstants).
  • Find the class initialization method (findMethodLocal), i.e. <clinit>.
  • Call the class initialization method (callMethodA), i.e. <clinit>.


  • allocStaticFields: classMethod.c

    Allocate the space for the static class data.

    _unlockMutex: locks.c

    Release a mutex by using the address to find a lock.
     
  • Retrieve the lock associated with the given address (getLock).
  • Unlock the found lock (__unlockMutex).
  • Free the lock if necessary (freeLock).


  • getLock: locks.c

    Retrieve a machine specific, possibly, locking structure associated with the given address.  If one isn't found, don't allocate it, which is different with (newLock).
     
  • Simply search the lockTable.


  • freeLock: locks.c

    Decrement the reference count or free a lock if no longer in use.
     
  • Get the hash list header from the lock table, lock the hash list (*Kaffe_LockInterface.spinon = Tspinon).
  • Decrement reference count; if reference is 0, release it for reallocation, i.e. no count, no holder.
  • Unlock the hash list (*Kaffe_LockInterface.spinoff = Tspinoff).


  • finishTypes: itypes.c

    Finish the internal types.

    initExceptions: exception.c

    Setup the internal exceptions.
     
  • Catch signals we need to convert to exceptions (Catchsignal = catchSignal).


  • initThreads: thread.c

    Initialise threads, not whole thread system, which is different than (Tinit.).
     
  • Get a handle on the thread and thread group classes (lookupClass).
  • Create base group, i.e. ThreadGroupClass (newObject), then initialise the group.
  • Allocate a thread to be the main thread (createInitialThread).
  • initialize thread start lock (initStaticLock = __initLock).
  • Start the GC daemons (createDaemon) we need, i.e. finaliser and gc with priority THREAD_MAXPRIO.


  • lookupClass: classMethod.c

    Get the handle on class by the given name.
     
  • Lookup a named class, loading it if necessary (loadClass).
  • Process the class (processClass) with state CSTATE_OK.


  • loadClass: classMethod.c

     Lookup a named class, loading it if necessary. The name is as used in class files, e.g. "java/lang/String".
     
  • look up the class entry (lookupClassEntry).
  • If failed to find class, we must now load it. If we use a class loader, we must call out to Java code.
  • If not use loader, i.e. loader == NULL, use findClass which will set centry->class if it finds it.
  • We process the class (processClass) with state Cstate_Linked, while we're holding the centry lock so that other threads will find a processed class if centry->class is not null.


  • newObject: object.c

    Create a new Java object. If the object is a Class then we make it a root, just for the moment. Otherwise, if the object has a finalize method we look it up and store it with the object for it's later use.  Otherwise the object is normal.
     
  • Allocate memory (gc_malloc = gcMalloc) and fill in object information.


  • createInitialThread: thread.c

    Create the initial thread with a given name, "main"? We should only ever call this once.
     
  • Allocate a thread to be the main thread (newObject) and initialise it.
  • (*Kaffe_ThreadInterface.createFirst = TcreateFirst).
  • Attach thread to threadGroup, i.e. by call the method "add" of ThreadGroupClass  (do_execute_java_method).


  • TcreateFirst: systems/unix-jthreads/internal.c

  • Set the function, runfinalizer, to be run (jthread_atexit) when all non-daemon threads have exited.
  • Set Java thread, which is obtained by GET_COOKIE = jthread_current, associated with main thread (GC_WRITE).


  • jthread_atexit: systems/unix-jthreads/jthread.c

    Set a given function to be run when all non-daemon threads have exited.

    do_execute_java_method: support.c

    Execute a method of a class.
     
  • Call a Java method on a class from native code (do_execute_java_method_v), static or non-static.


  • createDaemon: thread.c

    Start a daemon thread with a given function.
     
  • Keep daemon threads as root objects (newObject) and initialise it.
  • Create a thread with the given function by using internal system support (*Kaffe_ThreadInterface.create = Tcreate).


  • do_execute_java_method_v: support.c

    Call a Java method from native code.
     
  • If method handler is 0, look up the handler first. For static call, use (lookupClassMethod); for non-static call, use (lookupObjectMethod).
  • Call the method (callMethodV).


  • lookupObjectMethod: support.c

    Lookup a method given object, name and signature.
     
  • Get the object's class handler first, then call lookupClassMethod.


  • lookupClassMethod:  support.c

    Lookup a method given class, name and signature.
     
  • Find the method (findMethod) with name and signature converted to Utf8 string (makeUtf8Const).


  • findMethod: lookup.c

    Lookup a method (and translate) in the specified class, it will look up through super class.
     
  • Waz CSTATE_LINKED ?- Must resolve constants before we do any translation.  Might not be right though ... XXX --> call processClass with state CSTATE_OK.
  • Lookup method (findMethodLocal) up through each superclass until found.


  • findMethodLocal: lookup.c

    Lookup a method (and translate) in the specified class, no look up through superclass.
     
  • Compare the method name and signature, and ensure the method is not abstract.


  • callMethodA: support.c

    Generic routine to call a native or Java method (array style).
     
  • Fill in the callMethodInfo structure describes the information necessary to invoke a native or just-in-time compiled method.
  • For interpreter mode:
    1. If not native method, call virtualMachine to execute the bytecode.
    2. If native method, make the call for native method - system dependent (sysdepCallMethod).
    Fot JIT mode, make the call for native method - system dependent (sysdepCallMethod), which again will call Translate for the first time executing the bytecode.

    callMethodV: support.c

    Generic routine to call a native or Java method with varargs style.
     
  • Fill in the callMethodInfo structure describes the information necessary to invoke a native or just-in-time compiled method.
  • For interpreter mode:
    1. If not native method, call virtualMachine to execute the bytecode.
    2. If native method, make the call for native method - system dependent (sysdepCallMethod).
  • Fot JIT mode, make the call for native method - system dependent (sysdepCallMethod), which again will call Translate for the first time executing the bytecode.


  • Tcreate: systems/unix-jthreads/internal.c

  • Create a native thread (jthread_create).
  • Set Java thread associated with native thread (GC_WRITE).


  • jthread_create: systems/unix-jthreads/jthread.c

    Create a new jthread
     
  • Lock the threadLock (jmutex_lock).
  • Allocate a new thread context with given stack size, threadStackSize, (newThreadCtx).
  • Note: thread 1 is the main thread, thread 2 and 3 are the garbage collector/finalizer. Let's assume for now the finalizer does not have to be preempted. Thus, for thread > 3, we activate time slicing (activate_time_slicing).
  • Unlock the threadLock (jmutex_unlock).
  • Note that when we return from setjmp in the context of a new thread, we must no longer access any local variables in this function.  The reason is that we didn't munge the base pointer that is used to access these variables. To be safe, we immediately call a new function (start_this_sucker_on_a_new_frame).
  • Set up context for new thread. Use the macros dealing with stack pointer, like GET_SP, SET_SP, GET_FP, and SET_FP.
  • Resume the thread (resumeThread).


  • activate_time_slicing: systems/unix-jthreads/jthread.c

    Set virtual timer for 10ms round-robin time-slicing.