Filewatcher File Search
FTP Search
  
Directory (beta)
  
Content Search (beta)
   
pkg://kaffe-rhcn-1.0.b4-1.src.rpm:2270813/kaffe-1.0.b4.tar.gz  info  downloads

kaffe-1.0b4/   777    624    310           0  6703463065   5740 5kaffe-1.0b4/Makefile.in   644    624    310       36215  6703462461  10027 # Makefile.in generated automatically by automake 1.4a from Makefile.am

# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.

SHELL = @SHELL@

srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@

bindir = @bindir@
sbindir = @sbindir@
libexecdir = @libexecdir@
datadir = @datadir@
sysconfdir = @sysconfdir@
sharedstatedir = @sharedstatedir@
localstatedir = @localstatedir@
libdir = @libdir@
infodir = @infodir@
mandir = @mandir@
includedir = @includedir@
oldincludedir = /usr/include

DESTDIR =

pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@

top_builddir = .

ACLOCAL = @ACLOCAL@
AUTOCONF = @AUTOCONF@
AUTOMAKE = @AUTOMAKE@
AUTOHEADER = @AUTOHEADER@

INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_FLAG =
transform = @program_transform_name@

NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
host_alias = @host_alias@
host_triplet = @host@
AMTARFLAGS = @AMTARFLAGS@
AS = @AS@
AWT_DIR = @AWT_DIR@
AWT_LIBS = @AWT_LIBS@
CC = @CC@
CONFIG_JIT_MD_H = @CONFIG_JIT_MD_H@
CONFIG_MD_H = @CONFIG_MD_H@
CPP = @CPP@
DIRSEP = @DIRSEP@
DLLTOOL = @DLLTOOL@
DLOPEN_JAVA_LIBS = @DLOPEN_JAVA_LIBS@
ENGINE_NAME = @ENGINE_NAME@
EXEEXT = @EXEEXT@
HAVE_CONFIG_JIT_MD_H = @HAVE_CONFIG_JIT_MD_H@
JAVA_LIBS = @JAVA_LIBS@
JIKES = @JIKES@
KAFFEH = @KAFFEH@
KAFFEVM_ICODE_H = @KAFFEVM_ICODE_H@
KAFFEVM_JIT_DEF = @KAFFEVM_JIT_DEF@
KAFFEVM_MD_C = @KAFFEVM_MD_C@
KAFFEVM_TRAMPOLINES_C = @KAFFEVM_TRAMPOLINES_C@
KAFFE_ARCHOS = @KAFFE_ARCHOS@
KAFFE_LIBS = @KAFFE_LIBS@
KLIBFLAGS = @KLIBFLAGS@
KPREFIX = @KPREFIX@
KVER = @KVER@
KVMLIBFLAGS = @KVMLIBFLAGS@
Kaffe_TRANSF = @Kaffe_TRANSF@
LD = @LD@
LIBLTDL = @LIBLTDL@
LIBTOOL = @LIBTOOL@
LIBTOOL_DEPS = @LIBTOOL_DEPS@
LN_S = @LN_S@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MAKE_KAFFEH = @MAKE_KAFFEH@
MATH_LIBS = @MATH_LIBS@
M_LIBS = @M_LIBS@
NET_LIBS = @NET_LIBS@
NM = @NM@
PACKAGE = @PACKAGE@
PATHSEP = @PATHSEP@
RANLIB = @RANLIB@
REGEN_FORWARD = @REGEN_FORWARD@
TAR = @TAR@
THREAD_DIR = @THREAD_DIR@
THREAD_SYSTEM = @THREAD_SYSTEM@
VERSION = @VERSION@
VM_LIBS = @VM_LIBS@
ZIP = @ZIP@
ZIP_LIBS = @ZIP_LIBS@
classdir = @classdir@
host_cpu = @host_cpu@
host_os = @host_os@
kaffe_TRANSF = @kaffe_TRANSF@
nativedir = @nativedir@
with_engine = @with_engine@

# Top-level Makefile for Kaffe OpenVM.
#
# Copyright (c) 1996, 1997, 1998, 1999
#	Transvirtual Technologies, Inc.  All rights reserved.
#
# See the file "license.terms" for information on usage and redistribution 
# of this file. 


AUTOMAKE_OPTIONS = foreign 1.3e

SUBDIRS = . config include libltdl libraries kaffe test

EXTRA_DIST = \
	ChangeLog.1 \
	WHATSNEW \
	license.terms \
	FAQ/FAQ.BeOS \
	FAQ/FAQ.Known-Bugs \
	FAQ/FAQ.amigaos \
	FAQ/FAQ.automake \
	FAQ/FAQ.awt \
	FAQ/FAQ.class-states \
	FAQ/FAQ.classlibrary-compile \
	FAQ/FAQ.depend \
	FAQ/FAQ.gcblock \
	FAQ/FAQ.gcstrategy \
	FAQ/FAQ.hotjava \
	FAQ/FAQ.install-root \
	FAQ/FAQ.jsignal \
	FAQ/FAQ.libtool \
	FAQ/FAQ.linux \
	FAQ/FAQ.nativemethods \
	FAQ/FAQ.requiredlibraries \
	FAQ/FAQ.timing \
	FAQ/FAQ.unicode \
	FAQ/FAQ.win32 \
	developers/JavaClass.pm \
	developers/README \
	developers/README.unicode \
	developers/autogen.sh \
	developers/dumpClass.pl \
	developers/gdbinit \
	developers/sp_offset.c \
	developers/update-class-list \
	developers/unicode.pl \
	developers/utf8munge.pl


CLEANFILES = BUILD_ENVIRONMENT

noinst_SCRIPTS = libtool BUILD_ENVIRONMENT

CLASSDIRS = libraries/javalib
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
CONFIG_HEADER = ./config/config.h ./include/jtypes.h
CONFIG_CLEAN_FILES = 
SCRIPTS =  $(noinst_SCRIPTS)

DIST_COMMON =  README AUTHORS ChangeLog INSTALL Makefile.am Makefile.in \
acinclude.m4 aclocal.m4 config.guess config.sub config/config.h.in \
config/stamp-h1.in configure configure.in include/jtypes.h.in \
include/stamp-h2.in install-sh ltconfig ltmain.sh missing mkinstalldirs


DISTFILES = $(DIST_COMMON) $(SOURCES) $(TEXINFOS) $(EXTRA_DIST)

GZIP_ENV = --best
all: all-redirect
.SUFFIXES:
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
	cd $(top_srcdir) && $(AUTOMAKE) --foreign --include-deps Makefile

Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status
	cd $(top_builddir) \
	  && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status

$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ configure.in  acinclude.m4
	cd $(srcdir) && $(ACLOCAL)

config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
	$(SHELL) ./config.status --recheck
$(srcdir)/configure: @MAINTAINER_MODE_TRUE@$(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES)
	cd $(srcdir) && $(AUTOCONF)

config/config.h: config/stamp-h1
	@if test ! -f $@; then \
		rm -f config/stamp-h1; \
		$(MAKE) config/stamp-h1; \
	else :; fi
config/stamp-h1: $(srcdir)/config/config.h.in $(top_builddir)/config.status
	cd $(top_builddir) \
	  && CONFIG_FILES= CONFIG_HEADERS=config/config.h \
	     $(SHELL) ./config.status
	@echo timestamp > config/stamp-h1 2> /dev/null
$(srcdir)/config/config.h.in: @MAINTAINER_MODE_TRUE@$(srcdir)/config/stamp-h1.in
	@if test ! -f $@; then \
		rm -f $(srcdir)/config/stamp-h1.in; \
		$(MAKE) $(srcdir)/config/stamp-h1.in; \
	else :; fi
$(srcdir)/config/stamp-h1.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) 
	cd $(top_srcdir) && $(AUTOHEADER)
	@echo timestamp > $(srcdir)/config/stamp-h1.in 2> /dev/null

include/jtypes.h: include/stamp-h2
	@if test ! -f $@; then \
		rm -f include/stamp-h2; \
		$(MAKE) include/stamp-h2; \
	else :; fi
include/stamp-h2: $(srcdir)/include/jtypes.h.in $(top_builddir)/config.status
	cd $(top_builddir) \
	  && CONFIG_FILES= CONFIG_HEADERS=include/jtypes.h \
	     $(SHELL) ./config.status
	@echo timestamp > include/stamp-h2 2> /dev/null
$(srcdir)/include/jtypes.h.in: @MAINTAINER_MODE_TRUE@$(srcdir)/include/stamp-h2.in
	@if test ! -f $@; then \
		rm -f $(srcdir)/include/stamp-h2.in; \
		$(MAKE) $(srcdir)/include/stamp-h2.in; \
	else :; fi
$(srcdir)/include/stamp-h2.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) 
	cd $(top_srcdir) && $(AUTOHEADER)
	@echo timestamp > $(srcdir)/include/stamp-h2.in 2> /dev/null

mostlyclean-hdr:

clean-hdr:

distclean-hdr:
	-rm -f config/config.h include/jtypes.h

maintainer-clean-hdr:

# This directory's subdirectories are mostly independent; you can cd
# into them and run `make' without going through this Makefile.
# To change the values of `make' variables: instead of editing Makefiles,
# (1) if the variable is set in `config.status', edit `config.status'
#     (which will cause the Makefiles to be regenerated when you run `make');
# (2) otherwise, pass the desired values on the `make' command line.

@SET_MAKE@

all-recursive install-data-recursive install-exec-recursive \
installdirs-recursive install-recursive uninstall-recursive  \
check-recursive installcheck-recursive info-recursive dvi-recursive:
	@set fnord $(MAKEFLAGS); amf=$$2; \
	dot_seen=no; \
	target=`echo $@ | sed s/-recursive//`; \
	list='$(SUBDIRS)'; for subdir in $$list; do \
	  echo "Making $$target in $$subdir"; \
	  if test "$$subdir" = "."; then \
	    dot_seen=yes; \
	    local_target="$$target-am"; \
	  else \
	    local_target="$$target"; \
	  fi; \
	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
	   || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
	done; \
	if test "$$dot_seen" = "no"; then \
	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
	fi; test -z "$$fail"

mostlyclean-recursive clean-recursive distclean-recursive \
maintainer-clean-recursive:
	@set fnord $(MAKEFLAGS); amf=$$2; \
	dot_seen=no; \
	rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
	  rev="$$subdir $$rev"; \
	  if test "$$subdir" = "."; then dot_seen=yes; else :; fi; \
	done; \
	test "$$dot_seen" = "no" && rev=". $$rev"; \
	target=`echo $@ | sed s/-recursive//`; \
	for subdir in $$rev; do \
	  echo "Making $$target in $$subdir"; \
	  if test "$$subdir" = "."; then \
	    local_target="$$target-am"; \
	  else \
	    local_target="$$target"; \
	  fi; \
	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
	   || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
	done && test -z "$$fail"
tags-recursive:
	list='$(SUBDIRS)'; for subdir in $$list; do \
	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
	done

tags: TAGS

ID: $(HEADERS) $(SOURCES) $(LISP)
	list='$(SOURCES) $(HEADERS)'; \
	unique=`for i in $$list; do echo $$i; done | \
	  awk '    { files[$$0] = 1; } \
	       END { for (i in files) print i; }'`; \
	here=`pwd` && cd $(srcdir) \
	  && mkid -f$$here/ID $$unique $(LISP)

TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) $(LISP)
	tags=; \
	here=`pwd`; \
	list='$(SUBDIRS)'; for subdir in $$list; do \
   if test "$$subdir" = .; then :; else \
	    test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \
   fi; \
	done; \
	list='$(SOURCES) $(HEADERS)'; \
	unique=`for i in $$list; do echo $$i; done | \
	  awk '    { files[$$0] = 1; } \
	       END { for (i in files) print i; }'`; \
	test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
	  || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags  $$unique $(LISP) -o $$here/TAGS)

mostlyclean-tags:

clean-tags:

distclean-tags:
	-rm -f TAGS ID

maintainer-clean-tags:

distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)

# This target untars the dist file and tries a VPATH configuration.  Then
# it guarantees that the distribution is self-contained by making another
# tarfile.
distcheck: dist
	-rm -rf $(distdir)
	GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(TAR) xf -
	mkdir $(distdir)/=build
	mkdir $(distdir)/=inst
	dc_install_base=`cd $(distdir)/=inst && pwd`; \
	cd $(distdir)/=build \
	  && ../configure --srcdir=.. --prefix=$$dc_install_base \
	  && $(MAKE) $(AM_MAKEFLAGS) \
	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
	  && $(MAKE) $(AM_MAKEFLAGS) check \
	  && $(MAKE) $(AM_MAKEFLAGS) install \
	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
	  && $(MAKE) $(AM_MAKEFLAGS) dist
	-rm -rf $(distdir)
	@banner="$(distdir).tar.gz is ready for distribution"; \
	dashes=`echo "$$banner" | sed s/./=/g`; \
	echo "$$dashes"; \
	echo "$$banner"; \
	echo "$$dashes"
dist: distdir
	-chmod -R a+r $(distdir)
	tar ch$(AMTARFLAGS)f - $(distdir) | GZIP=$(GZIP_ENV) gzip -c > $(distdir).tar.gz
	-rm -rf $(distdir)
dist-all: distdir
	-chmod -R a+r $(distdir)
	tar ch$(AMTARFLAGS)f - $(distdir) | GZIP=$(GZIP_ENV) gzip -c > $(distdir).tar.gz
	-rm -rf $(distdir)
distdir: $(DISTFILES)
	-rm -rf $(distdir)
	mkdir $(distdir)
	-chmod 777 $(distdir)
	$(mkinstalldirs) $(distdir)/FAQ $(distdir)/developers
	@for file in $(DISTFILES); do \
	  d=$(srcdir); \
	  if test -d $$d/$$file; then \
	    cp -pr $$d/$$file $(distdir)/$$file; \
	  else \
	    test -f $(distdir)/$$file \
	    || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
	    || cp -p $$d/$$file $(distdir)/$$file || :; \
	  fi; \
	done
	for subdir in $(SUBDIRS); do \
	  if test "$$subdir" = .; then :; else \
	    test -d $(distdir)/$$subdir \
	    || mkdir $(distdir)/$$subdir \
	    || exit 1; \
	    chmod 777 $(distdir)/$$subdir; \
	    (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \
	      || exit 1; \
	  fi; \
	done
info-am:
info: info-recursive
dvi-am:
dvi: dvi-recursive
check-am: all-am
check: check-recursive
installcheck-am:
installcheck: installcheck-recursive
install-exec-am:
install-exec: install-exec-recursive

install-data-am:
install-data: install-data-recursive

install-am: all-am
	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
install: install-recursive
uninstall-am:
uninstall: uninstall-recursive
all-am: Makefile $(SCRIPTS)
all-redirect: all-recursive
install-strip:
	$(MAKE) $(AM_MAKEFLAGS) INSTALL_STRIP_FLAG=-s install
installdirs: installdirs-recursive
installdirs-am:


mostlyclean-generic:

clean-generic:
	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)

distclean-generic:
	-rm -f Makefile $(CONFIG_CLEAN_FILES)
	-rm -f config.cache config.log stamp-h stamp-h[0-9]*

maintainer-clean-generic:
mostlyclean-am:  mostlyclean-hdr mostlyclean-tags mostlyclean-generic

mostlyclean: mostlyclean-recursive

clean-am:  clean-hdr clean-tags clean-generic mostlyclean-am

clean: clean-recursive

distclean-am:  distclean-hdr distclean-tags distclean-generic clean-am
	-rm -f libtool

distclean: distclean-recursive
	-rm -f config.status

maintainer-clean-am:  maintainer-clean-hdr maintainer-clean-tags \
		maintainer-clean-generic distclean-am
	@echo "This command is intended for maintainers to use;"
	@echo "it deletes files that may require special tools to rebuild."

maintainer-clean: maintainer-clean-recursive
	-rm -f config.status

.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
install-data-recursive uninstall-data-recursive install-exec-recursive \
uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \
all-recursive check-recursive installcheck-recursive info-recursive \
dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \
maintainer-clean-recursive tags tags-recursive mostlyclean-tags \
distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
install-exec install-data-am install-data install-am install \
uninstall-am uninstall all-redirect all-am all install-strip \
installdirs-am installdirs mostlyclean-generic distclean-generic \
clean-generic maintainer-clean-generic clean mostlyclean distclean \
maintainer-clean


depend:
	@echo \`make depend\' is no longer needed

libtool: $(LIBTOOL_DEPS)
	$(SHELL) ./config.status --recheck

BUILD_ENVIRONMENT: Makefile
	$(MAKE) top_srcdir=`cd $(top_srcdir) >/dev/null; pwd` \
		top_builddir=`cd $(top_builddir) >/dev/null; pwd` \
		"DEBUG_ENV=$(DEBUG_ENV)" BUILD_ENVIRONMENT-make

BUILD_ENVIRONMENT-make: Makefile
	echo CLASSPATH=\$${CLASSPATH-.}\''$(PATHSEP)'\'$(top_srcdir)/libraries/javalib/Klasses.jar\''$(PATHSEP)'\'$(top_srcdir)/libraries/javalib/pizza.jar\; export CLASSPATH > BUILD_ENVIRONMENT; \
	echo KAFFELIBRARYPATH=\$${KAFFELIBRARYPATH+\"\$$KAFFELIBRARYPATH\"\''$(PATHSEP)'\'}`for f in $(JAVA_LIBS); do echo "$$f" | sed 's%/[^/]*$$%%'; done | (tr '\012' '$(PATHSEP)'; echo) | sed s/.$$//`\; export KAFFELIBRARYPATH >> BUILD_ENVIRONMENT; \
	echo JAVA=$(top_builddir)/kaffe/kaffe/Kaffe$(EXEEXT)\; export JAVA >> BUILD_ENVIRONMENT

.PHONY: Klasses new-classes compile-classes jar-classes build-classes
Klasses new-classes compile-classes jar-classes build-classes:
	@if test "$(CLASSDIRS)" = all; then \
	   $(MAKE) CLASSDIRS="`echo libraries/javalib \
				    libraries/extensions/*/javalib`" $@; \
	else \
	  for f in $(CLASSDIRS); do \
	    (cd $$f && $(MAKE) $@); \
	  done; \
	fi

# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
kaffe-1.0b4/FAQ/   755    624    310           0  6703462466   6347 5kaffe-1.0b4/FAQ/FAQ.BeOS   644    624    310        5361  6677531004   7470 NOTES ABOUT THE BeOS PORT
=========================
The BeOS port requires BeOS R4 or higher.  It uses its own threading
system designated "beos-native".  The "unix-jthreads" system can be
made to work for the most part; however, that system relies on
asynchronous I/O notifications, as well as virtual timer alarms, both
of which are absent on BeOS R4.

beos-native was modeled upon the oskit-pthreads system, which relies
on mutexes and condition variables.  BeOS has the former, but not the
latter, so I defined a condition variable type based upon an article I
found posted on the Web [1].

To build Kaffe for BeOS, you must configure Kaffe as follows:

	./configure --disable-shared --prefix=<insert-your-prefix-here>

as there is a known problem with building shared libs on R4 (yes, I
realize that the fix is documented in the R4 release notes, but I'll
let someone else muck with the Makefiles accordingly).

As of 26 March 1999, Kaffe has not been built or tested with AWT at all.
Also, the following entry points in the jthread interface have yet to be
implemented:

	jthread_suspendall
	jthread_unsuspendall
	jthread_spinon
	jthread_spinoff

KNOWN PROBLEMS
==============
The jthread interface requires that the macros GET_JTHREAD/SET_JTHREAD
and GET_COOKIE/SET_COOKIE store and retrieve per-thread information.
Right now, this information is maintained in a large BeOS "area" containing
an array of per_thread_info_t structures.  The info for a given thread
with thread id 'T' is simply (T % MAX_THREADS).  Naturally, since
successive calls to spawn_thread (invoked by jthread_create) will not
necessarily yield contiguous thread ids, the maximum number of jthreads
that can be successfully created will be somewhat less than MAX_THREADS.

The implementation of forkexec() in beos-native/syscalls.c makes use of
fork() instead of the recommended technique that uses load_image().

The gethostbyname() and gethostbyaddr() wrappers in beos-native/syscalls.c
are not thread-safe.

The test 'GCTest' gets stuck when executed from within the TestScript
on a machine with 32MB of memory; it passes, though, when run from the
command line.

The BeanBug test in the regression test suite fails because it requires AWT.

REFERENCES
==========
[1] "Locks and Condition Variables",
    http://www.cs.umd.edu/~hollings/cs412/s96/synch/locks.html

REV HISTORY
===========
27 Jan 99 -- Submitted for inclusion into Kaffe, alanlb@cs.vt.edu
08 Feb 99 -- Fixed various segmentation violations
18 Mar 99 -- Implemented jcondvar_wait timeout, forkexec;
		fixed jthread_interrupt
22 Mar 99 -- Fixed socket read/write in beos-native/syscalls.c, redid
		thread cancellation (i.e., stoppage)
24 Mar 99 -- Fixed forkexec; ProcessTest now passes
26 Mar 99 -- Implemented most socket timeouts; SoTimeout now passes
kaffe-1.0b4/FAQ/FAQ.Known-Bugs   644    624    310       15344  6677531004  10714 **********************************************************************

NOTE:

Kaffe has its own jitterbug bug tracking system now.
See www.kaffe.org and http://www.kaffe.org/cgi-bin/kaffe

We will note new bugs only there.

**********************************************************************

This file is a list of known bugs, short-comings and oddities.  
Treat it as an TODO list.   This file is divided in three sections.

MISSING AND/OR BROKEN FEATURES
---------------------------------------------------------------------------

* reflection: when reporting fields of a class, kaffe wrongly does not include
  public static final fields of the interfaces that this class implements.

* File.getCanonicalPath() is not properly implemented.
  This needs a native implementation.

* java.net.Socket:

  Gerhard Paulus <gpaulus@stud.uni-frankfurt.de> reports:
  +  a host name "" is not taken as local host (as in JDK)
  +  if a socket is closed and a thread is reading from the socket's input
  stream then this thread should be able to catch the SocketException
  "Socket closed" (as in JDK)  This might be fixed now.

* Kaffe does not currently provide its own implementation of RMI.

* Kaffe's java.lang.Character class does not conform to Sun's spec.  The 
  handling of unicode characters is incomplete.

* Some run-time classes may not serialize or deserialize according to their
  serial forms.

* java.math.* is incomplete.  java.math.BigInteger is mostly written and
  relies on the GNU Multi-precision math package (GMP).  BigDecimal is
  completely incomplete (?) and will just throw exceptions when used.

* The conversion of floats or doubles to strings does not completely
  conform to Sun's spec. For example, Double.toString() will display
  the double 0x400B333333333333 as "3.3999999999999999" instead of "3.4".

* DecimalFormat is broken :  no formatting at all and decimals are cut off.
  There are problems with date & calendar.

  Reported by: Gerhard Paulus <gpaulus@stud.uni-frankfurt.de>
  See http://rufus.w3.org/tools/Kaffe/messages/3785.html,
	http://rufus.w3.org/tools/Kaffe/messages/3784.html

* Runtime.runFinalizersOnExit() does not run the finalizers of all objects
  on exit, but only of those objects that are finalizable at the time.  It 
  hadn't even occurred to us that Sun wants us to run the finalizers of live 
  objects when exiting, but this is what the 1.2 JDK doc says:
  
      Enable or disable finalization on exit; doing so specifies that the 
      finalizers of all objects that have finalizers that have not yet been 
      automatically invoked are to be run before the Java runtime exits. 
      By default, finalization on exit is disabled. 
      Deprecated. This method is inherently unsafe. It may result in 
      finalizers being called on live objects while other threads are 
      concurrently manipulating those objects, resulting in erratic behavior 
      or deadlock.

ARCHITECTURE-SPECIFIC PROBLEMS
---------------------------------------------------------------------------

* Kaffe fails to work for several people under SunOS 4.1.3.  The error
  message seems to be "Can not find native library in path".  This problem
  is unclear: either a problem in the kaffe script that use @libdir@ and set 
  LD_LIBRARY_PATH (less likely), or a problem in the dynamic linking 
  mechanisms on this system.  Check the mailing list archive for more info.

* There have been reports of problems with exception handling on Linux 2.1.
  These problems have to do with libc versions.  Unfortunately, no patches
  have been submitted.  As a matter of precautious, make sure you run your
  kaffe binary on the system on which it was compiled, that is, don't run
  Linux 2.0 binaries on 2.1 or vice versa.  The test LostFrame in 
  test/regression (and possibly others) should fail if you're affected
  by this problem.  We would really appreciate a mechanism that would
  allow us to detect and handle different libc versions at run-time.

* On netbsd1.3/arm32, the % operator for long data types is broken.
  This means that the sign of such an operation may come out wrong.
  Java says it must be equal to the sign of the dividend.  This is
  mostly due to a bug in __moddi3.  FreeBSD <=2.2.8 and <=3.0 have
  the same problem, but there it's only in libc, not in libgcc, so
  we instruct configure to explicitly link with libgcc.


THINGS WHERE KAFFE'S BEHAVIOR DIFFERS
---------------------------------------------------------------------------

* Class.getFields() returns Fields in reverse order compared to Sun's jdk.
  Apparently, this causes some software such Cygnus's kawa to fail in 
  certain circumstances.  It seems like they should fix that.


MISCELLANEOUS & TODO
---------------------------------------------------------------------------

* Add some architecture-specific stack pointer alignment macro.  Currently,
  alignment is sizeof (jlong); apparently, some architectures require more
  specific alignments.

* While Kaffe currently doesn't compile with other compilers than Cygnus
  cygwin32 gcc compiler, we do know that people are trying to compile it
  with compilers such as Visual C++.  Jongwon Kim <freefish@chollian.net>
  reports that FIELD_OFFSET in classMethod.h conflicts with winnt.h.

* JNI incompatibilities pointed out by Johannes Deisenhofer 
  <joe@dillingen.baynet.de>:

 - NewXXXArray() should return jXXXArray instead of jarray
   This is a problem in C++, since these types are not simply casted to jref

  Note kaffe's jni.h doesn't even define these array types as of yet.

* The call to `native' in external.c is unprotected in the interpreter.
  In the jitter, this call is protected by locking the class.

* When verifying a method, we lock the whole class.  This means that other
  threads attempting the call other methods may be blocked until the
  verification succeeds.  This could lead to deadlock.
  
* OutOfMemoryErrors are not handled properly.  Don't even try to write 
  applications that attempt catch and recover from them.  Running out of
  memory may cause seemingly unrelated assertion failures, such as
  "Assertion `jitting == 0 || !!!"reentered jitter"' failed."
  Try increasing the total heap size using "-mx" in this case.

* Stopping threads at inopportune times may corrupt the VM.

* Classloaders don't keep classes alive if they are only an initiating loader of
  that class, but did not define that class.  This will cause problems with
  class gc in the face of delegation if the loader to which the loading was 
  delegated becomes unreachable before the loader that delegated it.
  A possible fix is to walk the centry for each loader; this would also
  allows us to get rid of the loadedClasses hashtable in 
  java/lang/ClassLoader.java.

* kaffeh leaves incomplete output files around if it bails because it cannot
  find the class file.

kaffe-1.0b4/FAQ/FAQ.amigaos   644    624    310       14335  6552662340  10341 KAFFE                                                       COMMON PROBLEMS
===========================================================================
Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996-97.
Ported to AmigaOS by Matthias Hopf <mshopf@informatik.uni-erlangen.de>


                     NOTES ABOUT THE AMIGAOS PORT
                     ============================

The Amiga port has been done in a relatively straightforward way using  the
Geek  Gadgets  (abbreviated  GG  in  this  document) developer environment,
formerly known as ADE (Amiga Developer Environment). Thus to work  properly
it  needs  at  least  version 45.0 of ixemul.library, available from any GG
mirror.

The needed Sun java classes are not included due to legal reasons. They are
available  as  a  seperate archive, e.g. kaffe-0.9.0-sun.tgz. If you get an
'Cannot find essential class 'java/lang/Object' in class library ...',  you
probably  do  not  have  the  Sun  classes  included in your CLASSPATH (see
below). Note that begining with kaffe-0.9.0 you  will  need  the  JDK-1.1.1
version of classes.zip.

To set up kaffe, do 'make  install'  in  an  GG  environment,  set  up  the
environment  variables as stated in the file ENVIRONMENT and make sure that
kaffe can be found in your path as well as in  the  sh  'PATH'  environment
variable.

Try 'kaffe HelloWorldApp' in the test/ directory. Guess what it does :-)

Try 'javac HelloWorldApp.java' in the  test/  directory.  But  be  prepared
- the javac compiler class is relatively slow...

Try 'make test' in the main directory. This  will  run  a  number  of  test
suites. Most tests should work by now. This test takes a lot of time...

When kaffe does not work properly, please  read  the  COMMON  CONFIGURATION
PROBLEMS  and  KNOWN  BUGS sections below before sending any bug reports to
me, or even better to the mailing list gg-java@ninemoons.com.  When  I  ask
you  to,  please  fill  out the system configuration report you can find in
config/m68k/amigaos/REPORT.amiga and send it to me. It will help debugging.



                     COMMON CONFIGURATION PROBLEMS
                     =============================

- 'version ixemul.library'
  You will need at least V44.0 of ixemul.library. The kaffe binaries that
  can be fetched from ftp.ninemoons.com need at least V45.0 of
  ixemul.library.

- Sun's classes.zip file.
  For kaffe to work properly you will need the JDK 1.1.1 classes.zip file
  from Sun, available from Sunsoft or from any kaffe-sun-* archive from
  ftp.ninemoons.com.
  *NOTE* The JDK classes.zip files from earlier releases will not work with
  kaffe! Guavac seems to be shipped with the old Sun classes.

- 'echo "$CLASSPATH"'
  The classpath has to include at least '.' (the current directory) and
  Sun's classes.zip file (the file itself, not the directory where it is
  located). A valid classpath may be set with e.g.
    setenv CLASSPATH ".;GG:share/kaffe/classes.zip"
  Don't forget the doublequotes (';' is the comment specifier in AmigaDos).
  And do not forget the trailing dot when specifying the current directory
  or volumes (e.g. 'kaffe:.'). Of course the CLASSPATH specification is
  also affected by your ixprefs settings!

- 'ixprefs -i 0'
  Either you will have to make ixemul.library recognize global environment
  variables, or you will have to set the local environment variable
    set CLASSPATH="$CLASSPATH"
  Again, don't forget the doublequotes.

- 'EMT Trap.'
  When kaffe simply aborts with 'EMT Trap', then you have probably an
  executable compiled for Amigas with coprozessors (versions before 0.7.1
  from GG were compiled with -m68881 by accident).
  It is possible, that you have the wrong ixemul.library installed, too.
  If you do not get this problem regulary, it may well be that you just
  encountered a problem inherrited to several IDE drives which cannot
  handle high MaxTransfer settings (if any is set up at all...).
  If you run kaffe from a SCSI drive, it can also be a drive and/or host
  adaptor with reselection problems. In that case disable reselection.



                              KNOWN BUGS
                              ==========

KNOWN BUGS of the current version

- Not all pointer access are checked for null pointer accesses. However,
  kaffe should work better now than in the last few versions.



KNOWN BUGS of former versions

* distributions before V0.9.0

- NullPointerExceptions were not generated at all. The kaffe code relied on
  SIGSEGV signals for NULL pointer access, which cannot work on AmigaOS.
  *NOTE* On NULL pointer access you *will* encounter Enforcer hits.
  These can even crash your computer...

* distributions before V0.7.1

- When kaffe simply aborts with 'EMT Trap', then you have probably an
  executable compiled for Amigas with coprocessors (binary archives from
  GG were compiled with -m68881 by accident and the configure script
  added the options -m68020-40 and -m68881).

* distributions before V0.7.0

- kaffeh produced loads of (most times harmless) Enforcer hits and
  generated wrong files sometimes.

- kaffeh liked to abort with 'Bus error' when invoked without the
  '-stubs' option.

* distributions before V0.6.0

- kaffeh of distributions before V0.6.0 used a different CLASSPATH scheme
  (':' as seperator).

- kaffevm/thread.c did not compile without warnings because of the thread
  switching macros.

- kaffe-0.5p4 aborted with an 'Exception thrown on NULL object' when the
  classes.zip file (see below) couldn't be found. kaffe-0.5.5 simply
  crashes, however. Begining with kaffe-0.6.0 this abnormal exception is
  caught.


KNOWN COMPILATION BUGS of former versions

* distributions before V0.7.1

- Kaffe was compiled with -m68020-40 and -m68881 by accident.

- Several version did not compile when fetched from Tim's original kaffe
  site. The GG versions had several bug fixes.


NONWORKING CLASSES

- some tests in regression still fail

* distributions before V0.9.0

- exception/NullTest  (see BUGS)
- exception/StackDump
- some more...


OTHER NOTES

* distributions before V0.7.1

- The thread switching code was not perfect. However, you should not notice
  that except when compiling kaffe...



That's it, folks :)

Matthias Hopf
<mshopf@informatik.uni-erlangen.de>


----
* Java and Javasoft are registered trademark of Sun Microsystems, Inc.
kaffe-1.0b4/FAQ/FAQ.automake   644    624    310        6311  6703446343  10503 **************************************************************************
IMPORTANT NOTE:

- in order to modify the Makefiles and configure.in, you need rather
recent versions of autoconf and automake: autoconf 2.13 and automake
1.4.  automake is supposed to work with perl4, but it apparently
doesn't, which means you probably need perl5 as well.

**************************************************************************

Kaffe has adopted GNU automake to ease the maintenance of Makefiles.
Changes were mostly straightforward, and nothing really important has
changed in the build environment.  I could list a few changes here:

- EXTRA_CFLAGS is no longer used; you can now use AM_CFLAGS and
AM_CPPFLAGS.  Prefer the latter for preprocessor definitions such as
-DDEBUG.

- using automake means editing Makefile.am instead of Makefile.in, and
that it will take care of rebuilding autoconf/automake-related files
whenever they become out-of-date, but only if you
--enable-maintainer-mode at configure time.  If not, you'll have to
run "aclocal; automake; autoconf; autoheader -l config" (or just
developers/autogen.sh) manually in the top-level source directory.

- you also have to specify which files should go in a distribution
(make dist): they should either be listed as sources to some binary or
library or be listed in the EXTRA_DIST variable.  In order to avoid
missing some file, you should run `make distcheck' instead of just
`make dist'.  distcheck runs make dist, builds the dist tree and runs
make check on it.

- since automake 1.4 doesn't accept sources from other directories, I
have created some makefiles in subdirectories such as jit, intrp and
system/unix-*.  A special kind of temporary library, called libtool
convenience library, is created to hold files from each of these
directories, and then the convenience library is included in
libkaffevm.  In other cases, such as gc-mem.c, I have preferred to
just create a gc-mem.c forwarder (a file that #includes mem/gc-mem.c)
within kaffe/kaffevm.  If we later decide that it was not a good idea,
it is very easy to create a Makefile.am within kaffevm/mem and create
a convenience library there.

- `make depend' is now implicit, but it only works with gcc and GNU
make.  A distribution file with portable makefiles should be created
with `make dist'

- `make test' is now called `make check', which is much more standard

- I added a new flag, --with-staticvm, to make only the VM library
static.  --with-includes and --with-libraries were also introduced, to
allow the user to specify a list of directories to search for
include-files and libraries.  --with-rtlibraries can be used to
hard-code run-time library search paths into the Kaffe program, so
that it can find libraries such as X11, libungif, etc, that happen to
be installed in non-standard places.  This option does not affect the
search path for native libraries loaded from Java code.

- you may miss one or other make target that I may have eliminated
from the Makefiles, for example, recursive `make classes' and `make
derived-files'.  It should be straightforward to re-create them within
Makefile.am's, but I'd prefer to avoid these, and create actual
dependencies to ensure that derived files are re-created as needed.

--
oliva
kaffe-1.0b4/FAQ/FAQ.awt   644    624    310        1443  6604574126   7472 FAQ for the AWT
===============

1.0.2 Event Model
-----------------

We do *not* support the old 1.0.2 Event Model since this has been deprecated.
Beware that code exists which 'claims' to be JDK 1.1 compatiable but still
uses some of the 1.0.2 features.

1.0.2 Deprecated methods
------------------------

We're adding in missing deprecated methods as we find them.  If you come
across a missing one, send us a patch.

Swing
-----

Whatever Sun may tell you about Swing being 100% pure Java - just don't
believe them.  Swing make a number of assumptions about the underlying AWT
implementation and makes calls to a number of "banned" methods - oh and it
makes extensive use of deprecates.  We're working toward Swing compatibility
but since this stuff is *all* undocumented it may take a little time.
kaffe-1.0b4/FAQ/FAQ.class-states   644    624    310        3702  6631343020  11270 
The new state graph for class loading.

			       PRELOADED
				  |
				  V
UNLOADED --- read bytecode ---> LOADED 
   +------------------------------|
   |    			  V
   |    		     DOING_PREPARE -- go "PREPARE" --> ClassCircularity
   |    			  |
   |    		bring superclass to LINKED
   |    		build interface table
   |    		resolveObjectFields
   |    		resolveStaticFields
   |    		buildDispatchTable -- any fail -------------------+
   |    			  |					  |
 L |    			  V					  |
   |    		       PREPARED					  |
 O |    			  |					  |
   |    			  V					  |
 C |    		       verify2					  |
   |    		       verify3     -- any fail -------------------|
 K |    			  |					  |
   |    			  V					  |
   |    		       LINKED					  |
   |    			  |					  |
   |    			  V					  |
   |    		resolveConstants       -- fail -------------------|
   |    			  |					  |
   |    			  V				     	  |
   |    		      DOING_SUPER  --> on go wait 		  | 
   |    			  |					  |
   +------------------------------|					  |
				  |					  |
        		bring superclass to COMPLETE       --> fail --> FAILED
				  |					  |
				  |					  v
 L +------------------------------|				   return false
 O |    			  |					  ^
 C |    			  V					  |
 K | 			   USABLE/DOING_INIT  --> on go wait 		  | go
   |				  |					  | "CO
   +------------------------------|					  | MPL
				  |					  | ETE"
        		    call static {}		  		  | 
				  |					  |
 L +------------------------------|					  | 
   |    			  V					  |
 O |    		          |--- static {} FAILS --> INIT_FAILED ---+
   |    			  |				|
 C |    		    static {} returns okay		|
   |    			  |				|
 K |    			  V				|
   +---------------------------COMPLETE				|
				  |				|
				  +-----------------------------|
								|
								V
							    return true


PRELOADED						COMPLETE(*)
     \							/
   LOADED -> DOING_PREPARE -...-> DOING_SUPER -> USABLE/DOING_INIT
     /							\
UNLOADED						INIT_FAILED(*)
			FAILED(*)

(*) final state
kaffe-1.0b4/FAQ/FAQ.classlibrary-compile   644    624    310        2355  6703446074  13022 
How do I compile the class library?
-----------------------------------

Cd to your build directory (the same as the source directory if you
did ./configure), then cd to libraries/javalib.

Type "make Klasses".
This will build the java libraries, put them in a Klasses.jar file
and overwrite the version in your source tree.  Type "make install"
to install the jar file in your target prefix.

If you have added or removed files from the javalib tree, you may have
to run "make new-classes" first.  This will update Makefile.am, but
not Makefile.in nor Makefile.  In order to update these other files,
for the changes to take effect, you'll need automake 1.4 or newer, and
you'll have to configure with --enable-maintainer-mode or run automake
by hand.

If you want to rebuild not only Klasses.jar, but also the jar-files of
Kaffe extensions, type "make CLASSDIRS=all Klasses".

What compilers are known to work?
---------------------------------

As of 4/1/99, the pizza in Kaffe's pizza.jar should work, 
as well as Sun's 1.1.5 javac (according to Peter).

jikes Version 0.47 also works, and is quite speedy.
jikes is available from http://www.ibm.com/research/jikes

Sun's 1.1.7 or 1.2 javac does not work. (Somebody wants to file
a bug report with them?)

kaffe-1.0b4/FAQ/FAQ.depend   644    624    310         333  6663635727  10125 Kaffe's build environment no longer has a depend target; automake
takes care of it with the developer's version of the makefiles.  After
`make dist', dependencies are hard-coded into the Makefiles.

-- 
Alexandre Oliva
kaffe-1.0b4/FAQ/FAQ.gcblock   644    624    310        4723  6645025361  10304 The new gc_block allocation scheme optimizes virtual memory and
cache behavior.

Previously, each page of the heap had a header, struct gc_block.
The conservative GC tested whether a pointer pointed into the heap by
searching a simple hash table called gc_object_hash.  The new
allocation scheme allows faster conservative object marking by
eliminating gc_object_hash, and reduces cache conflict misses and
paging by placing struct gc_blocks in an array.

Since gc_blocks are not part of the pages they describe further memory
optimizations are possible:  Free pages anywhere in the heap can be
marked as low priority with madvise.  And, free pages can be made
unreadable, as is done when compiled with -DDEBUG.  Madvise is not
currently used, since it seems to have a negligible effect.  

Rather than searching a hash table to find a gc_block structure,
markObject simply needs to subtract the heap base from a pointer, and
use the difference in pages to index the gc_block array.

There are some complications:  The java heap is not really an array of
pages:  Pages allocated by malloc and gc_system_alloc may be interleaved.
There are three ways to deal with this:
1. Allocate the entire heap up front, which needlessly ties up
   resources.
2. Allocate some address space, then fill in the heap and gc_block
   array as needed (using mmap).  On systems which support the
   MAP_NORESERVE flag, mmap can allocate address space without
   allocating backing store.  But, on other systems a strange trick
   would be required:  Call mmap(0) to find out where mmap wants to
   place memory, add an arbitrary constant, and start the heap there.
   This trick is less than reliable.  And, neither approach works on
   systems that don't have mmap.
3. Allocate the full gc_block array up front.  Compensate for holes in
   the heap address range, but be prepared to realloc the gc_block
   array if neeeded.

I have taken the third approach.  Kaffe initially allocates an array
of max-heap-pages * 1.25 gc_block structures using malloc.  If this
array turns out to be too small (because quite a few memory
allocation requests are bypassing gc_malloc), the array is
realloc'ed to a better guess at its maximum size.  Pointers to
gc_blocks (both inside gc_blocks and in variables such as
gc_prim_freelist) are relocated.  

This adds a wrinkle to any work on a concurrent GC:  There can be no
gc_block *'s on any stack when the array is realloced (inside
gc_heap_malloc).

Jason Baker <jbaker@cs.utah.edu>
Jan  6, 1999
kaffe-1.0b4/FAQ/FAQ.gcstrategy   644    624    310       16344  6644562251  11101 
Kaffe's garbage collector is a classical conservative collector that follows
the tricolor scheme, and it is not very sophisticated.  However, by tweaking 
its parameters, it is sometimes possible to speed up applications 
significantly.

This FAQ explains an gc strategy I implemented for Kaffe and it should
answer the following questions:

+ How does Kaffe decide when to garbage collect and when to get more memory
  from the operating system instead?

+ How does Kaffe use the parameters specified by the -ms and -mx switches?  
  What's that -as switch for?  What values should I choose for what 
  applications?

Kaffe recognizes the following three switches, which can be followed by
a numerical argument (either immediately following the switch, as in
``-mx32M'', or separated by a space as in ``-mx 32M'') specifying a byte amount.
K/k and M/m suffixes denote Kilo and Mega bytes, respectively.

	Default
-ms:	 5M	This value specifies the initial heap size.  When Kaffe
		starts up, it will request this amount of memory upfront
		from the operating system and add it to the heap it
		manages.

-mx:	64M	This value specifies the maximum heap size.  Kaffe will
		never request more memory than that from the operating
		system.  Note that the total memory usage of kaffe may be 
		higher since libraries such as Xlib may obtain memory from 
		the operating system without consulting Kaffe's allocator.

		Note further that Kaffe's internal data structures will
		be allocated from that heap as well (not only the garbage
		collectable objects produced by your application.)

-as:	 1M	This value specifies the heap increment.  The heap increment
		is the amount of memory Kaffe will request from the operating 
		system to add to the heap it manages if it decides to postpone
		a collection.  See below what ``postponing'' means.

If kaffe's allocator is asked to provide some memory, it follows a simple
strategy.  First, it checks whether there is some free memory of the 
corresponding size in the heap it manages.  If so, this memory is returned.

This means that kaffe won't ever invoke the garbage collector until it
uses more memory than the initial heap size specified using the -ms switch!

Therefore, increasing the initial heap size using -ms will make short-lived
applications, such as pizza, run faster.  Garbage collection can be avoided 
if an application does not allocate more memory than the initial heap size
during its lifetime.

If there is no memory left in the heap when an allocation is attempted, 
Kaffe will invoke the garbage collector.  The collector uses a heuristics to 
decide whether it should perform a collection or whether it should postpone it.

Postponing a collection means that Kaffe asks the operating system for
more memory, growing its heap.  Every time it decides to postpone a collection, 
it will ask for the amount of memory specified by the heap increment (-as) 
cmdline switch, which defaults to 1MB.  Of course, postponing gc is only an 
option if the current size of the heap is less than the maximum heap size 
specified.  If the maximum heap size has already been reached, a collection
will be performed.

If the maximum heap size has not been reached, how does Kaffe decide whether
to grow its heap or whether to garbage collect?  Like many things in CS,
it's a classical space-time trade-off.  Consider the two extremes:

First, suppose Kaffe always postponed collection until it obtained the 
maximum amount of memory from the system.  This would mean that collections
would occur less frequently, but it would also mean that most long-running 
applications will actually require the full amount of memory specified as
the maximum heap size.  This would mean to trade memory for speed.

Trading memory for speed is not always desirable, for instance if the
machine on which you're running has little memory, or if you can't afford
to set the necessary amount of memory aside.  In systems using virtual
memory, specifying heap sizes that are too large may lead to the well-known 
thrashing effect.

On the other hand, if Kaffe always collected if it ran out, it would collect 
more often, but it won't ever use much more memory than the amount of memory 
occupied by long-lived and fixed data.  This would mean to trade speed for
memory.

Let's look at a hypothetical example:
Suppose an application uses 16MB of long-lived data, and produces
160 MB of short-lived data.  Suppose the initial heap size is 5MB and the 
maximum heap size is 64MB.  The heap increment is also the default, 1MB.

If we always collected, then this application won't ever take more than
17MB, but it will perform 160/(17-16) = 160 collections!  On the other hand, if 
we used all the available memory, then the application would use 64MB, but 
only perform 160/(64-16) = 4 collections.

[ You might say: but wait, the cost of each of the 160 collections will be 
lower than the cost of the 4 collections in the second case.  This is not
true, however.  The costs of Kaffe's mark-and-sweep algorithm is the sum
of the costs of marking the heap and sweeping it.  The costs of sweeping
is linear in the total number of objects freed --- which is the same no
matter how frequently you collect.  However, the costs of marking only
depends on the amount of live data, which is roughly equivalent to the
amount of long-lived data.  Hence, this cost is the same in both cases,
but since this cost must be paid per collection, fewer collections will
be faster. ]

So, what does Kaffe do to find the sweet spot between not collecting too
often and not using too much memory?  It looks at how much memory has been
allocated since the last time a collection happened.  If this amount of
memory is less than 1/3 of the total amount of memory in use, then the
collection is skipped.  In our hypothetical example above, Kaffe would 
use 24MB (since it will grow to 24MB in 1MB increments.)  Every time 8MB 
of short-lived data have been added to the long-lived data occupying 16MB,
kaffe will collect and free 8MB.  Hence, it will perform 160/(24-16) = 20 
collections instead of 160 or 4.  

Of course, if you have 64MB to spare, running Kaffe with '-ms 64M' is
always your option and will require only 4 collections.

Why 1/3?  It's just a number I pulled out of my hat.  Asymptotically, the
number means that Kaffe's heap will always have 33% free memory after a
gc.  Using the -verbosegc option will tell you how much it actually is.

If you know of a better heuristics --- maybe one that takes other factors
than the amount of memory allocated since the last collection into account,
we'd certainly all like to know about it.  Possible candidates for such 
factors are the time elapsed since the start of the program, the time it 
takes to mark the heap, the time it takes to sweep freed objects, the length
of the finalizer list, and the phase of the moon.

Note that the discussion and the number in this FAQ are somewhat hypothetical
and do not take various overheads into account.  In particular, they do not
account for Kaffe's unfortunate tendency to keep more temporary garbage afloat
than it probably should.  However, we're still working on improving Kaffe's
collector.

Thanks to Jason Baker and Archie Cobbs for contributing to the discussion 
above.  Any comments and suggestions for improvements are welcome.

	- Godmar Back <gback@cs.utah.edu> 

1/5/99
kaffe-1.0b4/FAQ/FAQ.hotjava   644    624    310        1330  6654133704  10324 
HotJava does not currently run with kaffe and it is very unlikely that
the current HotJava version (1.1.5) ever will.

The reason is that HotJava is not 100% pure Java.  It relies upon
classes from the proprietary sun.* hierarchy.   References to such classes 
are hardcoded in HotJava's code.  

To make matters worse, it not only relies on classes that have 
native methods (which would allow the kaffe user to simply use Sun's 
classes.zip by appending it to the CLASSPATH), but it relies on classes 
that have undocumented native methods, for instance for gif image 
processing or inqueries about the state of the VM. 
HotJava even bypasses equivalent, documented interfaces provided by
classes in the java.awt hierarchy.

kaffe-1.0b4/FAQ/FAQ.install-root   644    624    310        2705  6646471716  11337 
Setting an "install root" directory
-----------------------------------

If you want to build and package kaffe on one machine, but actually
run it on another machine, you may want to set an "install root"
directory.

Setting an install root directory only affects "make install". It
causes every install target directory to be prefixed with the install
root directory. The default install root directory is just /.

For example, if you say:

  $ ./configure --prefix=/usr/local
  $ make
  $ make install

this does two things:

  1. Kaffe is compiled in such a way that it expects to live in
     /usr/local, and this is where it will look for certain files
     at runtime.

  2. The "make install" step will install everything under /usr/local.

Setting an install root affects step #2 only -- not step #1.

To set the install root directory to /tmp/kaffe-build, for example,
add the "DESTDIR=/tmp/kaffe-build" argument to `make install':

  $ ./configure --prefix=/usr/local
  $ make
  $ make install DESTDIR=/tmp/kaffe-build

If you do the above steps, then kaffe will still expect to live in
/usr/local, but "make install" will install everything under
/tmp/kaffe-build/usr/local instead of just /usr/local.  Note, however,
that this may break libtool shared libraries on some platforms.

Then you can tar up the /tmp/kaffe-build directory, take it to your
target machine and install it, etc.

-Archie Cobbs <archie@whistle.com>,  Alexandre Oliva <oliva@dcc.unicamp.br>
kaffe-1.0b4/FAQ/FAQ.jsignal   644    624    310       17202  6654123053  10340 
Kaffe: Signals & jthreads
-------------------------

	by Patrick Tullmann and Godmar Back
	<tullmann@cs.utah.edu> and <gback@cs.utah.edu>.

This document is an attempt to describe the behavior of signals and
threads in Kaffe, specifically in the jthread threading system.  Other
threading systems in Kaffe should be similar.  It is hoped that folks
trying to port Kaffe to other operating systems will use this document
to figure out just what Kaffe needs in terms of signal and
setjmp/longjmp support so they can match it to what their OS provides.

Kaffe uses signals for:

	Timeslice expiration for pre-emption (SIGVTALRM).
	Timeouts on Object.wait()s, Thread.sleep()s (SIGALRM).
	Asynchronous notification of I/O readiness (SIGIO).
	Synchronous processor errors (SIGFPE, SIGSEGV, SIGBUS). 
	Asynchronous notification of a child's death (SIGCHLD).
	Cleaning up fds before dying (SIGINT, SIGTERM).

	SIGPIPE is ignored (see initExceptions() in baseClasses.c).

	All other signals have their default behavior.

	Neither of the user signals (SIGUSR1 and SIGUSR2) are used for
	anything in jthreads.  (In Godmar's Linux threads port for
	Kaffe, the user signals are used for stopping kernel threads.)
	(Oh, and Godmar once used SIGUSR1 for prompting the VM to
	dump all sorts of thread info when its received.)

Signals can be grouped into two categories: asynchronous and
synchronous.  Synchronous signals are a direct result of the current
thread's immediate action: SIGFPE, SIGSEGV, and SIGBUS. Asynchronous
signals are those that are not a direct and immediate result of the
currently executing thread's actions: SIGVTALRM, SIGALRM, SIGIO,
SIGCHLD.  SIGINT and SIGTERM are terminal signals and are not
classified as either synchronous nor asynchronous.

Signals are handled by whatever thread is currently executing.
Signals are handled on the currently executing thread's stack.
Several operating systems support a separate "signal stack" for
handling signals.  Kaffe does not use separate signal stacks because
the GC thread-stack-walk function requires that the registers in
use when the signal arrived be visible.  In the current scheme those
registers are pushed onto the thread's stack, so they are directly
visible to the stack walking code.  This is believed to be the only
barrier to using a separate signal stack.

In this document "signal state" refers to whatever OS state is saved
and restored when sigsetjmp() is given a non-zero second argument.  We
assume that this signal state only includes the mask of blocked
signals.  If a system includes other information in its signal state,
the signal handlers will have to clean that state up before
longjmp()'ing anywhere.


Synchronous Signals
-------------------

For synchronous signals, the handling thread is the thread which
caused the problem.  How the signal is transformed into a Java
exception is different in the interpreter and the JIT.  The important
point is that for synchronous signals, the signal handler *never*
returns, it unilaterally jumps directly to the exception handler.

For the interpreter, a setjmp() point was created for each exception
handler entry point as the first bytecode covered by the handler was
executed.  The signal handler will longjmp() to this point to handle
the exception.  All of the setjmp()'s occur in the context of the
virtualMachine() function in intrp/machine.c; virtualMachine()
recurses for each Java method invocation, so the context of the
setjmp() is always valid.  The longjmp() to the handler deals with
"unwinding" the stack, if necessary.  The setjmp() need not include
signal state as the signal handler will clean up the signal state
before longjmp()'ing to the exception handler.

The JIT keeps information on code ranges covered by each exception
handler in a method.  When a synchronous exception is dispatched out
of the signal handler, the appropriate location is looked up in the
per-method exception tables, and CALL_KAFFE_EXCEPTION() is invoked.
This method (on FreeBSD, at least) does an (asm) jmp to the
appropriate native code to handle the exception after "unwinding" the
stack.  Since the signal handler for synchronous signals in the JIT
never returns, the signal handler must clean up the current signal
state before jumping off into the exception handler.  This entails
(potentially) restoring the signal handler and (potentially)
unblocking the signal.  (Some OS's disable the signal handler on first
use, others don't.  Some OS's block signals before entering a signal
handler, others don't.)


Asynchronous Signals
--------------------

Asynchronous signals are handled by a thread which is not necessarily
involved in the action which triggered the signal.  For example, if a
SIGIO comes in, the handler might put threads which are blocked on the
appropriate I/O channel onto the runnable queue, and then return.  As
another example if thread A is running and a SIGVTALRM signal is
delivered, thread A might enter the thread scheduling code, pick a
new thread, B, and longjmp() to B's setjmp() point from when it was
preempted or blocked.  Eventually, thread A will be rescheduled and
will return from the signal handler into the context where the
original SIGVTALRM interrupted it.  (Note that rescheduling might
happen as a result of a SIGIO---for example, if a higher priority
thread was waiting on the IO channel.)

Because a signal handler might longjmp() into any thread, the signal
state of the Kaffe process must be clean before the longjmp().  If we
jumped into a thread and left all the asynchronous signals blocked,
the system would grind to a halt.


The Optimization
----------------

When a thread blocks (for example on a mutex, in a wait(), due to
yield(), or in a sleep()) the thread has no interesting signal state
associated with it.  Saving and restoring this state is potentially a
great waste of time.  (On FreeBSD the cost of a context switch
decreases from 1,400 cycles to 240 cycles on a P2-300 when signal
state is appropriately ignored.)  Thus, we make sure that the process
signal state is clean before longjmp()'ing out of a signal handler.


Signals, Setjmp() and Platform Dependencies
---------------------------------

Some platforms need to re-install a signal after it arrives.  By
default, Kaffe calls reinstallSignalHandler() when it feels safe in
re-enabling the signal.  On some platforms this call may be no-op'd.

For integrity, Kaffe requires that when handling any asynchronous
signal *all* other asynchronous signals are delayed.  Asynchronous
signals should be delayed until the signal handler returns, or the
signal handler explictly unblocks them (unblockAsyncSignals()).  (The
set of signals to delay while handling signal X is specified by the
sa_mask field used in the sigaction() call for signal X---see
registerAsychSignalHandler() in exception.c.)  (NOTE: If delay of all
asynchronous signals cannot be guaranteed, the race condition between
checking blockints and disabling interrupts in interrupt() will have
to be solved in some other fashion.)

For synchronous signals, no signals should be delayed during the
execution of the signal handler.  Most systems will always delay
signal X when handling signal X.  The system must support explicitly
re-enabling the signal as the synchronous signal handlers never return
(thus, the OS will not get a chance to restore the signal state).

Kaffe requires that sigsetjmp() and siglongjmp() save and restore
signal state when indicated (the second parameter on the sigsetjmp is
non-zero).  It is sufficient for correctness if all setjmp/longjmp
calls save and restore signal state, its just overkill.  It should be
true that the second parameter to sigsetjmp() is always zero, so the
system should never have to save or restore signal state for a setjmp
or longjmp.
kaffe-1.0b4/FAQ/FAQ.libtool   644    624    310        6070  6703447375  10351 Kaffe has adopted GNU libtool to ease the creation of shared
libraries, where available, and static libraries, where shared
libraries are unavailable or undesirable.  Whether a library is shared
or static is totally transparent to the application that uses libtool
and to the user.

Any libtool library can be made static by adding -static to its link
command.  You can do that manually by removing the .la file in the
corresponding directory and running `make AM_CFLAGS=-static' within
that directory, then running make in the top-level directory.

In order to provide dynamic linking, or simulate it, Kaffe uses
libltdl, a library that is part of libtool.  It currently supports the
following dlopening mechanisms: dlopen, shl_load, GNU DLD, BeOS'
load_add_on and MS-Windows' LoadLibrary.

Additionally, it supports dlopening simulation for platforms that lack
shared libraries (or have shared libraries disabled) through the
dlpreopen mechanism, in which a symbol table is created when the
program is linked and it -dlopens a set of libraries.  If any of these
libraries is static, libtool will link the library into the program
and add its symbols to the dlpreopening symbol table.  It's that
simple.

In order to implement this dlpreopening mechanism, libltdl needs some
help from the main application.  Therefore, a call to
lt_dlpreopen_default() was added to kaffe/kaffe/main.c.  In order to
support dlopening simulation, any other application that links with
libkaffevm should also be linked using libtool, and it should be
created with the flag -export-dynamic, so that the symbol table is
created.

By default, on platforms that lack dlopening mechanisms, Kaffe will be
linked with its own libraries only.  You may extend this list of
libraries by setting the JAVA_LIBS flag to a list of libtool (.la) or
regular (.a/.so/.sl/.lib) libraries.

If the same symbol is defined in more than one library, it is possible
that linking (either static or dynamic) fails.  If it does not fail,
which symbol libltdl will select for a given name depends on the
sequence of dlopening of the libraries, and the mechanism libltdl uses
to dlopen them.


By default, libtool compiles each file that may become part of a
library twice.  It does so because a file must be compiled with PIC
(position independent code) in order to become part of a shared
library, but this sometimes imposes some overhead, and libtool
believes that a static library shouldn't impose this overhead.
Therefore, libtool uses an object file with PIC to build a shared
library, and one without PIC for static libraries.

Kaffe changes this libtool default so as to avoid double compilation,
so you'll get only PIC object files if shared libraries are supported
and not disabled, and only non-PIC ones otherwise.  You may force
non-PIC objects to be created with --enable-static; you may force
libtool not to create shared libraries by configuring
--disable-shared.  In fact, --disable-shared is almost equivalent to
--with-staticlib --with-staticvm; the only difference is that
--with-staticlib does not affect libltdl, whereas --disable-shared
does.

--
oliva
kaffe-1.0b4/FAQ/FAQ.linux   644    624    310        1520  6552662340  10030 FAQ for Linux
=============

Doesn't work on Linux 1.2.13
----------------------------

Kaffe is no longer supported on Linux 1.2.13.  This is because of a limitation
in the shared library implementation on this system.  You should still
be able to use Kaffe in static library mode however (but don't quote me).

No 'dlopen' and 'dlsym'
-----------------------

Some verison of Linux fail to locate the dynamic library loading code
during configure.  This fault usually manifests itself during linking
as a failure to find 'dlopen' and 'dlsym'.  To fix this problem you will
need to create a sybolic link as follows:

	ln -s libdl.so.xx.xx /lib/libdl.so

Where 'xx.xx' is the version number of this library (ls -l /lib/libdl.so.*
should tell you this).

Once this has been done rebuild as follows:

	make distclean
	./configure
	make
	make install
kaffe-1.0b4/FAQ/FAQ.nativemethods   644    624    310        1324  6552662340  11545 From: Paul M Reilly <pmr@preilly.bbn.com>

Q. What's it take to get native code running in Kaffe?

A. For the most part, follow the steps described in various Java texts
(e.g. ``teach yourself Java in 21 days'', Chapter 20, or Chapter 13 of
``Programming with Java!'').  The major gotcha is in the naming of the
shared library.  If you are building a library to handle display
functions, loading it with the following Java code,

      public class Display {

        public native void toScreen();

        static {
          System.loadLibrary("Display");
        }
      }


then make sure to name the library "libDisplay.so" and put the
library in a directory that is listed in the LD_LIBRARY_PATH
environment variable.
kaffe-1.0b4/FAQ/FAQ.requiredlibraries   644    624    310        1426  6661347022  12411 
To use all functionality provided by Kaffe, you need some freely available
libraries installed on your system.

Some of these libraries are absolutely required, in other cases, the 
configure script detects whether the library is available and uses it 
if so.  If your library is installed is not installed in a standard
place where your compiler and linker finds it, you need to tell
configure where to find it.

<Alexandre explains how you do that with an example>

libungif 4.0:	You need libungif 4.0 if you want Kaffe's awt to be 
    able to decompress gif images.  Note that earlier versions will not
    work.  You can get libgif from:
    http://prtr-13.ucsc.edu/~badger/software/libungif.shtml

libz:		You need libz to use the functionality in java.util.zip

<Complete this list>
kaffe-1.0b4/FAQ/FAQ.timing   644    624    310        1503  6631360040  10147 The new timing mechanism allows arbitrary portions of vm execution to
be measured.  When configured --with-timing, kaffe will print a
summary of all timings measured on exit.
 
Usage is simple:
#include "support.h"
 
foo()
{
        static timespent spent;
 
        startTiming(&spent, "foo");
        ...
        stopTiming(&spent);
}
 
When kaffe is configured for timing, the macro TIMING is defined and
this code does something useful.  When not configured for timing,
these statements are mostly harmless:
/* We either can't or wont perform timing:  The first macro suppresses
   unused variable warnings. */
typedef char timespent;
 
#define startTiming(C,N) (*(C) = 0)
#define stopTiming(C)
 
The timing mechanism counts the number of calls to startTiming for
each timespent structure, and measures user time with getrusage.
 
kaffe-1.0b4/FAQ/FAQ.unicode   644    624    310        4627  6700347457  10336 Kaffe Unicode Database.

Kaffe use a compressed form of the Unicode 2.1 database for
the java.lang.Character class.

The Unicode 2.1 database, have lot of usefull compression properties.

The class java.lang.Character uses a subset of the the Unicode properties:
. The category [getType()]
. The decimal digit value [digit()]
. The numeric value [getNumericValue()]
. The uppercase, lowercase and titlecase equivalent [toUpperCase(),
  toLowerCase(), toTitleCase()]

Unicode compression properties:
. few characters have a titlecase equivalent different than the uppercase.
  [uppercase and titlecase of true titlecase character, category "Lt"]
. few characters have a numeric value and a case equivalent.
  [roman numeric letters, category "Nl"]
. all digit number "Nd" have the same decimal digit value than the numeric
  value.

Then, we define two character properties format, small and extended.

Small character proteries format:
. the category
. whitch field (none, numeric value, uppercase, lowercase)
. the generic value

xFFCCCCC GGGGGGGG GGGGGGGG

Extended character properties format:
. the category
. the numeric value
. the uppercase equivalent
. the lowercase equivalent
. the titlecase equivalent

xxxCCCCC NNNNNNNN NNNNNNNN UUUUUUUU UUUUUUUU
LLLLLLLL LLLLLLLL TTTTTTTT TTTTTTTT

Consecutives entries in the Unicode 2.1 database could be grouped with
the following rules:
. not compressed
  consecutive entries don't have the same category or the same field
  or the same value nor one increment.
  [U+0028 - U+002D]
. compressed same value:
  consecutive entries have the same category, the same field and the
  same generic value.
  [U+0000 - U+001F, control, no field]
. compressed one increment:
  consecutive entries have the same category, the same field and the
  same increment (one) for the generic value.
  [U+0041 - U+005A (A-Z) uppercase letter, one increment for lowercase]
. not compressed, extended entry:
  consecutive entried that have more than one field.
  [U+2160 - U+216F (Roman number)]


To handle all these range, we create an index with the format:
SSSSSSSS SSSSSSSS EEEEEEEE EEEEEEEE xxMMOOOO OOOOOOOO OOOOOOOO

S: start unicode value for this range
E: end unicode value for this range
M: compression method
O: offset in the properties table.


The Perl script unicode.pl creates two files
. unicode.idx the ranges index
. unicode.tbl the properties tables


Edouard G. Parmelan <egp@quadratec.fr>
March 27, 1999
kaffe-1.0b4/FAQ/FAQ.win32   644    624    310         763  6552662340   7623 FAQ for Cygnus Win32
====================

I don't in general to get into debugging installations of Cygnus's Win32
environment.  However a few problems come up often:

Failure to create temporary files during configuration
------------------------------------------------------

Often the configuration doesn't work correctly and reports the failure
to create odd files, usually with names like 921345.  This means you don't
have the expected temp. directory.  Make sure you have created 'c:\tmp'.
kaffe-1.0b4/developers/   755    624    310           0  6703462467  10111 5kaffe-1.0b4/developers/JavaClass.pm   644    624    310       63054  6660670355  12345 #
# Functions for reading in and writing out a Java .class file.
# Also does a bit of consistency checking of the file.
#
# The only really nasty thing I've done (because of poor perl skils more
# than anything else) is to make the %class a local() in a number of
# places so that the check routines can see it.
#
# Class structure:  Generally references to hashes. Tables are implemented as arrays.
#
# TODO: 
#	make a &checkClass() function.
#	change a lot of 'local's to 'my's. (not local(%class), though)
#	Make CLASSIN and CLASSOUT parameters to read/write functions.
#	POD documentation
#	Cannot handle modifying float values.  I can read and decode, but don't
#	have the math to convert back to a binary format (both floats and doubles).

#
# Copyright (c) 1999 University of Utah CSL.
#
# This file is distributed under the terms of the GNU Public License.
#


package JavaClass;

###
### Define constants for Java Classes
###

*classMagic = \0xcafebabe;	# The magic header every .class file starts with

## The magic identifiers for entries in the .class Constant Table.
*CONSTANT_Class = \7;
*CONSTANT_FieldRef = \9;
*CONSTANT_MethodRef = \10;
*CONSTANT_InterfaceMethodRef = \11;
*CONSTANT_String = \8;
*CONSTANT_Integer = \3;
*CONSTANT_Float = \4;
*CONSTANT_Long = \5;
*CONSTANT_Double = \6;
*CONSTANT_NameAndType = \12;
*CONSTANT_Utf8 = \1;

## String names associated with each type of Constant Table entry.
%CONSTANTNames = (
    $CONSTANT_Class => "Class",
    $CONSTANT_FieldRef => "Field",
    $CONSTANT_MethodRef => "Method",
    $CONSTANT_InterfaceMethodRef => "Inteface Method",
    $CONSTANT_String => "String",
    $CONSTANT_Float => "Float",
    $CONSTANT_Integer => "Integer",
    $CONSTANT_Double => "Double",
    $CONSTANT_Long => "Long",
    $CONSTANT_NameAndType => "Name&Type",
    $CONSTANT_Utf8 => "Utf8"
    );

## String names for the shorthand used in signatures
$sig{'V'} = 'void';
$sig{'I'} = 'int';
$sig{'J'} = 'long';
$sig{'Z'} = 'boolean';
$sig{'F'} = 'float';
$sig{'D'} = 'double';
$sig{'B'} = 'byte';
$sig{'S'} = 'short';
$sig{'C'} = 'char';

## Access control flags for classes, methods and fields.
*ACC_PUBLIC    = \0x0001;
*ACC_PRIVATE   = \0x0002;
*ACC_PROTECTED = \0x0004;
*ACC_STATIC    = \0x0008;
*ACC_FINAL     = \0x0010;
*ACC_SUPER     = \0x0020;
*ACC_VOLATILE  = \0x0040;
*ACC_TRANSIENT = \0x0080;
*ACC_INTERFACE = \0x0200;
*ACC_ABSTRACT  = \0x0400;
*ACC_NATIVE    = \0x0100;

*ACC_UNKNOWN   = \0xF800;

###
###  Global variables
###

# Control the verbosity of &printClass()
$detailedFields = 0;
$detailedMethods = 0;

###
### Conversion functions
###

## parseJavaSig() takes a single argument, a single Java-internal
## method signature and returns a list ($package, $return, $class,
## $method, @args) where the items have been converted to a more
## source-like format (e.g., english).
sub parseJavaSig() {
  ## Parameters
  my $jsig = shift;

  ## Local variables
  my $class = '';
  my $package = '';
  my $method = '';
  my @args = ();
  my $ret = '';

  ## Temporaries
  my $depth = 0;
  my $repct = 0;
  my $arg = '';

  ### First is the class (all chars until a ".")
  $jsig =~ s/^([^.]*).//;
  $class = $1;
  $class =~ s,/,.,g; # / -> .

  # Peel the package name out of the class name (everything before last ".")
  if ($class =~ m/(.*)\.[^\.]*$/) {
    $package = $1;
  }

  ### Second comes the method name (all chars until a left paren)
  $jsig =~ s/^([^\(]*)\(//;
  $method = $1;

  ### Now the arguments
 SIGPARSE:
  while(1) {
    $repct = $jsig =~ s/^(I|J|Z|F|D|B|S|C|L|\[|\))//;  ## No V types
    die "badly formed signature at $jsig" if ($repct == 0);
    $arg = $1;
    
    ## Stop if we hit the end paren
    last SIGPARSE if $arg eq "\)";
    
    if ($arg eq '[') {
      $depth++;
      # continue parsing array type... 
      next SIGPARSE;
    } elsif ($arg eq 'L') {
      $jsig =~ s/^([^;]*);//;
      $arg = $1;
      $arg =~ s,/,.,g;
    } else {
      ## convert single-char identifier to english
      $arg = $sig{$arg};
    }
    
    ## If we hit an array, tack the array depth on the end
    if ($depth > 0) {
      $arg = $arg . "[]" x $depth;
      $depth = 0;
    }

    # Put the arg at the end of the list of args
    push (@args, $arg)
  }

  ### Last is the return type
  $depth = 0;
  $repct = $jsig =~ s/^(I|J|Z|F|D|B|S|C|L|V|\[)//; ## Adds V over argument types
  die "badly formed return type: \'$jsig\'" if ($repct == 0);
  $ret = $1;

  # If its an array, eat the [ and re-set $ret
  if ($ret eq '[') {
    $depth = 1;
    while ($jsig =~ s/\[//) {
      $depth++;
    }
    $jsig =~ s/^(I|J|Z|F|D|B|S|C|L)//; ## No [ or V
    die "badly formed return type: \'$jsig\'" if ($repct == 0);
    $ret = $1;
  } 

  if ($ret eq 'L') {
    $jsig =~ s/^([^;]*);//;
    $ret = $1;
    $ret =~ s,/,.,g;
  } else {
    ## Convert single char identifier to english
    $ret = $sig{"$ret"};
  }

  # Tack the array brackets on
  if ($depth > 0) {
    $ret = $ret . "[]" x $depth;
  }
    
  ### Return the info in an easy-to-use list
  return ($package, $ret, $class, $method, @args);
}

###
### Print functions
###

sub printClass {
    my $r_cl = shift;		
    my %class = %{$r_cl};

    my $flStr = &ACCFlagsToString($class{accessFlags});
    print "$flStr\n";

    &printConstantPool($r_cl);

    ## Print 'this_class'
    my $thisClName = %{$class{constantPool}[$class{thisClass}]}->{nameIndex};
    $thisClName = %{$class{constantPool}[$thisClName]}->{val};
    print "this_class @ $class{thisClass} ($thisClName)\n";

    ## Print 'super_class'
    if ($class{superClass} != 0) {
      my $superClName = %{$class{constantPool}[$class{superClass}]}->{nameIndex};
      $superClName = %{$class{constantPool}[$superClName]}->{val};
      print "super_class @ $class{superClass} ($superClName)\n";
    } else {
      print "No super class\n";
    }

    ## Print direct super interfaces
    &printInterfaces($r_cl);

    ## Print fields
    &printFields($r_cl);

    ## Print methods
    &printMethods($r_cl);

    ## Print attributes
    &printAttributes("", $r_cl, $class{attributes});
}

sub printMethods {
    my $r_cl = shift;
    local(%class) = %{$r_cl};

    if ($class{methodCt} == 0) {
	print "No fields.\n";
    } else {
	$i = 0;
	print "Methods:\n";
	while ($i < $class{methodCt}) {
	    my %method = %{$class{methods}[$i]};
    
	    my $accflags = ACCFlagsToString($method{accessFlags});
	    my $name = $class{constantPool}[$method{nameIndex}]->{val};
	    my $desc = $class{constantPool}[$method{descriptorIndex}]->{val};

	    if ($detailedMethods) {
		print ("\t$i: ");
		print (".accessFlags=$accflags; ");
		print (".name @ $method{nameIndex} ($name); ");
		print (".descriptor @ $method{descriptorIndex} ($desc); ");
		print (".attrCt = $method{attributesCt};\n");
		&printAttributes("\t\t", \%class, $method{attributes});
	    } else {
		print ("\t$accflags $name $desc\n");
	    }
	} continue {
	    $i++;
	}
    }
}

sub printFields {
    my $r_cl = shift;
    local(%class) = %{$r_cl};

    if ($class{fieldCt} == 0) {
	print "No fields.\n";
    } else {
	$i = 0;
	print "Fields:\n";
	while ($i < $class{fieldCt}) {
	    my %field = %{$class{fields}[$i]};
    
	    my $accflags = ACCFlagsToString($field{accessFlags});
	    my $name = $class{constantPool}[$field{nameIndex}]->{val};
	    my $desc = $class{constantPool}[$field{descriptorIndex}]->{val};

	    if ($detailedFields) {
		print ("\t$i: ");
		print (".accessFlags=$accflags; ");
		print (".name @ $field{nameIndex} ($name); ");
		print (".descriptor @ $field{descriptorIndex} ($desc); ");
		print (".attrCt = $field{attributesCt};\n");
		&printAttributes("\t\t", \%class, $field{attributes});
	    } else {
		print ("\t$accflags $desc $name\n");
	    }
	} continue {
	    $i++;
	}
    }
}

sub printInterfaces {
    my $r_cl = shift;
    local(%class) = %{$r_cl};

    if ($class{interfaceCt} == 0) {
	print "No interfaces.\n";
    } else {
	my $i = 0; 
	print "Interfaces:\n";
	while ($i < $class{interfaceCt}) {
	    my $iClass = $class{interfaces}[$i];
	    my %iClassConst = %{$class{constantPool}[$iClass]};
	    my $iClassName = $iClassConst{nameIndex};
	    my $interfaceName = %{$class{constantPool}[$iClassName]}->{val};
	    print "\t$i] @ $iClass ($interfaceName)\n";
	} continue {
	    $i++;
	}
    }
}

sub ACCFlagsToString {
    my $flags = shift;
    my @flags = ();

    push(@flags, "public") if $flags & $ACC_PUBLIC;
    push(@flags, "private") if $flags & $ACC_PRIVATE;
    push(@flags, "protected") if $flags & $ACC_PROTECTED;
    push(@flags, "static") if $flags & $ACC_STATIC;
    push(@flags, "final") if $flags & $ACC_FINAL;
    push(@flags, "super") if $flags & $ACC_SUPER;
    push(@flags, "volatile") if $flags & $ACC_VOLATILE;
    push(@flags, "transient") if $flags & $ACC_TRANSIENT;
    push(@flags, "interface") if $flags & $ACC_INTERFACE;
    push(@flags, "abstract") if $flags & $ACC_ABSTRACT;
    push(@flags, "native") if $flags & $ACC_NATIVE;
    push(@flags, "UNKNOWN") if $flags & $ACC_UNKNOWN;

    return join(',', @flags);
}

sub printConstantPool {
    my $r_cl = shift;
    local(%class) = %{$r_cl};		# cvt the class reference to the class hash

    print("Constant Pool Entries: $class{constantPoolCt}\n");

    $i = 1;
    while ($i < $class{constantPoolCt}) {
	my %cpEntry = %{$class{constantPool}[$i]};

	print "$i] $CONSTANTNames{$cpEntry{tag}}: ";

    	if ($cpEntry{tag} eq $CONSTANT_Class) {
	    my $ni = $cpEntry{nameIndex};

    	    &checkIndex($ni, "Name", $CONSTANT_Utf8);
    
	    my $nm = $class{constantPool}[$ni]->{val};

	    print (".name @ $ni ($nm);");

    	} elsif (($cpEntry{tag} eq $CONSTANT_FieldRef)
		 || ($cpEntry{tag} eq $CONSTANT_MethodRef)
		 || ($cpEntry{tag} eq $CONSTANT_InterfaceMethodRef)) {
    	    &checkIndex($cpEntry{classIndex}, "Class", $CONSTANT_Class);
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type", $CONSTANT_NameAndType);

	    print (".class @ $cpEntry{classIndex}; .name&type @ $cpEntry{nameTypeIndex};");
    	} elsif ($cpEntry{tag} eq $CONSTANT_String) {
	    my $si = $cpEntry{stringIndex};
    	    &checkIndex($si, "String", $CONSTANT_Utf8);
	    
	    my $str = $class{constantPool}[$si]->{val};

	    print (".string @ $cpEntry{stringIndex} ($str);");
    	} elsif ($cpEntry{tag} eq $CONSTANT_NameAndType) {
	    my $ni = $cpEntry{nameIndex};
	    my $di = $cpEntry{descriptorIndex};

    	    &checkIndex($ni, "Name", $CONSTANT_Utf8);
    	    &checkIndex($di, "Descriptor", $CONSTANT_Utf8);

	    my $nstr = $class{constantPool}[$ni]->{val};
	    my $dstr = $class{constantPool}[$di]->{val};

	    print (".name @ $ni ($nstr); .descriptor @ $di ($dstr); ");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Integer) {
	    print (".value = $cpEntry{val}");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Utf8) {
	    print (".length=$cpEntry{len}; ");
	    print (".val=$cpEntry{val}; ");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Float) {
	    print (".val=$cpEntry{strVal}; ");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Double) {
	    print (".val=$cpEntry{strVal}; ");
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} elsif ($cpEntry{tag} eq $CONSTANT_Long) {
	    print (".val=$cpEntry{strVal}; ");
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} else {
    	    &fatal("Unknown Constant type $cpEntry{tag}!\n");
    	}

	print ("\n");
	
	$i++;
    }
}

sub printAttributes {
    my ($prefix, $r_class, $r_attrs) = @_;

    return if (!defined($r_attrs));

    my $i = 0;
    my %class = %{$r_class};
    
    print ("${prefix}Attributes:\n");
    foreach $r_attr (@{$r_attrs}) {

	my $name = $class{constantPool}[$r_attr->{nameIndex}]->{val};
	print ("${prefix}\t.name=$name; ");
	print (".length=" . $r_attr->{len} . ";");

	if ($name eq 'SourceFile') {
	  if ($r_attr->{len} != 2) {
	    print ("!Badly formed SourceFile Attribute, must be 2!");
	  } else {
	    my ($high, $low) = unpack("CC", $r_attr->{attr});
	    my $idx = ($high * 256) + $low;
	    my $name = $class{constantPool}[$idx]->{val};
	    print (" @ " . $idx . " (\"" . $name . "\")");
	  }
	}

	print ("\n");
    }
}

###
### Read Class function
###

sub readClass {
    my $classFile = shift;
    local(%class) = (());

    open(CLASSIN, $classFile) 
	|| open(CLASSIN, "${classFile}.class")
	    || die ("Cannot open $classFile for reading");
    
    ###
    ### Header Magic
    ###
    	
    $class{magic} = read_u4();
    if ($class{magic} != $classMagic) {
    	fatal("Bad class magic '$class{magic}' --expected '$classMagic'.  $classFile is probably not a Java class file.");
    }
    
    ## Read in the major and minor version numbers
    $class{minorVersion} = &read_u2();
    $class{majorVersion} = &read_u2();
    
    print("Version: $class{majorVersion}.$class{minorVersion}  (expected 45.3)\n") 
    	if ($class{minorVersion} ne 3) || ($class{majorVersion} ne 45);
    
    ###
    ### Constant Pool
    ###
    $class{constantPoolCt} = &read_u2();
    $class{constantPool} = [];
    
    $i = 1; # constant pool actually starts with entry 1...
    while ($i < $class{constantPoolCt}) {
    	my %cpEntry;
    	$cpEntry{tag} = &read_u1();
    
    	if ($cpEntry{tag} eq $CONSTANT_Class) {
    	    $cpEntry{nameIndex} = &read_u2();
    
    	    &checkIndex($cpEntry{nameIndex}, "Name");
    	} elsif ($cpEntry{tag} eq $CONSTANT_FieldRef) {
    	    $cpEntry{classIndex} = &read_u2();
    	    $cpEntry{nameTypeIndex} = &read_u2();
    	    
    	    &checkIndex($cpEntry{classIndex}, "Class");
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type");
    	} elsif ($cpEntry{tag} eq $CONSTANT_MethodRef) {
    	    $cpEntry{classIndex} = &read_u2();
    	    $cpEntry{nameTypeIndex} = &read_u2();
    
    	    &checkIndex($cpEntry{classIndex}, "Class");
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type");
    	} elsif ($cpEntry{tag} eq $CONSTANT_InterfaceMethodRef) {
    	    $cpEntry{classIndex} = &read_u2();
    	    $cpEntry{nameTypeIndex} = &read_u2();
    
    	    &checkIndex($cpEntry{classIndex}, "Class");
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type");
    	} elsif ($cpEntry{tag} eq $CONSTANT_String) {
    	    $cpEntry{stringIndex} = &read_u2();
    
    	    &checkIndex($cpEntry{stringIndex}, "String");
    	} elsif ($cpEntry{tag} eq $CONSTANT_NameAndType) {
    	    $cpEntry{nameIndex} = &read_u2();
    	    $cpEntry{descriptorIndex} = &read_u2();
    
    	    &checkIndex($cpEntry{nameIndex}, "Name");
    	    &checkIndex($cpEntry{descriptorIndex}, "Descriptor");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Integer) {
    	    $cpEntry{val} = &read_u4();
    	} elsif ($cpEntry{tag} eq $CONSTANT_Utf8) {
    	    $cpEntry{len} = &read_u2();
    	    $cpEntry{val} = &read_utf8($cpEntry{len});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Float) {
	    $cpEntry{val} = &read_u4();
	    $cpEntry{strVal} = &read_float($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Double) {
	    $cpEntry{val} = &read_u8();
	    $cpEntry{strVal} = &read_double($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Long) {
	    $cpEntry{val} = &read_u8();
	    $cpEntry{strVal} = "<Unknown>";
    	} else {
    	    &fatal("Unknown Constant type $cpEntry{tag}!\n");
    	}
    
    	$class{constantPool}[$i] = \%cpEntry;

	## Ick.  8-byte entries take two constant pool entries
	$i++ if (($cpEntry{tag} == $CONSTANT_Long)
		 || ($cpEntry{tag} == $CONSTANT_Double));
    } continue {
    	$i++;
    }
    
    ###
    ### Misc. Class Info
    ###
    
    $class{accessFlags} = &read_u2();
    
    $class{thisClass} = &read_u2();
    &checkIndex($class{thisClass}, "this_class", $CONSTANT_Class);
    
    $class{superClass} = &read_u2();
    if ($class{superClass} != 0) {
      &checkIndex($class{superClass}, "super_class", $CONSTANT_Class);
    } else {
      print ("Warning: class has no super class.  Must be java.lang.Object\n"); 
    }
    
    ###
    ### Direct super-interfaces
    ###
    $class{interfaceCt} = &read_u2();
    $class{interfaces} = [];
    
    $i = 0;
    while ($i < $class{interfaceCt}) {
    	$class{interfaces}[$i] = &read_u2();
    
    	&checkIndex($class{interfaces}[$i], "Interface \#$i", $CONSTANT_Class);	
    } continue {
    	$i++;
    }
    
    ###
    ### Fields
    ###
    $class{fieldCt} = &read_u2();
    $class{fields} = [];
    
    $i = 0;
    while ($i < $class{fieldCt}) {
    	my %field;
    	$field{accessFlags} = &read_u2();
    	$field{nameIndex} = &read_u2();
    	$field{descriptorIndex} = &read_u2();
    	$field{attributesCt} = &read_u2();
    	$field{attributes} = &readAttributes($field{attributesCt});
    
    	&checkIndex($field{nameIndex}, "Field Name", $CONSTANT_Utf8);
    	&checkIndex($field{descriptorIndex}, "Field Descriptor", $CONSTANT_Utf8);
    
    	$class{fields}[$i] = \%field;
    } continue {
    	$i++;
    }
    
    ###
    ### Methods
    ###
    $class{methodCt} = &read_u2();
    $class{methods} = [];
    
    $i = 0;
    while ($i < $class{methodCt}) {
    	my %method;
    
    	$method{accessFlags} = &read_u2();
    	$method{nameIndex} = &read_u2();
    	$method{descriptorIndex} = &read_u2();
    	$method{attributesCt} = &read_u2();
    	$method{attributes} = &readAttributes($method{attributesCt});
    
    	&checkIndex($method{nameIndex}, "Method Name", $CONSTANT_Utf8);
    	&checkIndex($method{descriptorIndex}, "Method Descriptor", $CONSTANT_Utf8);
    
    	$class{methods}[$i] = \%method;
    } continue {
    	$i++;
    }
    
    ###
    ### Class attributes
    ###
    $class{attributesCt} = &read_u2();
    $class{attributes} = &readAttributes($class{attributesCt});

    ###
    ### End of .class file
    ###

    return \%class;
}

###
### Write Class function
###

sub writeClass {
    my $r_class = shift;
    my $classFile = shift;
    local(%class) = %{$r_class};

    if ($classFile =~ /\.class$/) {
	open(CLASSOUT, ">$classFile") 
	    || die ("Cannot open $classFile for writing");
    } else {
	open(CLASSOUT, ">$classFile.class") 
	    || die ("Cannot open $classFile.class for writing");
    }

    ###
    ### Header Magic
    ###

    if ($class{magic} != $classMagic) {
    	fatal("Bad class magic '$class{magic}' --expected '$classMagic'.  Not writing class file.");
    }
    	
    &write_u4($class{magic});
    
    ## Write major/minor version numbers
    &write_u2($class{minorVersion});
    &write_u2($class{majorVersion});
    
    ###
    ### Constant Pool
    ###
    &write_u2($class{constantPoolCt});

    $i = 1; # constant pool actually starts with entry 1...
    while ($i < $class{constantPoolCt}) {
    	my %cpEntry = %{$class{constantPool}[$i]};
    	&write_u1($cpEntry{tag});
    
    	if ($cpEntry{tag} eq $CONSTANT_Class) {
    	    &checkIndex($cpEntry{nameIndex}, "Name", $CONSTANT_Utf8);

    	    &write_u2($cpEntry{nameIndex});
    	} elsif (($cpEntry{tag} eq $CONSTANT_FieldRef)
		 || ($cpEntry{tag} eq $CONSTANT_MethodRef)
		 || ($cpEntry{tag} eq $CONSTANT_InterfaceMethodRef)) {
    	    &checkIndex($cpEntry{classIndex}, "Class", $CONSTANT_Class);
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type", $CONSTANT_NameAndType);

    	    &write_u2($cpEntry{classIndex});
    	    &write_u2($cpEntry{nameTypeIndex});
    	} elsif ($cpEntry{tag} eq $CONSTANT_String) {
    	    &checkIndex($cpEntry{stringIndex}, "String", $CONSTANT_Utf8);

    	    &write_u2($cpEntry{stringIndex});
    	} elsif ($cpEntry{tag} eq $CONSTANT_NameAndType) {
    	    &checkIndex($cpEntry{nameIndex}, "Name", $CONSTANT_Utf8);
    	    &checkIndex($cpEntry{descriptorIndex}, "Descriptor", $CONSTANT_Utf8);

    	    &write_u2($cpEntry{nameIndex});
    	    &write_u2($cpEntry{descriptorIndex});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Integer) {
    	    &write_u4($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Utf8) {
    	    &write_u2($cpEntry{len});
    	    &write_utf8($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Float) {
	    &write_u4($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Double) {
	    &write_u8($cpEntry{val});
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} elsif ($cpEntry{tag} eq $CONSTANT_Long) {
	    &write_u8($cpEntry{val});
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} else {
    	    &fatal("Unknown Constant type $cpEntry{tag}!\n");
    	}
    } continue {
    	$i++;
    }
    
    ###
    ### Misc. Class Info
    ###
    
    &write_u2($class{accessFlags});
    &write_u2($class{thisClass});
    &write_u2($class{superClass});
    
    ###
    ### Direct super-interfaces
    ###
    &write_u2($class{interfaceCt});
    
    $i = 0;
    while ($i < $class{interfaceCt}) {
    	&write_u2($class{interfaces}[$i]);
    } continue {
    	$i++;
    }
    
    ###
    ### Fields
    ###
    &write_u2($class{fieldCt});
    
    $i = 0;
    while ($i < $class{fieldCt}) {
    	my %field = %{$class{fields}[$i]};
    	&write_u2($field{accessFlags});
    	&write_u2($field{nameIndex});
    	&write_u2($field{descriptorIndex});
    	&write_u2($field{attributesCt});
    	&writeAttributes($field{attributes});
    } continue {
    	$i++;
    }
    
    ###
    ### Methods
    ###
    &write_u2($class{methodCt});
    
    $i = 0;
    while ($i < $class{methodCt}) {
    	my %method = %{$class{methods}[$i]};
    
    	&write_u2($method{accessFlags});
    	&write_u2($method{nameIndex});
	&write_u2($method{descriptorIndex});
    	&write_u2($method{attributesCt});
    	&writeAttributes($method{attributes});
    } continue {
    	$i++;
    }
    
    ###
    ### Class attributes
    ###
    &write_u2($class{attributesCt});
    &writeAttributes($class{attributes});

    ###
    ### End of .class file
    ###

    return \%class;
}

###
### Integrity check functions
###

sub checkIndex {
    my ($val, $name, $type) = @_;

    # $class is a global 

    if ($val == 0) {
      &fatal("ERROR: Found constant pool index 0 for $name.  (Expecting a CONSTANT_$CONSTANTNames{$type} entry.)");
    }
     
    if ($val >= $class{constantPoolCt}) {
	&fatal("ERROR: $name index for current constant is $val, must be less than $class{constantPoolCt}\n");
    } 

    if (defined($type)) {
	my $actualTag = $class{constantPool}[$val]{tag};
	if ($actualTag != $type) {
	    &fatal("ERROR: $name expects a CONSTANT_$CONSTANTNames{$type} entry at $val, but found a CONSTANT_$CONSTANTNames{$actualTag} entry\n");
	}
    }
}

###
### Read primitives
###

sub read_u8 {
    my $long = 0;
    (read(CLASSIN, $long, 8) == 8) || die ("premature eof in read_u8()\n");
    my ($b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8) = unpack("CCCCCCCC", $long);
    return (($b1 << 56) + ($b2 << 48) + ($b3 << 40) + ($b4 << 32)
	    + ($b5 << 24) + ($b6 << 16) + ($b7 << 8) + $b4);
}

sub read_u4 {
    my $long = 0;
    (read(CLASSIN, $long, 4) == 4) || die ("premature eof in read_u4()\n");
    my ($top, $highmid, $lowmid, $low) = unpack("CCCC", $long);
    return ($top * (256*256*256)) + ($highmid * (256*256)) + ($lowmid * 256) + $low;
}

sub read_u2 {
    my $short = 0;
    (read(CLASSIN, $short, 2) == 2) || die ("premature eof in read_u2()\n");
    my ($high, $low) = unpack("CC", $short);
    #print("read_u2: $high, $low\n");
    return ($high * 256) + $low;
}

sub read_u1 {
    my $byte = 0;
    (read(CLASSIN, $byte, 1) == 1) || die ("premature eof in read_u1()\n");
    my $val = unpack("C", $byte);
    return $val;
}

sub read_n {
    my $byteCt = shift;
    my $foo = '';
    (read(CLASSIN, $foo, $byteCt) == $byteCt) || die ("premature eof in read_n($byteCt)\n");

    return $foo;
}

sub read_float {
    my $intVal = shift;
    
    return "+INF" if ($intVal == 0x7f800000);
    return "-INF" if ($intVal == 0xff800000);
    if ((($intVal >= 0x7f800001) && ($intVal <= 0x7fffffff))
	|| (($intVal >= 0xff800001) && ($intVal <= 0xffffffff))) {
	return "NaN";
    }

    ## Otherwise, convert to a floating point number
    $sign     = (($intVal >> 31) == 0) ?  1 : -1;
    $exponent = (($intVal >> 23) & 0xFF);
    $mantissa = ($exponent == 0) ? ($intVal & 0x7fffff) << 1 : ($intVal & 0x7fffff) | 0x800000;

    return $sign * $mantissa * 2 ** ($exponent - 150);
}

sub read_double {
    my $intVal = shift;

    return "+INF" if ($intVal == 0x7f800000);
    return "-INF" if ($intVal == 0xff800000);
    if ((($intVal >= 0x7f800001) && ($intVal <= 0x7fffffff))
	|| (($intVal >= 0xff800001) && ($intVal <= 0xffffffff))) {
	return "NaN";
    }

    ## Otherwise, convert to a floating point number
    $sign     = (($intVal >> 31) == 0) ?  1 : -1;
    $exponent = (($intVal >> 23) & 0xFF);
    $mantissa = ($exponent == 0) ? ($intVal & 0x7fffff) << 1 : ($intVal & 0x7fffff) | 0x800000;

    return $sign * $mantissa * 2 ** ($exponent - 150);
}

sub read_utf8 {
    my $byteCt = shift;
    my $utf = '';
    (read(CLASSIN, $utf, $byteCt) == $byteCt) || die ("premature eof in read_utf8($byteCt)\n");

    my $str = unpack("A$byteCt", $utf);
    return $str;
}

sub readAttributes {
    my ($ct) = @_;
    
    my @attrs = [];

    return undef if ($ct < 1);

    my $i = 0;
    while ($i < $ct) {
	my %attribute;
        $attribute{nameIndex} = &read_u2();
	$attribute{len} = &read_u4();
	$attribute{attr} = &read_n($attribute{len});
	
	&checkIndex($attribute{nameIndex}, "Attribute Name", $CONSTANT_Utf8);

	$attrs[$i] = \%attribute;
    } continue {
	$i++;
    }

    return \@attrs;
}

###
### Write primitives
###

sub write_u4 {
    my $val = shift;
    print CLASSOUT pack("N", $val);
}

sub write_u2 {
    my $val = shift;
    print CLASSOUT pack("n", $val);
}

sub write_u1 {
    my $byte = shift;
    print CLASSOUT pack("C", $byte);
}

sub write_n {
    my $val = shift; 
    print CLASSOUT $val;	# XXX assumes $val."length" is 'n'
}

sub write_utf8 {
    my $utf8 = shift;
    print CLASSOUT $utf8
}

sub writeAttributes {
    my $r_attrs = shift;

    return if (!defined $r_attrs);

    my $i = 0;
    foreach $r_attr (@{$r_attrs}) {
	my %attribute = %{$r_attr};
	&write_u2($attribute{nameIndex});
	&write_u4($attribute{len});
	&write_n($attribute{attr});
    }
}

sub fatal {
    print STDERR @_;
    print STDERR "\n";
    exit 11;
}

1;

# eof
kaffe-1.0b4/developers/README   644    624    310        1277  6700442501  10760 
This directory contains various files useful for Kaffe developers.

sp_offset.c:	a program that guesses the correct offset of the sp in a
		jmpbuf for the jthread threading system.

gdbinit:	a gdb macro file to help in debugging kaffe

JavaClass.pm:	Perl 5 module for .class file manipulation.
		Used by dumpClass.pl and utf8munge.pl

dumpClass.pl:	dump the content of a .class file

utf8munge.pl:	change the values of utf8 constants of a .class file
		Note that both .pl scripts require /usr/local/bin/perl 
		(or change the first line in the script)

autogen.sh:	script to run all of the various auto* programs
		in the correct order

unicode.pl:	Perl script to generate unicode.idx and unicode.tbl

kaffe-1.0b4/developers/README.unicode   644    624    310       12776  6700347460  12443 From kaffe-core@rufus.w3.org Mon Mar 29 05:07:50 1999
Date: Mon, 29 Mar 1999 15:02:53 +0200
From: Edouard Parmelan <Edouard.Parmelan@quadratec.fr>
To: Kaffe Core Team <kaffe-core@rufus.w3.org>
Subject: Re: Kaffe Unicode Database

Hi,

I have finish it :)

Following, a tar ball with the new java.lang.Character, a FAQ.unicode that
explain the database, the database generator unicode.pl and the generated
database unicode.idx and unicode.tbl.

The inner class CharacterPropetries load the database with the native
method ClassLoader.getSystemResourceAsBytes0() in the same package.

The database files should be places in Klasses.jar under kaffe/lang/.

I don't change any Makefile.am to incorporate them :(


As promiss, I run Mauve for java.lang.Character:

Mauve results for java.lang.Character: 156 of 3579141 tests failed

Let's me explain the 156 failed test:


getNumericValue test:

* FAIL: gnu.testlet.java.lang.Character.getNumericValue (number 6)
  It's getNumericValue('A'), see getNumericValue() in unicode test.


unicode test:

getNumericValue()

* 52 characters ``wrong numeric value''
  Mauve:
    It is not stated that A-Z and a-z should
    have getNumericValue() (as it is in digit())

  JDK1.2
    public static int getNumericValue(char ch)

    Returns the Unicode numeric value of the character as a nonnegative
    integer. If the character does not have a numeric value, then -1 is
    returned. If the character has a numeric value that cannot be
    represented as a nonnegative integer (for example, a fractional value),
    then -2 is returned.

  Kaffe
    as JDK1.2 says :)


isLowerCase()

* 68 characters ``incorectly reported as lowercase''
* 1 character ``incorectly reported as not-lowercase''
  Mauve
    // NOTE: JLS doesn't say anything about `Ll'
    // category.  And Unicode 2.1.8 has some
    // characters which might be considered
    // lowercase by all the other rules, but which
    // are not marked Ll -- e.g., 0x0345.  So we

  JDK1.2
    public static boolean isLowerCase(char ch)

    Determines if the specified character is a lowercase character.
    A character is lowercase if it is not in the range '\u2000'
    through '\u2FFF', the Unicode attribute table does not specify
    a mapping to lowercase for the character, and at least one of
    the following is true:

    + The attribute table specifies a mapping to uppercase for the character.
    + The name for the character contains the words "SMALL LETTER".
    + The name for the character contains the words "SMALL LIGATURE".

    A character is considered to be lowercase if and only if it is
    specified to be lowercase by the Unicode 2.0 standard (category
    "Ll" in the Unicode specification data file).

  Kaffe
    use the category "Ll".


isUpperCase()

* 31 characters ``incorectly reported as uppercase''
  Mauve
    // NOTE: JLS doesn't say anything about `Lu'
    // category.  And Unicode 2.1.8 has some
    // characters which might be considered
    // uppercase by all the other rules, but which
    // are not marked Lu -- e.g., 0x03d2.  So we
    // don't check for this.

  JDK1.2
    public static boolean isUpperCase(char ch)

    Determines if the specified character is an uppercase character.
    A character is uppercase if it is not in the range '\u2000'
    through '\u2FFF', the Unicode attribute table does not specify
    a mapping to uppercase for the character, and at least one of
    the following is true:

    + The attribute table specifies a mapping to lowercase for the character.
    + The name for the character contains the words "CAPITAL LETTER".
    + The name for the character contains the words "CAPITAL LIGATURE".

  [ says nothing about category "Lu" in Unicode 2.0 :( ]
  
  Kaffe
    use category "Lu".


isWhitespace()

* 1 character ``incorectly reported as not-whitespace''
  JDK1.2 says:
    + It is a Unicode space separator (category "Zs"), but is not a
      no-break space (\u00A0 or \uFEFF).

  In Unicode 2.1.8:
     U+00A0 Zs, noBreak
     U+2007 Zs, noBreak
  but
     U+FEFF Cf (Other, Format)

  Kaffe
    use category "Zs" and decomposition <noBreak> from Unicode 2.1.8


toTitleCase()

* 2 characters ``has wrong titlecase form''
  Mauve
    use the uppercase if the caracter don't have titlecase in
    the Unicode database.

  JDK1.2
    Converts the character argument to titlecase. A character has a
    titlecase equivalent if and only if a titlecase mapping is
    specified for the character in the Unicode attribute table.

    Note that some Unicode characters in the range '\u2000' through
    '\u2FFF' have titlecase mappings; this method does map such
    characters to their titlecase equivalents even though the
    method isTitleCase does not return true for such characters.

    There are only four Unicode characters that are truly titlecase
    forms that are distinct from uppercase forms. As a rule, if a
    character has no true titlecase equivalent but does have an
    uppercase mapping, then the Unicode 2.0 attribute table
    specifies a titlecase mapping that is the same as the uppercase
    mapping.

 Unicode 2.1.8
    U+0345 and U+1EFB don't have titlecase mapping.

 Kaffe
    return U+0000 for toTitleCase(U+0345) and toTitleCase(U+1EFB)


TODO:
Run mauve java.lang.Character with Sun JDK, as I don't have it any more,
I can't run it :(

Comments are wellcome,
Edouard
-- 
Edouard G. Parmelan                         Ingenieur Developpeur
Quadratec - Parc Club "Orsay Universite" - 14/16,rue Jean Rostand
91893 Orsay Cedex - FRANCE               Phone (+33)1 69 33 20 80
Email: edouard.parmelan@quadratec.fr
kaffe-1.0b4/developers/autogen.sh   644    624    310         275  6672266257  12077 #! /bin/sh

# This script runs all of the various auto* programs in the correct order.
# Written by Mo DeJong.

aclocal -I .
autoheader -l config
automake --add-missing --verbose
autoconf

kaffe-1.0b4/developers/dumpClass.pl   755    624    310        1666  6660670355  12414 #!/usr/local/bin/perl -w
#
# Dump a java .class file to stdout
#

#
# Copyright (c) 1999 University of Utah CSL.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# Written by Patrick Tullmann <tullmann@cs.utah.edu>
#

use JavaClass;

# Control the verbosity of &printClass()
$JavaClass::detailedFields = 0;
$JavaClass::detailedMethods = 0;

## Parse the command line
my $classFile = shift  || &usage();

## Read/parse the class file
my $class = &JavaClass::readClass($classFile);

## Print the class filea
&JavaClass::printClass($class);

###
###
###

sub usage() {
  print STDOUT "Usage:\n";
  print STDOUT "    dumpClass.pl