Initial checkin from Perforce.
authorScott Wood <scott@odin.buserror.net>
Sun, 3 Dec 2006 18:19:46 +0000 (12:19 -0600)
committerScott Wood <scott@odin.buserror.net>
Sun, 3 Dec 2006 18:19:46 +0000 (12:19 -0600)
239 files changed:
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.head [new file with mode: 0644]
Makefile.tail [new file with mode: 0644]
Makefile.target [new file with mode: 0644]
doc/abi/alpha [new file with mode: 0644]
doc/abi/x86 [new file with mode: 0644]
doc/attributions [new file with mode: 0644]
doc/copyright-header [new file with mode: 0644]
doc/idl/corba-differences [new file with mode: 0644]
doc/idl/language-bindings/c++/specification [new file with mode: 0644]
doc/idl/namespace-precedence [new file with mode: 0644]
doc/orb/ids [new file with mode: 0644]
doc/orb/memory-management [new file with mode: 0644]
doc/orb/parameter-info-block [new file with mode: 0644]
idl/Makefile [new file with mode: 0644]
idl/addrspace.idl [new file with mode: 0644]
idl/events.idl [new file with mode: 0644]
idl/exceptions.idl [new file with mode: 0644]
idl/io.idl [new file with mode: 0644]
idl/io/bus/generic.idl [new file with mode: 0644]
idl/io/bus/pci.idl [new file with mode: 0644]
idl/io/console.idl [new file with mode: 0644]
idl/io/interrupts.idl [new file with mode: 0644]
idl/namespace.idl [new file with mode: 0644]
idl/notifier.idl [new file with mode: 0644]
idl/objmgr.idl [new file with mode: 0644]
idl/proc.idl [new file with mode: 0644]
idl/thread.idl [new file with mode: 0644]
idl/time.idl [new file with mode: 0644]
idl/traps.idl [new file with mode: 0644]
idlcomp/BUGS [new file with mode: 0644]
idlcomp/DONE-TODO [new file with mode: 0644]
idlcomp/FIXED-BUGS [new file with mode: 0644]
idlcomp/Makefile [new file with mode: 0644]
idlcomp/THOUGHTS [new file with mode: 0644]
idlcomp/TODO [new file with mode: 0644]
idlcomp/cdl.h [new file with mode: 0644]
idlcomp/cdlparse.y [new file with mode: 0644]
idlcomp/compileddef.h [new file with mode: 0644]
idlcomp/idlc.h [new file with mode: 0644]
idlcomp/idlparse.y [new file with mode: 0644]
idlcomp/input.cc [new file with mode: 0644]
idlcomp/lang.h [new file with mode: 0644]
idlcomp/languages/c++/Makefile [new file with mode: 0644]
idlcomp/languages/c++/c++.h [new file with mode: 0644]
idlcomp/languages/c++/interface-caller.cc [new file with mode: 0644]
idlcomp/languages/c++/main.cc [new file with mode: 0644]
idlcomp/languages/c++/server.cc [new file with mode: 0644]
idlcomp/languages/c/Makefile [new file with mode: 0644]
idlcomp/languages/c/main.cc [new file with mode: 0644]
idlcomp/main.cc [new file with mode: 0644]
idlcomp/namespace.cc [new file with mode: 0644]
idlcomp/output.cc [new file with mode: 0644]
idlcomp/parser.h [new file with mode: 0644]
idlcomp/scan.lex [new file with mode: 0644]
idlcomp/targets.cc [new file with mode: 0644]
idlcomp/targets.h [new file with mode: 0644]
idlcomp/tests/Makefile [new file with mode: 0644]
idlcomp/tests/bug.idl [new file with mode: 0644]
idlcomp/tests/bug2.idl [new file with mode: 0644]
idlcomp/tests/bug3.idl [new file with mode: 0644]
idlcomp/tests/cpp-caller.cc [new file with mode: 0644]
idlcomp/tests/cpp-server.cc [new file with mode: 0644]
idlcomp/tests/cpp-server.cdl [new file with mode: 0644]
idlcomp/tests/cpp-vstruct.cc [new file with mode: 0644]
idlcomp/tests/foo.idl [new file with mode: 0644]
idlcomp/tests/foo2.idl [new file with mode: 0644]
idlcomp/tests/namespace-precedence.idl [new file with mode: 0644]
idlcomp/types.cc [new file with mode: 0644]
idlcomp/util.cc [new file with mode: 0644]
idlcomp/util.h [new file with mode: 0644]
idlcomp/vtable-sample.h [new file with mode: 0644]
include/Makefile [new file with mode: 0644]
include/c++/kernel/kernel.h [new file with mode: 0644]
include/c++/kernel/region.h [new file with mode: 0644]
include/c++/kernel/time.h [new file with mode: 0644]
include/c++/orb.h [new file with mode: 0644]
include/c++/util/assert.h [new file with mode: 0644]
include/c++/util/heap.h [new file with mode: 0644]
include/c++/util/list.h [new file with mode: 0644]
include/c++/util/lock.h [new file with mode: 0644]
include/c++/util/misc.h [new file with mode: 0644]
include/c++/util/rbtree.h [new file with mode: 0644]
include/c++/util/spinlock.h [new file with mode: 0644]
include/c++/util/tests/Makefile [new file with mode: 0644]
include/c++/util/tests/heap.cc [new file with mode: 0644]
include/c++/util/tests/rbtree.cc [new file with mode: 0644]
include/c/lowlevel/README [new file with mode: 0644]
include/c/lowlevel/arch-ppc/io.h [new file with mode: 0644]
include/c/lowlevel/arch-x64/atomic.h [new file with mode: 0644]
include/c/lowlevel/arch-x64/barriers.h [new file with mode: 0644]
include/c/lowlevel/arch-x64/bitops.h [new file with mode: 0644]
include/c/lowlevel/arch-x64/clock.h [new file with mode: 0644]
include/c/lowlevel/arch-x64/io.h [new file with mode: 0644]
include/c/lowlevel/arch-x64/misc.h [new file with mode: 0644]
include/c/lowlevel/arch-x64/types.h [new file with mode: 0644]
include/c/lowlevel/arch-x86-common/atomic.h [new file with mode: 0644]
include/c/lowlevel/arch-x86-common/barriers-fence.h [new file with mode: 0644]
include/c/lowlevel/arch-x86-common/clock.h [new file with mode: 0644]
include/c/lowlevel/arch-x86-common/misc.h [new file with mode: 0644]
include/c/lowlevel/arch-x86/atomic.h [new file with mode: 0644]
include/c/lowlevel/arch-x86/barriers.h [new file with mode: 0644]
include/c/lowlevel/arch-x86/bitops.h [new file with mode: 0644]
include/c/lowlevel/arch-x86/clock.h [new file with mode: 0644]
include/c/lowlevel/arch-x86/io.h [new file with mode: 0644]
include/c/lowlevel/arch-x86/misc.h [new file with mode: 0644]
include/c/lowlevel/arch-x86/types.h [new file with mode: 0644]
include/c/lowlevel/arch.h [new file with mode: 0644]
include/c/lowlevel/atomic.h [new file with mode: 0644]
include/c/lowlevel/barriers.h [new file with mode: 0644]
include/c/lowlevel/bitops.h [new file with mode: 0644]
include/c/lowlevel/clock.h [new file with mode: 0644]
include/c/lowlevel/io.h [new file with mode: 0644]
include/c/lowlevel/misc.h [new file with mode: 0644]
include/c/lowlevel/types.h [new file with mode: 0644]
include/c/std/stdint.h [new file with mode: 0644]
include/c/std/string.h [new file with mode: 0644]
kernel/Makefile [new file with mode: 0644]
kernel/arch/x64/Makefile [new file with mode: 0644]
kernel/arch/x64/Makefile.final [new file with mode: 0644]
kernel/arch/x64/descriptors.cc [new file with mode: 0644]
kernel/arch/x64/entry.S [new file with mode: 0644]
kernel/arch/x64/linker-script [new file with mode: 0644]
kernel/arch/x64/mem.cc [new file with mode: 0644]
kernel/arch/x64/misc.cc [new file with mode: 0644]
kernel/arch/x64/multiboot.cc [new file with mode: 0644]
kernel/arch/x64/thread.cc [new file with mode: 0644]
kernel/arch/x86-common/Makefile [new file with mode: 0644]
kernel/arch/x86-common/local-apic.cc [new file with mode: 0644]
kernel/arch/x86/Makefile [new file with mode: 0644]
kernel/arch/x86/Makefile.final [new file with mode: 0644]
kernel/arch/x86/descriptors.cc [new file with mode: 0644]
kernel/arch/x86/entry.S [new file with mode: 0644]
kernel/arch/x86/linker-script [new file with mode: 0644]
kernel/arch/x86/mem.cc [new file with mode: 0644]
kernel/arch/x86/misc.cc [new file with mode: 0644]
kernel/arch/x86/multiboot.cc [new file with mode: 0644]
kernel/arch/x86/thread.cc [new file with mode: 0644]
kernel/core/Makefile [new file with mode: 0644]
kernel/core/event.cc [new file with mode: 0644]
kernel/core/event.cdl [new file with mode: 0644]
kernel/core/init.cc [new file with mode: 0644]
kernel/core/irq.cc [new file with mode: 0644]
kernel/core/irq.cdl [new file with mode: 0644]
kernel/core/lock.cc [new file with mode: 0644]
kernel/core/misc.cc [new file with mode: 0644]
kernel/core/thread.cc [new file with mode: 0644]
kernel/core/time.cc [new file with mode: 0644]
kernel/core/time.cdl [new file with mode: 0644]
kernel/idl/README [new file with mode: 0644]
kernel/include/arch-x64/addrs.h [new file with mode: 0644]
kernel/include/arch-x64/conf.h [new file with mode: 0644]
kernel/include/arch-x64/current.h [new file with mode: 0644]
kernel/include/arch-x64/mem.h [new file with mode: 0644]
kernel/include/arch-x64/multiboot.h [new file with mode: 0644]
kernel/include/arch-x64/pagetable.h [new file with mode: 0644]
kernel/include/arch-x64/paging.h [new file with mode: 0644]
kernel/include/arch-x64/thread.h [new file with mode: 0644]
kernel/include/arch-x86-common/multiboot.h [new file with mode: 0644]
kernel/include/arch-x86/addrs.h [new file with mode: 0644]
kernel/include/arch-x86/conf.h [new file with mode: 0644]
kernel/include/arch-x86/current.h [new file with mode: 0644]
kernel/include/arch-x86/mem.h [new file with mode: 0644]
kernel/include/arch-x86/multiboot.h [new file with mode: 0644]
kernel/include/arch-x86/pagetable.h [new file with mode: 0644]
kernel/include/arch-x86/paging.h [new file with mode: 0644]
kernel/include/arch-x86/thread.h [new file with mode: 0644]
kernel/include/arch-x86/usercopy.h [new file with mode: 0644]
kernel/include/assert.h [new file with mode: 0644]
kernel/include/kern/addrs.h [new file with mode: 0644]
kernel/include/kern/arch.h [new file with mode: 0644]
kernel/include/kern/assert.h [new file with mode: 0644]
kernel/include/kern/bitops.h [new file with mode: 0644]
kernel/include/kern/compiler.h [new file with mode: 0644]
kernel/include/kern/conf.h [new file with mode: 0644]
kernel/include/kern/console.h [new file with mode: 0644]
kernel/include/kern/event.h [new file with mode: 0644]
kernel/include/kern/generic-pte.h [new file with mode: 0644]
kernel/include/kern/i8259.h [new file with mode: 0644]
kernel/include/kern/io.h [new file with mode: 0644]
kernel/include/kern/irq.h [new file with mode: 0644]
kernel/include/kern/kernel.h [new file with mode: 0644]
kernel/include/kern/libc.h [new file with mode: 0644]
kernel/include/kern/list.h [new file with mode: 0644]
kernel/include/kern/lock.h [new file with mode: 0644]
kernel/include/kern/mem.h [new file with mode: 0644]
kernel/include/kern/notifier.h [new file with mode: 0644]
kernel/include/kern/orb.h [new file with mode: 0644]
kernel/include/kern/pagealloc.h [new file with mode: 0644]
kernel/include/kern/pagetable.h [new file with mode: 0644]
kernel/include/kern/paging.h [new file with mode: 0644]
kernel/include/kern/spinlock.h [new file with mode: 0644]
kernel/include/kern/thread.h [new file with mode: 0644]
kernel/include/kern/time.h [new file with mode: 0644]
kernel/include/kern/types.h [new file with mode: 0644]
kernel/include/stdint.h [new file with mode: 0644]
kernel/include/string.h [new file with mode: 0644]
kernel/io/Makefile [new file with mode: 0644]
kernel/io/console/Makefile [new file with mode: 0644]
kernel/io/console/README [new file with mode: 0644]
kernel/io/console/misc.cc [new file with mode: 0644]
kernel/io/console/vga.cc [new file with mode: 0644]
kernel/io/console/vga.cdl [new file with mode: 0644]
kernel/io/irq/Makefile [new file with mode: 0644]
kernel/io/irq/i8259.cc [new file with mode: 0644]
kernel/io/timer/Makefile [new file with mode: 0644]
kernel/io/timer/i8254.cc [new file with mode: 0644]
kernel/lib/Makefile [new file with mode: 0644]
kernel/lib/ctors.cc [new file with mode: 0644]
kernel/lib/kernel [new symlink]
kernel/lib/libc.cc [new file with mode: 0644]
kernel/lib/orb.cc [new symlink]
kernel/mem/Makefile [new file with mode: 0644]
kernel/mem/addrspace.cc [new file with mode: 0644]
kernel/mem/addrspace.cdl [new file with mode: 0644]
kernel/mem/orbmm.cc [new file with mode: 0644]
kernel/mem/pagealloc.cc [new file with mode: 0644]
kernel/mem/pagealloc.cdl [new file with mode: 0644]
kernel/mem/pagetable.cc [new file with mode: 0644]
kernel/mem/rmap.cc [new file with mode: 0644]
kernel/orb/Makefile [new file with mode: 0644]
kernel/orb/invoke.cc [new file with mode: 0644]
kernel/tests/Makefile [new file with mode: 0644]
kernel/tests/aspace.cc [new file with mode: 0644]
kernel/tests/mutex.cc [new file with mode: 0644]
kernel/tests/threads.cc [new file with mode: 0644]
lib/Makefile [new file with mode: 0644]
lib/c++/Makefile [new file with mode: 0644]
lib/c++/orb.cc [new file with mode: 0644]
lib/c/Makefile [new file with mode: 0644]
lib/kernel/Makefile [new file with mode: 0644]
lib/kernel/Makefile.kernel [new file with mode: 0644]
lib/kernel/README [new file with mode: 0644]
lib/kernel/time/Makefile [new file with mode: 0644]
lib/kernel/time/timer.cc [new file with mode: 0644]
lib/kernel/time/timer.cdl [new file with mode: 0644]
utils/buildbfendian.c [new file with mode: 0644]
utils/buildendian.c [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..0d32b62
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Except where indicated otherwise, this software is:
+Copyright (c) 2006 Scott Wood <scott@buserror.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimers.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimers in the
+      documentation and/or other materials provided with the distribution.
+
+    * The names of the Software's authors and/or contributors
+      may not be used to endorse or promote products derived from
+      this Software without specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..8e0e381
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+SUBDIRS = idlcomp idl include lib kernel
+
+all :
+       for i in $(SUBDIRS); do if ! ($(MAKE) -C $$i); then break; fi; done
+
+.PHONY: $(SUBDIRS)
+$(SUBDIRS):
+       $(MAKE) -C $@
+
+.PHONY : all
+
+clean : 
+       for i in $(SUBDIRS); do $(MAKE) -C $$i clean; done
+
+distclean:
+       for i in $(SUBDIRS); do $(MAKE) -C $$i distclean; done
+
+dep :
+       @echo Top-level "make dep" not supported\; dependencies will be done automatically.
+
+depend : dep
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif 
diff --git a/Makefile.head b/Makefile.head
new file mode 100644 (file)
index 0000000..0742497
--- /dev/null
@@ -0,0 +1,74 @@
+# Makefile.head -- the initial portion of a generic makefile
+# framework to be included at the top directory of each component. 
+#
+# Before including this file, the TOP variable should be set to the
+# absolute path of the top-level project directory, and the COMP
+# variable should be set to the path to the component's top-level
+# directory, relative to the project's top-level directory.  After
+# including this file, include the directory makefiles, and then
+# include Makefile.tail.  Before including Makefile.tail, TARGETS
+# should be set to the default targets to be made when "make" is
+# invoked with no arguments.  The target rules themselves can be
+# declared after Makefile.tail in order to use variables such as OBJS
+# which are set in Makefile.tail.
+#
+# WARNING: These makefiles will not work if you have spaces in any
+# pathnames, including any parent directory outside the project
+# hierarchy.  This is a limitation of GNU make (quoting things
+# doesn't help).  Sorry.
+#
+# The makefiles also probably won't work very well if the component
+# directory is a symlink, and the top-level directory is the symbolic
+# parent (i.e. dirname `pwd -L`) rather than the physical parent
+# (i.e. dirname `pwd -P`).
+#
+# NOTE: due to suckage of make that I haven't figured out how to work
+# around, you'll have to run make twice after you update a .cdl file
+# in order to make sure that any code that includes the generated
+# headers has been rebuilt.  This is despite having an explicit
+# dependency of the .cc file on the normal .h, of the normal .h on
+# the generated .h, of the generated headers on the server directory,
+# and of the server directory on the CDL file.  The CDL->directory
+# rule is used to generated the headers, but even if it's done
+# specifically for the .cc file up the chain, it doesn't rebuild the
+# .cc file.
+
+TARGET := $(TOP)/Makefile.target
+
+ifeq ($(TARGET),$(wildcard $(TARGET)))
+include $(TARGET)
+endif
+
+CXXINCS += -I$(TOP)/include/c++ -I$(GENINCLUDES)/c++ \
+           -I$(TOP)/include/c -I$(TOP)/include/c/std \
+           -I$(BUILDDIR)/include
+
+WARN += -Wall -Werror
+OPT += -O2
+DEBUG += -g3
+
+CXXFLAGS += $(CXXINCS) $(DEFS) $(CXXWARN) $(OPT) $(DEBUG)
+BUILDCXXFLAGS += $(BUILDDEFS) $(CXXWARN) $(OPT) $(DEBUG)
+
+.PHONY: all default rerun dep servers clean distclean
+.SUFFIXES:
+
+all: default
+
+# Make sure "make" treats thse as the right type of variable
+# when += is used later.
+DIRS :=
+
+CFILES :=
+CXXFILES :=
+ASFILES :=
+
+GENCFILES :=
+GENCXXFILES :=
+GENASFILES :=
+
+BUILDCFILES :=
+BUILDCXXFILES :=
+
+BUILDGENCFILES :=
+BUILDGENCXXFILES :=
diff --git a/Makefile.tail b/Makefile.tail
new file mode 100644 (file)
index 0000000..bc5baca
--- /dev/null
@@ -0,0 +1,154 @@
+# Makefile.tail -- see Makefile.head for usage information
+
+ASSRCS := $(ASFILES:%=%.S)
+CSRCS := $(CFILES:%=%.c)
+CXXSRCS := $(CXXFILES:%=%.cc)
+
+ASOBJS := $(ASFILES:%=$(BUILDDIR)/%.o)
+GENASOBJS := $(GENASFILES:%=$(BUILDDIR)/%.o)
+COBJS := $(CFILES:%=$(BUILDDIR)/%.o)
+GENCOBJS := $(GENCFILES:%=$(BUILDDIR)/%.o)
+CXXOBJS := $(CXXFILES:%=$(BUILDDIR)/%.o)
+GENCXXOBJS := $(GENCXXFILES:%=$(BUILDDIR)/%.o)
+
+# ASOBJS must come first, so that the kernel entry code can be
+# at the beginning of the output image.
+
+FILES := $(ASFILES) $(CFILES) $(CXXFILES)
+OBJS := $(ASOBJS) $(COBJS) $(CXXOBJS) $(GENASOBJS) $(GENCOBJS) $(GENCXXOBJS)
+GENSRCS := $(GENASFILES:%=$(BUILDDIR)/%.S) $(GENCFILES:%=$(BUILDDIR)/%.c) \
+           $(GENCXXFILES:%=$(BUILDDIR)/%.cc)
+SRCS := $(ASFILES:%=%.S) $(CFILES:%=%.c) $(CXXFILES:%=%.cc)
+
+BUILDCOBJS := $(BUILDCFILES:%=$(BUILDDIR)/%.o)
+BUILDGENCOBJS := $(BUILDGENCFILES:%=$(BUILDDIR)/%.o)
+BUILDCXXOBJS := $(BUILDCXXFILES:%=$(BUILDDIR)/%.o)
+BUILDGENCXXOBJS := $(BUILDGENCXXFILES:%=$(BUILDDIR)/%.o)
+
+BUILDOBJS := $(BUILDCOBJS) $(BUILDCXXOBJS) $(BUILDGENCOBJS) $(BUILDGENCXXOBJS)
+BUILDGENSRCS := $(BUILDGENCFILES:%=$(BUILDDIR)/%.c) $(BUILDGENCXXFILES:%=$(BUILDDIR)/%.cc)
+BUILDSRCS := $(BUILDCFILES:%=%.c) $(BUILDCXXFILES:%=%.cc)
+
+$(ASOBJS): $(BUILDDIR)/%.o: %.S
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(CC) $(ASFLAGS) -c -o "$@" "$<"
+
+$(COBJS): $(BUILDDIR)/%.o: %.c
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(CC) $(CFLAGS) -c -o "$@" "$<"
+
+$(CXXOBJS): $(BUILDDIR)/%.o: %.cc
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(CXX) $(CXXFLAGS) -c -o "$@" "$<"
+
+$(GENASOBJS): %.o: %.S
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(CC) $(ASFLAGS) -c -o "$@" "$<"
+
+$(GENCOBJS): %.o: %.c
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(CC) $(CFLAGS) -c -o "$@" "$<"
+
+$(GENCXXOBJS): %.o: %.cc
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(CXX) $(CXXFLAGS) -c -o "$@" "$<"
+
+$(BUILDCOBJS): $(BUILDDIR)/%.o: %.c
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(BUILDCC) $(BUILDCFLAGS) -c -o "$@" "$<"
+
+$(BUILDCXXOBJS): $(BUILDDIR)/%.o: %.cc
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(BUILDCXX) $(BUILDCXXFLAGS) -c -o "$@" "$<"
+
+$(BUILDGENCOBJS): %.o: %.c
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(BUILDCC) $(BUILDCFLAGS) -c -o "$@" "$<"
+
+$(BUILDGENCXXOBJS): %.o: %.cc
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(dir $@)
+       @$(BUILDCXX) $(BUILDCXXFLAGS) -c -o "$@" "$<"
+
+clean: $(EXTRACLEAN)
+       $(RM) $(foreach dir,$(DIRS),$(BUILDDIR)/$(dir)*.o)
+       $(RMDIR) $(BUILDDIR)/include/servers
+       $(RMDIR) $(TARGETS) $(GENSRCS) $(BUILDGENSRCS)
+
+distclean: clean $(EXTRADISTCLEAN)
+       $(RM) .gdb_history
+       $(RMDIR) $(BUILDDIR)
+
+# Every .o file which has a corresponding .cdl file will
+# depend on the server stubs.
+
+SERVERS := $(wildcard $(FILES:%=%.cdl))
+SERVERS := $(SERVERS:%.cdl=%) 
+
+$(SERVERS:%=$(SERVERDIR)/%): $(SERVERDIR)/%: %.cdl $(IFACES)
+       @echo $(COMP): "$<"
+       @$(RMDIR) "$@"
+       @if ! $(IDLC) -t $(ARCH) -l c++ -r -i "$(IFACES)" -o "$@" "$<"; then \
+               $(RMDIR) "$@"; \
+               false; \
+       fi
+
+.DELETE_ON_ERROR: $(SERVERS:%=$(SERVERDIR)/%)
+
+$(SERVERS:%=%.cc): %.cc: $(SERVERDIR)/%
+servers: $(SERVERS:%=$(SERVERDIR)/%)
+
+ifneq ($(NODEPS),y)
+dep: servers $(PREDEP) 
+       @echo $(COMP): Generating dependencies
+       @$(RM) "$(BUILDDIR)"/.depend
+       @$(MKDIR) "$(BUILDDIR)"
+       @$(TOUCH) "$(BUILDDIR)"/.depend
+       @for i in $(SRCS); do if [ -f "$$i" ]; then \
+               OBJ=`echo "$$i" | sed s/\\.\[^.\]*$$//`.o; \
+               $(CXX) $(CXXFLAGS) -DMAKEDEP -M -MT "$(BUILDDIR)/$$OBJ" "$$i" >> "$(BUILDDIR)"/.depend; \
+       fi; done
+       @for i in $(GENSRCS); do if [ -f "$$i" ]; then \
+               OBJ=`echo "$$i" | sed s/\\.\[^.\]*$$//`.o; \
+               $(CXX) $(CXXFLAGS) -DMAKEDEP -M -MT "$$OBJ" "$$i" >> "$(BUILDDIR)"/.depend; \
+       fi; done
+       @for i in $(BUILDSRCS); do if [ -f "$$i" ]; then \
+               OBJ=`echo "$$i" | sed s/\\.\[^.\]*$$//`.o; \
+               $(BUILDCXX) $(BUILDCXXFLAGS) -DMAKEDEP -M -MT "$(BUILDDIR)/$$OBJ" "$$i" >> "$(BUILDDIR)"/.depend; \
+       fi; done
+       @for i in $(BUILDGENSRCS); do if [ -f "$$i" ]; then \
+               OBJ=`echo "$$i" | sed s/\\.\[^.\]*$$//`.o; \
+               $(BUILDCXX) $(BUILDCXXFLAGS) -DMAKEDEP -M -MT "$$OBJ" "$$i" >> "$(BUILDDIR)"/.depend; \
+       fi; done
+       @for i in $(SERVERS); do \
+               $(IDLC) -M -l c++ -r -i "$(IFACES)" -o "$(SERVERDIR)/$$i" \
+               "$$i.cdl" >> "$(BUILDDIR)"/.depend; \
+       done
+
+depend: dep
+
+ifeq ($(BUILDDIR)/.depend,$(wildcard $(BUILDDIR)/.depend))
+include $(BUILDDIR)/.depend
+default: $(TARGETS)
+else
+rerun: dep
+       @$(MAKE)
+
+default: rerun
+endif
+
+else
+dep:
+depend:
+
+default: $(TARGETS)
+endif
diff --git a/Makefile.target b/Makefile.target
new file mode 100644 (file)
index 0000000..f759208
--- /dev/null
@@ -0,0 +1,112 @@
+ifndef TARGETDEFS
+TARGETDEFS := done
+
+ifndef ARCH
+$(error Please define $$(ARCH).)
+endif
+
+VALIDARCH := no
+
+ifeq ($(ARCH),x86)
+CROSS := i686-polintos-
+DEFS += -DBITFIELD_LE
+VALIDARCH := yes
+endif
+
+ifeq ($(ARCH),x64)
+CROSS := x86_64-polintos-
+DEFS += -DBITFIELD_LE
+VALIDARCH := yes
+endif
+
+ifeq ($(VALIDARCH),no)
+$(error $(ARCH) is not a supported target.)
+endif
+
+ifndef USECROSS
+CROSS :=
+endif
+
+ifdef NOSMP
+DEFS += -D_LL_NOSMP
+endif
+
+# C++ prohibits defeferencing a NULL non-POD pointer (and thus
+# prohibits using offsetof on non-POD types, even though there's no
+# good reason to disallow it).  Some headers use offsetof on non-POD
+# types (typically, the types are non-POD only because they have a
+# constructor to initialize fields to a known state (or with
+# passed-in values); hardly a reason to make it completely non-POD
+# and throw out a bazillion otherwise legal actions).
+#
+# Thus, since I know of no way (such as a #pragma) for code to
+# disable a warning itself for a specific region of code, everything
+# needs to disable this warning.  On one hand, I consider it a bogus
+# warning, and thus it's not that big of a deal.  On the other hand,
+# having the warning suppressed means you won't know if your code
+# will produce the warning in other environments without actually
+# trying it there.  Oh well; blame the C++ and/or GCC people, not me.
+#
+# This warning option requires at least GCC 3.4.  If you want to use
+# an older compiler, remove this and live with the warnings (as well
+# as whatever other unknown issues you encounter).
+
+CXXWARN += $(WARN) -Wno-invalid-offsetof
+
+ifndef BUILDTYPE
+BUILDTYPE := user
+endif
+
+BUILDNAME := build
+BASEBUILDDIR := $(TOP)/$(BUILDNAME)
+ARCHBUILDDIR := $(TOP)/$(BUILDNAME)/$(ARCH)
+
+ifeq ($(ARCHINDEP),y)
+BUILDDIR := $(BASEBUILDDIR)/$(BUILDTYPE)/$(COMP)
+else
+BUILDDIR := $(ARCHBUILDDIR)/$(BUILDTYPE)/$(COMP)
+endif
+
+UTILBUILDDIR := $(BASEBUILDDIR)/build/utils
+SERVERDIR := $(BUILDDIR)/include/servers
+
+IFACES := $(BASEBUILDDIR)/build/idl/ifaces
+GENINCLUDES := $(ARCHBUILDDIR)/build/include/generated
+
+BUILDCC := g++
+BUILDCXX := g++
+CC := $(CROSS)g++
+CXX := $(CROSS)g++
+AS := $(CROSS)as
+LD := $(CROSS)ld
+STRIP := $(CROSS)strip
+DEFS += -D_LL_ARCH_$(ARCH) -D_LL_ARCH=$(ARCH)
+MKDIR := mkdir -p
+MV := mv
+RM := rm -f
+RMDIR := rm -rf
+LN := ln -s
+IDLC := $(BASEBUILDDIR)/build/idlcomp/idlc
+TOUCH := touch
+BISON := bison
+FLEX := flex
+
+# If you want to cross-compile idlc, set BUILD_ENDIAN to LE or BE
+# and build only the idlc component.  Also, set BUILDCXX to the
+# cross compiler.
+
+ifndef BUILD_ENDIAN
+$(shell $(MKDIR) $(UTILBUILDDIR))
+$(shell $(BUILDCC) $(TOP)/utils/buildendian.c -o $(UTILBUILDDIR)/buildendian)
+BUILD_ENDIAN := $(shell $(UTILBUILDDIR)/buildendian)
+endif
+
+ifndef BUILD_BFENDIAN
+$(shell $(MKDIR) $(UTILBUILDDIR))
+$(shell $(BUILDCC) $(TOP)/utils/buildbfendian.c -o $(UTILBUILDDIR)/buildbfendian)
+BUILD_BFENDIAN := $(shell $(UTILBUILDDIR)/buildendian)
+endif
+
+BUILDDEFS += -DBITFIELD_$(BUILD_BFENDIAN)
+
+endif
diff --git a/doc/abi/alpha b/doc/abi/alpha
new file mode 100644 (file)
index 0000000..41bb053
--- /dev/null
@@ -0,0 +1,56 @@
+Method Invocation:
+   Caller:
+      a0: object ID
+      a1: method ID
+
+      a2: pointer to args (low addr to high, padded as in a struct):
+          Out arrays can be preallocated.  Scalar outs are ignored,
+          but space is made for them.  NULL array params and arrays
+          which are larger than the preallocated buffer are
+          allocated by the callee.  Arrays are passed as a pointer
+          and a 64-bit element count, in that order.  Objects are
+          passed as pointers.
+      
+      Call a special symbol TBD to invoke the method.
+      
+      Upon return:
+      params on stack, with out params filled in and out params clobbered
+
+      t0-t11, at, ra clobbered
+      v0: pointer to exception, or NULL if none.
+      a2: pointer to args, with out params filled in; in params
+          may be clobbered
+      
+   Callee:
+      params on stack (low addr to high), ids replaced with pointers,
+      at least 8 bytes of spare space beyond the high element
+
+      a0: object pointer
+      a1: pointer to caller information struct, if such was
+          requested
+      ra: return address
+      
+      Upon return:
+      
+      params on stack (low addr to high), in params may be clobbered
+      t0-t11, at, ra may be clobbered
+      v0: pointer to exception, or NULL if none.
+
+Stack:
+   sp is stack pointer, grows down, decrement before store
+      
+Object structure:
+   The object ID is stored as a 64-bit quantity at an offset
+   specified by calling a method TBD.
+
+Wrapper object creation:
+       The function to create wrapper objects is specified by calling a
+   method TBD.  The function shall conform to the local ABI, and
+   takes an ID as a 64-bit integer as the first parameter, and a
+   pointer to the class as the second. It returns a pointer.
+   
+   Wrapper objects may be preemptively declared to avoid infinite
+   loops by calling a method TBD.
+
+Struct padding:
+   All fields are padded so that basic types are naturally aligned.
diff --git a/doc/abi/x86 b/doc/abi/x86
new file mode 100644 (file)
index 0000000..2038cd5
--- /dev/null
@@ -0,0 +1,60 @@
+Function Calls and In-Process Method Invocation:
+   SysV i386 ABI
+
+Out-of-Process Method Invocation:
+   Caller:
+      eax: object ID
+      ecx: method ID
+
+      edx: pointer to parameter info block (PIB), described below
+
+      Call the 32-bit address stored at 0x7fff0000 to invoke the method.
+      
+      Upon return:
+      ebx, esi, edi, ebp, esp: preserved
+      eax: pointer to exception, or NULL if none.
+           If there is an exception, the user part of the syscall
+           function will search for an exception handling function
+           that covers the calling address.  If none is found,
+           it will assume that it is a language without exception
+           handling, and return the exception to the caller in eax.
+      ecx: clobbered
+      edx: pointer to args, with out params filled in; in params
+           may be clobbered.  This will be the same pointer as
+           was passed in by the caller.
+      
+   Callee:
+      params on stack (low addr to high), ids replaced with pointers,
+      at least 4 bytes of spare space beyond the high element
+
+      eax: object pointer
+      edx: pointer to caller information struct, if such was
+           requested
+      ecx: return address
+      
+      Upon return:
+      
+      params on stack (low addr to high), in params may be clobbered
+      eax: pointer to exception, or NULL if none.
+      ebx, esi, edi, ebp, esp: should be preserved
+      ecx, edx: may be clobbered
+
+Stack:
+   esp is stack pointer, grows down, decrement before store
+      
+Object structure:
+   The object ID is stored as a 32-bit quantity at an offset
+   specified by calling a method TBD.
+
+Wrapper object creation:
+       The function to create wrapper objects is specified by calling a method
+   TBD.  The function shall conform to the local ABI, and takes an ID as a
+   32-bit integer as the first parameter, and a pointer to the class as
+   the second. It returns a pointer.
+   
+   Wrapper objects may be preemptively declared to avoid infinite loops by
+   calling a method TBD.
+
+Struct padding:
+   All fields are padded so that basic types are naturally aligned.
+
diff --git a/doc/attributions b/doc/attributions
new file mode 100644 (file)
index 0000000..5b67798
--- /dev/null
@@ -0,0 +1,70 @@
+The PolIntOS project contains code from various sources and under
+various licenses.  Many of these licenses require that binary
+distributions contain attribution and/or the full license text; this
+is where such things go.  This file only applies to the base PolIntOS
+project; add-on components which have been separated out due to
+license compatibility issues or for other reasons will have their
+attributions within the component in question.
+
+Original code written by Scott Wood
+===================================
+
+// This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal with
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+// 
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimers.
+// 
+//     * Redistributions in binary form must reproduce the above copyright notice,
+//       this list of conditions and the following disclaimers in the
+//       documentation and/or other materials provided with the distribution.
+// 
+//     * The names of the Software's authors and/or contributors
+//       may not be used to endorse or promote products derived from
+//       this Software without specific prior written permission.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+// SOFTWARE.
+
+Portions of the C library which come from newlib
+================================================
+
+/* Copyright (c) 2002 Red Hat Incorporated.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+
+     Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+     Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+
+     The name of Red Hat Incorporated may not be used to endorse
+     or promote products derived from this software without specific
+     prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED.  IN NO EVENT SHALL RED HAT INCORPORATED BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS   
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/doc/copyright-header b/doc/copyright-header
new file mode 100644 (file)
index 0000000..3150645
--- /dev/null
@@ -0,0 +1,29 @@
+//
+// This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal with
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+// 
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimers.
+// 
+//     * Redistributions in binary form must reproduce the above copyright notice,
+//       this list of conditions and the following disclaimers in the
+//       documentation and/or other materials provided with the distribution.
+// 
+//     * The names of the Software's authors and/or contributors
+//       may not be used to endorse or promote products derived from
+//       this Software without specific prior written permission.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+// SOFTWARE.
+
diff --git a/doc/idl/corba-differences b/doc/idl/corba-differences
new file mode 100644 (file)
index 0000000..7825a8e
--- /dev/null
@@ -0,0 +1,44 @@
+These are some of the differences between CORBA and this object
+model (this list is not meant to be exhaustive):
+
+No preprocessor in the IDL.
+
+No double-extended floating point type (most architectures don't
+support it, and its main purpose is extra precision for temporary
+calculations, where RPC is somewhat irrelevant).
+
+No fixed point type, at least not as a basic type.  Perhaps as a
+value type...
+
+All characters are Unicode; if you need to pass characters or strings
+in ISO Latin-1 and/or other character sets, use the octet type.
+
+A string type exists, which holds characters in a UTF-8 encoding.
+
+No "any" type (use objects that encapsulate basic types instead (or
+maybe built-in value types?)).
+
+No unions.
+
+Sequences are not a special case; they are merely arrays whose
+minimum and maximum lengths are not equal.
+
+No multi-dimensional arrays.
+
+No value types for now (though they look interesting).  At the
+moment, I'm leaning towards implementing this as a binding between a
+class and a struct. 
+
+Instead of "oneway", there's "async".  The basic idea is the same,
+except that async method invocations have the same execution
+guarantees as normal methods (that is, it may never complete because
+the remote machine is down, or the remote process is hung; however,
+it will never be dropped due to things like packet loss).
+
+There is no return type (only out parameters).  This may change.
+
+No attributes (yet).
+
+Structs can inherit from other structs, and this hierarchy can be
+used for exceptions.  All structs inherit from the root Struct type.
+
diff --git a/doc/idl/language-bindings/c++/specification b/doc/idl/language-bindings/c++/specification
new file mode 100644 (file)
index 0000000..3c3da4b
--- /dev/null
@@ -0,0 +1,125 @@
+General
+=======
+
+Each IDL namespace (as opposed to an interface, struct, etc. namespace)
+is mapped to a C++ header file and a directory.  The name of the
+directory is the name of the namespace; the name of the file is the
+namespace name, followed by ".h".  The header file and the directory
+are both part of the same directory; the former does not go in the
+latter.  The directory contains sub-IDL-namespaces; if there are no
+such namespaces, the implementation is permitted, but not required, to
+create an empty directory.
+
+Each sub-IDL-namespace's header file is #included from its parent
+header file, so that #including the top-level header file shall be
+sufficient to access any descendent namespace.
+
+Each header file shall contain protection against being #included
+multiple times.  This protection may not interfere with that of any
+other namespace.
+
+All identifiers and preprocessor symbols beginning with _IDL_CPP_ are
+reserved for use by the language binding.
+
+Types
+=====
+
+IDL types are mapped to C++ types as follows:
+
+  IDL Type                C++ Type
+  ========                ========
+
+  bool                    uint8_t
+  octet                   uint8_t
+  short                   int16_t
+  ushort                  uint16_t
+  int                     int32_t
+  uint                    uint32_t
+  long                    int64_t
+  ulong                   uint64_t
+  fshort                  float
+  flong                   double
+  char                    uint8_t (UTF8)
+
+FIXME: currently char simply acts as octet; should it refer to an
+actual character, and thus be uint32_t?  If so, then unless the
+language supports UTF8 natively, an array of char would still be an
+array of octets, which could be confusing...  Alternatively, get rid
+of char as a basic type, and use a typedef of octet to indicate that
+this particular octet is UTF8.
+
+Namespaces
+==========
+
+IDL namespaces are mapped directly to C++ namespaces.  Symbols
+declared inside a struct or interface which are not methods or
+non-const data members will go into a parallel namespace, which is
+the type-namespace name with "_ns" appended.
+
+Enumerations
+============
+
+As the size of a native C++ enum type is not guaranteed, and since
+they are not type-safe or namespace-safe, IDL enums are mapped to C++
+structs.  Each enumeration value is declared inside the struct as an
+anonymous enum value, and there is a _val entry which is an unsigned
+integer of the smallest type that can hold the IDL enum size.
+
+Bitfields
+=========
+
+An IDL bitfield is mapped to a C++ struct using field width
+specifiers on the members.  Integral members are mapped directly to
+C++ unsigned integers of the smallest type that can hold the IDL
+bitfield size, with a field size specifier.  
+
+When a bitfield is used as the type of a bitfield entry, it cannot be
+directly mapped, as C++ does not allow non-integral types to have
+field size specifiers.  Instead, get and set methods are provided to
+pack and unpack the data, and the contents of the bitfield type are
+directly inlined in the enclosing bitfield.  If the field name is foo
+and the type is Foo, then the methods will be "Foo get_foo() and
+set_foo(Foo foo)".  If a field name begins with "set_" or "get_", it
+will be preceded with an underscore for the field itself (but not for
+the set or get methods).  Thus, a field called set_foo in IDL would
+become _set_foo, but a get method (if required) would be get_set_foo,
+not get__set_foo.  The underscore prefix happens regardless of
+whether the field requires get and set methods.
+
+With an embedded bitfield, the inlined fields have the sub-bitfield
+name prefixed to the field name.  Thus, if a bitfield Foo with fields
+a and b is included in a bitfield Bar as fields c and d, then Bar will have
+fields c_IDLNS_a, c_IDLNS_b, d_IDLNS_a, and d_IDLNS_b, and methods
+set_c(Foo), Foo get_c(), set_d(Foo), and Foo get_d().  The IDLNS is
+ugly, but there's no way I know of to use a C++ namespace mechanism
+without introducing unwanted padding (even __attribute__((packed))
+will byte-align a substruct).
+
+Likewise, embedded enumerations cannot use the actual enumeration
+struct as a C++ bitfield member; they are treated as a simple
+integers instead.  Get and set methods are not needed, as enums will
+convert to integers, but they may be added in the future to provide a
+type-safe alternative to direct assignment.
+
+Objects
+=======
+
+A reference to an object has the name of the type, with no pointer or
+reference marker.  If uninitialized, they default to NULL.  Methods
+are called in the same way as on a C++ class, using the dot, not the
+arrow.  To increment an object's reference count, call the retain()
+method.  To decrement it, call the release() method.  Weak references
+and autorelease pools will be added later.
+
+Classes
+=======
+
+The implementor of a class must #include the generated .h file for
+the class in a public: portion of the class definition.  The class's
+constructor must call init_iface() before any object references to
+the object are used.  The generated footer.cc file must be included
+in exactly one non-header file.  It is recommended that this be the
+file in which the methods are defined, so that they can be inlined by
+the stubs; however, the only requirement is that it have access to
+the class definition and any interface headers the implemented
+interfaces reference.
diff --git a/doc/idl/namespace-precedence b/doc/idl/namespace-precedence
new file mode 100644 (file)
index 0000000..01d14cb
--- /dev/null
@@ -0,0 +1,180 @@
+When a non-globally-qualified identifier is used that could
+potentially refer to more than one symbol in different namespaces,
+the following rules are used to determine which, if any, is assumed
+to be the intended symbol:
+
+The current stack of parent namespaces (not parent types) is
+searched, starting with the current namespace and proceeding outward. 
+At each level, the following searches are made in order:
+
+1. All symbols declared directly in the namespace, including aliases
+and symbols imported with a specific using statement.
+
+2. Symbols imported by a using namespace statement at that level.  If
+more than one imported symbol is matched, it is an error.  Symbols
+imported by a using statement take effect immediately, and are used
+for subsequent imports, even in the same statement.  Lookups for any
+purpose *other* than a using statement use all importations,
+regardless of whether they come before or after the lookup in the
+file.
+
+Symbols defined in namespaces of inherited types are not imported, as
+I felt that for IDL, the benefit wasn't worth the implementation
+complexity (there are some chicken-and-egg problems; aliases declared
+in the namespace need to be looked up early so that alias chains can
+be formed, but inherited types need to be looked up earlier so that
+they can be used for namespace searches, but types can be inherited
+using alias names...).
+
+Note that importing a namespace does not bring the symbol name of that
+namespace into the current namespace along with its context.  In
+certain circumstances, this could lead to problems if the name used
+to refer to the namespace is shadowed by an imported name.  This
+won't necessarily cause an error to be reported, if the name that was
+used for importing the namespace was imported or declared in an outer
+namespace, and the alternate name is valid in the context used.  In
+general, you should not use whole-namespace imports except for
+closely related bits of code/interfaces where you can be sure that
+nothing unintended is getting imported.
+
+At some point, I may change this so that imported namespace names
+(including the path as used to import) do get added as private
+aliases.  If so, it could apply to inherited types as well.
+
+If a match is found at one rule, the search stops, and no further
+rules or levels are considered.  If an error occurs, the search also
+stops (i.e. a proper match at a later level does not undo the error).
+
+If no symbols were found in the above steps, the global namespace is
+searched.
+
+Only the first component of a namespace-qualified name is looked up;
+further components are not used for disambiguation.
+
+Note that when using a namespace either in a namespace statement, or
+when creating a type using a qualified identifier, no searching is
+done, but the current namespace is assumed (or global, if there's a
+leading "..").  If the namespace does not exist, it is created.
+
+Example:
+
+namespace A {
+   struct D {
+   };
+   
+   struct F {
+   };
+
+   // A.G is declared as a namespace; no attempt is made to look for
+   // a global G.
+   struct G.Q {
+   };
+}
+
+struct B {
+   struct C {
+      struct X {
+      };
+   };
+   
+   struct G {
+   };
+   
+   struct H {
+   };
+
+       struct I {
+       };
+};
+
+namespace C {
+   using A.*;
+   
+   struct D {
+   };
+   
+   struct E {
+   };
+   
+   struct F {
+   };
+   
+   struct H {
+   };
+
+       struct I {
+       };
+   
+   struct B.E {
+      struct F {
+      };
+      
+      struct E {
+      };
+      
+      B b;    // Error: Uses C.B, rule #1 once C is reached,
+              // but C.B is not a type
+      D d;    // Uses C.D, rule #1 once C is reached
+      B.E be; // Uses C.B.E, rule #1 once C is reached
+      E e;    // Uses C.B.E.E, rule #1 in current namespace
+      E.E ee; // Error, as the first E matches C.B.E.E, and
+              // there is no C.B.E.E.E.  The extra .E is
+              // not used for disambiguation.
+      F f;    // Uses C.B.E.F, rule #1 in current namespace
+      G g;    // Error, Uses A.G, rule #2 once namespace C is
+              // reached, but A.G is not a type
+      H h;    // Uses C.H, rule #1 once namespace C is reached.
+   };
+}
+
+struct D {
+       // Imports B and B.C, not B and C.  If it were "using C.*, B.*",
+       // then it would import C and C.B.
+
+   using B.*, C.*;
+   using ..B.I;
+   
+   struct E {
+      B b;    // Error: Uses C.B, rule #2 once D is reached, but C.B
+              // not a type.  Only the contents of namespaces are
+                       // imported, not the names themselves.
+      C.D cd; // Error.  For the same reason as above, C refers
+              // to B.C, not C.  There is no D in B.C.
+      C.X cx; // Uses B.C.X.
+      D d;    // Uses C.D, rule #2 when D is reached.  D itself
+              // is declared in the global namespace, which doesn't
+              // get reached because a match is found sooner.
+      E e;    // Uses D.E, rule #1 once D is reached.  C.E would 
+              // have been matched by rule #2 if D.E did not exist.
+      F f;    // Uses C.F, rule #2 once D is reached.  A.F is not
+              // matched, as importation is not transitive.
+      G g;    // Uses B.G, rule #2
+      H h;    // Error, both B.H and C.H match on rule #2 once
+              // D is reached.  B does not get precedence for
+              // coming first, or because the C importation
+              // comes after this lookup.
+               I i;    // Uses B.I, as specific-symbol imports are treated
+                       // as symbols declared in this namespace, rather than
+                       // imports (and thus B.I has precedence over C.I).
+   };
+   
+   int I;     // Error; this conflicts with the specific-symbol
+                  // importation of B.I.
+
+   // Now C is imported.  This importation is valid through the
+   // entire namespace (except for prior using statements), not just
+   // after this point.
+   
+   using ..C.*;
+};
+
+
+It goes without saying that it is not recommended that code be
+written which relies excessively on these rules; they are mainly
+intended so that:
+
+1. Local namespaces are not unduly restricted by what some imported
+library declares, and
+
+2. If there is an ambiguity, all compilers will handle it the same
+way.
diff --git a/doc/orb/ids b/doc/orb/ids
new file mode 100644 (file)
index 0000000..0721a82
--- /dev/null
@@ -0,0 +1,46 @@
+IDs are used to identify a specific objects and methods to the ORB. Each
+address space has its own object ID space, but method IDs are a separate ID
+space that is shared by all users of an ORB.  Each interface does *not*
+form its own ID space for methods.  Classes, interfaces, and struct types
+are merely objects of specific classes, and thus share the object ID space.
+
+Object IDs
+==========
+
+Applications interact with objects via pointers (or references, or whatever
+else is appropriate for the language), not IDs.  This includes structs
+intended for marshalling to another address space.  To deal with this, the
+ORB accepts all object references in pointer form, rather than ID form. 
+For object references, the language mapping ensures that the ID can be
+found from the pointer in a manner consistent with the platform ABI. 
+Typically, it is stored as some fixed offset of the first word of the
+entity pointed to.  Structs do not have IDs; see the "memory-management"
+file for information on how they are marshalled across address spaces.
+
+When presenting the object to the destination address space, the ORB needs
+to convert the local object ID to the object ID in the server's ID space. 
+If the object has not yet been used in that address space, there will be no
+local ID (or pointer) associated with it.  If this is the case, an object
+stub will be created by the ORB.
+
+To maintain the ID->pointer mapping, the ORB will maintain a pointer object
+for every local ID in an ID space that is not the "home" of the real
+object.  These objects, whose memory is chargeable to the address space
+they were created for, contain the application address associated with the
+ID, as well as a pointer to the real object.  The pointer object may also
+hold cached results of permission checks.
+
+For every real object, the ORB maintains a similar internal object, but
+instead of a pointer to a real object, it contains all the information
+needed for the ORB to carry out requests on the ID.
+
+Method IDs
+==========
+
+Each method has an ID which is global to the ORB, which is used when
+specifying which method to invoke on a given object.  A method will also
+have an interface-local index "id", which can be used internally for things
+such as indexing into a pointer-object's cached permission checks, but such
+indices are not application-visible.  The only pointer associated with a
+method is its entry point, which is not visible to the client.
+
diff --git a/doc/orb/memory-management b/doc/orb/memory-management
new file mode 100644 (file)
index 0000000..98871b6
--- /dev/null
@@ -0,0 +1,386 @@
+1. Method Parameters
+====================
+
+1.1 Overview
+============
+
+The writeability, sharedness, and lifetime of memory passed by reference
+as a parameter (in or out) depends on the parameter attributes, as well as
+whether the method is asynchronous.  The implementation of the semantics
+will be different for in-process and remote methods, and not all
+restrcitions are enforced as strictly for in-process methods, but the
+semantics when the rules are followed must be the same for both.
+
+The data type of parameter also affects how it is handled.  Built-in
+datatypes, bitfields, enums, and inline structs and arrays are passed
+directly by value (or, depending on the ABI, simple reference-valid-
+until-end-of-method for out parameters), and thus do not require memory
+management.  Object references are reference counted both at the ORB and
+process level, and are unique in that the client data structures are
+read-only and uncopyable (because a unique object needs a unique address)
+within an address space; most of this section does not apply to them.
+
+That leaves non-inline structs and arrays.  The ideal way of treating
+these depends on how they're being used.  Simple, small structs and arrays
+would benefit from being passed as a simple pointer (plus a length field
+in the case of arrays), with the method copying anything it wants to keep
+and/or change.  For async methods, the caller would need to avoid
+modifying referenced memory until it knows the method has been executed
+(in the case of remote methods, it only matters until the ORB has COWed or
+copied the memory, but client code should not depend on this, or it will
+not work with an in-process server).  This is the default behavior if no
+parameter attributes are specified.  With this type of parameter, there
+are no restrictions on what allocator the memory being passed came from
+for in parameters.  "Out" and inout parameters will be discussed later.
+
+The only major complexity here is in how the duplicate is made. Struct
+duplications will need to do a deep copy without being confused by
+reference loops; the stubs will need to provide a "duplicate" function
+that does this (possibly using a hash table or other associative array to
+identify reference loops if IDLC determines that they are possible with
+that particular type of struct).
+
+To avoid a double-copy, out-of-process methods may want to merely ask the
+ORB to give it full, permanent access (via COW if not already copied) to
+the memory.  This may be hard to implement, though, as it requires the
+server to know that the data came from an out-of-process implementation
+(even if it's been passed to other functions, some of which may be
+in-process object-model methods).  This optimization may only be useful
+for large amounts of data that is not likely to have most of its pages
+modified; if it is likely to be heavily modified, then COW doesn't help
+much, and may hurt (and if it's large, it probably hasn't already been
+copied).  It may be better to not implement this optimization, and instead
+recommend that the user use a parameter attribute to deal with the
+problem.
+
+1.2 Parameter Attributes
+========================
+
+With larger arrays and complex data structures, one would often benefit
+from being able to avoid the copy altogether.  This can be accomplished by
+altering the interface semantics.  All of the parameter attributes but
+"copy" do this, and thus cannot be changed without breaking interface
+compatibility.  None of the current parameter attributes can be combined
+with any other current parameter attribute.
+
+1.2.1 Default Semantics
+=======================
+
+If no attribute is specified, "in" parameters are visible only until the
+method returns, and are read-only.  There will be a "duplicate" function
+provided by the language binding for the server to use if it wants to
+retain and/or write to the data.  For "small" data (the threshold needs to
+be empirically determined), it just makes a copy.  For "large" data, the
+pages will be copy-on-write mapped (unless the caller asks for an
+immediate copy).  The only real reason not to use the immediate flag for
+small data (as determined by the programmer) as well (rather than have a
+threshold) is so that the threshold can be tunable based on the relative
+performance of copies versus traps on a given system.  It'd also be nice
+if the programmer could ask a profiler to determine whether large data
+should be COWed or copied immediately on a per-call basis.
+
+When the "duplicate" function is called, a copy-on-write mapping of the
+data will be created.  Edge data will be overmapped regardless of page
+type, but the overmap status will be retained (so that edge pages will not
+be overmapped into some other address space), though read overmap will be
+promoted to read/write overmap, as the extra data in the copy will not be
+used anymore.  There will be an option to the "duplicate" function to
+create fully overmappable pages by copying the edge data and zeroing the
+rest of the edge pages (in case the caller wants to share the data).
+   
+1.2.2 Copy
+==========
+
+The "copy" attribute affects only the implementation; changing it does not
+break interface compatibility (and thus require a new GUID).  As such, the
+use of this attribute is specified in the CDL rather than the IDL. 
+Language bindings that do not require a CDL file will provide some way of
+specifying copy semantics directly from the implementation language.
+
+This attribute directs the ORB to automatically make a copy (possibly via
+COW, but no read-only or shared mappings) of the parameter.  For
+in-process invocation, methods with any "copy" parameters will need to go
+through a stub to do the copy before calling the real method.
+
+1.2.3 Shared
+============
+
+The "shared" attribute declares that the method implementation and caller
+will treat the data as a read/write shared memory region, lasting beyond
+the end of the method.  The caller must ensure that all segments of data
+provided (including every nested struct, or struct/array in an array of
+structs/arrays) either begins and ends exactly on a page boundary, or has
+all partial pages marked for read/write overmap.  For out-of-process
+methods, an exception will be thrown if this is not the case.  For
+in-process methods, you'll merely go to hell for writing bugs that won't
+show up until someone hands your code a reference to an out-of-process
+implementation.  All data must be under the management of the ORB memory
+manager.
+
+The shared region is terminated when the caller or callee frees the memory
+using the ORB memory manager.  This requires calling some function that
+knows whether to actually free it (in the out-of-process case), or release
+a reference or something (in the in-process case).  For arrays, where
+we're already using a struct with pointer and length, adding a reference
+count pointer wouldn't be a big deal.  Struct pointers, OTOH, are
+currently plain pointers, and adding an indirection struct with pointer
+and reference pointer would be ugly, but doable.  The alternative is to
+have the memory manager look up the memory fragment and find the refcount
+in its internal data structures, which would require a lookup for every
+reference count operation.
+1.2.4 Push
+==========
+
+The "push" attribute transfers the memory region to the destination,
+unlinking it from the source address space.  The source memory region will
+be unmapped for out-of-process invocations; for in-process invocations,
+the memory region will simply belong to the callee, and the caller must
+not reference it any more.  Like "shared", all data fragments must either
+begin and end on a page boundary, or be in a page with read/write overmap
+enabled.  It is also required that every page being pushed be under the
+management of the ORB allocator.
+
+1.2.5 Inline
+============
+
+When used as a parameter attribute (either explicitly or as part of the
+type definition), "inline" specifies that the struct or array will be
+passed directly as a value parameter.  A NULL reference cannot be passed,
+and for out parameters, the method cannot return a reference to an
+existing struct or array, but instead must fill in the reference given.
+
+The "inline" attribute is similar to "copy", except that no in-process
+stub is required because it is part of the interface (though a stub may be
+required in certain languages if they do not provide automatic copying of
+value-passed structs/arrays).
+
+An inline array cannot be of variable length, and may be treated
+differently from other arrays in the language binding (e.g. in C++, inline
+arrays are bare language arrays, and do not use the Array or MutableArray
+wrappers).
+
+The "inline" attribute can also be used on members of a struct, to
+indicate that the member is embedded directly in the outer struct, rather
+than linked with a pointer.
+
+1.2.6 Immutable
+===============
+
+The "immutable" attribute is similar to "const" in C/C++ ("const" in
+IDL is used only for compile-time constants), and specifies that the
+array or struct cannot be modified through this particular reference.
+It is the default for parameters when neither "shared" nor "push" is
+used ("copy" and "inline" parameters will accept immutable references
+on the caller side, but will produce a mutable copy for the server
+side). It may be specified without effect when it is the default.
+
+Immutable "shared"/"push" parameters will result in read-only
+mappings in the destination address space, though for "push"
+parameters, the process will have permission to enable writes (this
+is intended for use by the ORB memory manager when the memory is
+freed).
+
+The "immutable" attribute may also be used on members of a struct.
+
+1.3 Asynchronous methods
+========================
+
+Most of the semantics are the same for asynchronous methods; however, the
+points at which the method begins and ends are less clear.  As far as the
+ORB is concerned, an async method begins when it processes the message. 
+When invoking an async method, there should be a mechanism to get a handle
+to track the progress of the invocation.  This can be used by the caller
+to try to cancel the method on a timeout, which can only be done if the
+message has not yet been accepted by the recipient.  Once the message has
+been accepted, in the in-process, non-"copy" case, the caller must not
+touch the data after this point until it receives a message from the
+callee that it is finished.  In the out-of-process case, if a caller loses
+patience with the callee, it can free the memory (thus making it exist
+only in the callee's address space, non-shared).
+
+1.4 Out parameters
+==================
+
+When a struct or array is being returned via an out or inout parameter,
+there is no end of method on which to base reference lifetime.  As such,
+if neither "shared" nor "inline" is specified, an out parameter is treated
+as "push".  The default of "push" only applies to the out half of an inout
+parameter; in general, use of inout should be probably limited to value
+types and parameters that use "push" in both directions so as to avoid
+potentially confusing semantics.
+
+To return static data that does not need to be freed, out parameters can
+use the "copy" implementation attribute.  The interface semantics will
+still be "push", but the ORB (or a wrapper function for in-process calls)
+will allocate a pushable buffer and copy the data into it.  If the static
+data is managed by the ORB memory manager, it will reference count the
+page rather than make a copy if the buffer is of sufficient size.
+
+1.5 Exceptions
+==============
+
+Exceptions are thrown as copy/push out parameters.  This will often mean
+unnecessary copies at in-process IDL method boundaries, but exceptions
+should be small and infrequently thrown, and usually not managed by the
+ORB memory manager except across method boundaries.
+
+1.6 Unmet needs
+===============
+
+It is currently impossible to specify attributes (other than "immutable"
+and "inline") on individual struct members.  This would be useful to pass
+a struct normally that contains a status code or other metadata along with
+a pushed or shared buffer, without making the outer struct also pushed or
+shared.  It would also be useful to pass a pushed buffer through some
+fixed superstruct such as a NotifierInfo.
+
+2. The ORB Memory Manager (ORBMM)
+=================================
+
+The ORB memory manager keeps track of memory allocated by the ORB during
+an out-of-process method invocation.  It is also used for allocations made
+by user code for memory that may need to be freed by another component in
+the same address space (such as when using the shared or push attributes).
+
+A reference count is kept on each page, so that shared-within-a-process
+mappings must be released by both caller and callee before the memory is
+freed.  Passing a chunk of data through a "shared" parameter to in-process
+method increments the page's reference count; this requires a memory
+manager lookup for every such parameter.  Because of this, consider using
+"push" instead if sharing is not required.
+
+In the out-of-process case, each mapping is freed separately, and the
+kernel handles reference counting the physical page.
+
+2.1 Methods
+===========
+
+Each language binding shall provide a mechanism by which code may call the
+following functions on a given type of array or struct.  The prototypes
+are for C++; other language bindings express these methods in whatever
+form is most appropriate.  In C++, the ORB memory manager is at
+System::RunTime::orbmm, which is a pointer to an instance of
+System::RunTime::ORBMM.
+
+2.1.1 alloc
+===========
+
+void *ORBMM::alloc(size_t size, ORBMM::AllocGroup *group = NULL);
+
+Allocate the memory required for the given type (and the given array size,
+if the type is an array).  A group handle may be passed; if not, no page
+will contain more than one allocation.  The reference count on the page is
+incremented if a page has been reused and per-object refcounts are not
+supported; otherwise, the object's reference count is one.  If the
+allocation spans multiple pages, it will be tracked as an "object", so
+that each page will have its reference count incremented and/or
+decremented when appropriate.
+
+The implementation may, but is not required to, track reference counts on
+a per-page basis rather than per-object.  The former will generally be
+more efficient, but will preclude the reuse of an object's memory upon
+release until the entire page is released.
+
+Alternative forms:
+Type *obj = new(orbmm) Type;
+Type *obj = new(orbmm) Type[];
+Type *obj = new(orbmm, group) Type;
+Type *obj = new(orbmm, group) Type[];
+
+2.1.2 retain
+============
+
+void ORBMM::retain(Region region);
+
+Increment the reference count on the specified object.  
+
+The region must refer to only one ORBMM object; the implementation may,
+but is not required to, throw an exception if this rule is violated.  If a
+region smaller than the object is retained, it will not prevent other
+pages in the region from being freed.
+
+2.1.3 release
+=============
+
+void ORBMM::release(Region region);
+
+Decrement the reference count on the specified object, freeing it if it
+reaches zero.  
+
+It is allowed, but not required, that space in multi-object groups be
+reused when freed, if the same group is used to allocate new objects. 
+This is only possible if reference counts are kept on a per-object basis
+rather than per-page.
+
+The region must refer to only one ORBMM object; the implementation may,
+but is not required to, throw an exception if this rule is violated.  If a
+region smaller than the object is released resulting in a reference count
+of zero, portions may be freed prior to the rest of the region's reference
+count reaching zero.
+
+2.1.4 super_retain
+==================
+
+void ORBMM::super_retain(Region region);
+
+Increment the reference and super-reference counts on the specified
+object.  If the reference count ever goes below the super-reference count,
+an exception is thrown.  This mechanism is intended to ease debugging
+reference count problems, by turning memory corruption into an exception. 
+
+It would typically be used when a given object is not intended to be
+released until the program exits (or some well-defined cleanup procedure
+is done), such as program and module code and static data.  It should also
+be used when a mapping is created using mmap() or other higher-level
+function, so as to be able to detect if such a reference is released
+through release() rather than through the high-level mechanism.
+
+The region must refer to only one ORBMM object; the implementation may,
+but is not required to, throw an exception if this rule is violated. 
+
+2.1.5 super_release
+===================
+
+void ORBMM::super_release(Region region);
+
+Decrement the reference and super-reference counts on the given object.
+
+The region must refer to only one ORBMM object; the implementation may,
+but is not required to, throw an exception if this rule is violated. 
+
+2.1.6 create_group
+==================
+
+ORBMM::AllocGroup *ORBMM::create_group();
+
+Create a group handle that can be passed to the alloc function to pack
+multiple allocations into the same page(s).
+
+2.1.7 destroy_group
+===================
+
+void ORBMM::destroy_group(ORBMM::AllocGroup *group);
+
+Free the memory associated with the group handle returned by create_group.
+The allocations made under the group are unaffected, and must be released
+separately.
+
+2.1.8 add_region
+================
+
+void *ORBMM::add_region(System::Mem::Region region);
+
+The ORB memory manager can manage reference counts of pages that were not
+allocated using ORBMM.  This can be used to refcount non-anonymous
+mappings (and thus make them usable with parameters that require ORBMM
+memory).  It can also be used on static pages that will never be freed
+until the program exits.
+
+The add_region method places a non-ORBMM controlled region under ORBMM
+control.  The ORBMM may use the existing mapping, or it may remap the
+pages into its own region of the virtual address space.  The address that
+it uses will be returned.  The entire region will be one ORBMM object.
+
+Upon a reference count of zero, the pages will be unmapped using
+System.AddrSpace.unmap().
diff --git a/doc/orb/parameter-info-block b/doc/orb/parameter-info-block
new file mode 100644 (file)
index 0000000..9438d1d
--- /dev/null
@@ -0,0 +1,67 @@
+Parameter Info Block (PIB), all offsets in pointer-length words
+   Name          Offset  Meaning
+   buffer_size   0       Size of the destination buffer
+   
+      The total number of bytes in all of the segments that require a
+      buffer to be created in the destination address space.  This is
+      specified so that the kernel can allocate one large buffer for all
+      segments before traversing the segment list.  When returning from a
+      method, the buffer size only includes buffers allocated by the
+      caller; "inout" segments where the caller specified a non-NULL ptr,
+      and the callee did not increase the length, are not included
+      (because the kernel does not need to allocate a caller-side buffer
+      for them).
+                 
+   objlist_ptr   1       Pointer to the object list
+   objlist_len   2       Length of the object list
+
+      The object list is a list of pointers into segment data describing
+      where object IDs can be found.  When copying a segment to the
+      destination address space, it will convert all IDs (allocating a new
+      ID if necessary).  The object list must be in order (first by
+      segment, then by address); an exception may be thrown if it is out
+      of order or if it contains invalid entries.  Segments with object
+      IDs cannot have the Shared flag.  Unmarshalling code should always
+      verify that any ID it expects is actually in the object list.
+
+       ptrlist_ptr   3       Pointer to the pointer list
+       ptrlist_len   4       Length of the pointer list
+       
+          The pointer list, like the object list, is a list of pointers to
+          segment data.  Each pointer pointed to must also point within
+          segment data, and will be modified by the ORB when copied to point
+          to the equivalent location in the destination address space.  The
+          pointer list must be in order (first by segment, then by address);
+          an exception may be thrown if it is out of order of if it contains
+          invalid entries.  Segments with internal pointers cannot have the
+          Shared flag (shared segments can still be pointed to, of course). 
+          Unmarshalling code should always verify that any internal pointer it
+          expects actually points within a valid segment.
+
+   num_segments  5       Number of data segments
+   segment.ptr   6+n*3   Pointer to data segment
+   segment.len   7+n*3   Length of data segment in bytes
+   segment.flags 8+n*3   Attributes of data segment
+   
+      Each segment describes data being transmitted to and/or from the
+      callee.  For out segments, the caller may designate a buffer to hold
+      the data, or it may leave the ptr field NULL.  The caller may
+      replace an out segment pointer with its own (it must do this if it
+      was NULL), and it may change the length of the segment.  Except when
+      flags such as Push or Shared force the kernel to map, rather than
+      copy, the data, it will choose which method to use based on the
+      size, page-alignment, and overmap status of the segment.
+
+Segment Flags (see doc/orb/memory-management for more details):
+   In            0x01    Data is copied/mapped from caller to callee
+   Out           0x02    Data is copied/mapped from callee to caller
+   Shared        0x04    A permanent shared mapping is created
+   Push          0x08    The region is unmapped from the source and
+                         transferred to the destination.
+   Inline        0x10    The callee cannot change the length of an
+                         Out segment.  Ignored for In segments.
+   Immutable     0x20    The segment is to be mapped read-only in
+                         the destination.
+   Copy        0x8000    The segment is permanently copied into the
+                         destination address space, with read/write
+                         access (unless Immutable is set).
diff --git a/idl/Makefile b/idl/Makefile
new file mode 100644 (file)
index 0000000..86aae6d
--- /dev/null
@@ -0,0 +1,20 @@
+TOP := $(shell dirname `pwd -P`)
+COMP := idl
+BUILDTYPE := build
+NODEPS := y
+ARCHINDEP := y
+include ../Makefile.head
+
+IDL := $(shell find . -name '*.idl')
+
+TARGETS := $(BUILDDIR)/ifaces
+
+$(BUILDDIR)/ifaces: $(IDL) $(IDLC)
+       @echo $(COMP): System IDL files
+       @$(RMDIR) $(BUILDDIR)/ifaces
+       @if ! $(IDLC) -o $(BUILDDIR)/ifaces -s System $(IDL); then \
+               $(RMDIR) "$@"; \
+               false; \
+       fi
+
+include ../Makefile.tail
diff --git a/idl/addrspace.idl b/idl/addrspace.idl
new file mode 100644 (file)
index 0000000..14f4ae2
--- /dev/null
@@ -0,0 +1,336 @@
+// AddrSpace
+//
+// These are the interfaces through which operations are performed on
+// virtual and physical address spaces.
+//
+namespace Mem;
+
+// There are two main kinds of mappable objects:
+// 1. Objects that can be mapped directly into physical address space. 
+//    This includes memory, device I/O, nested AddrSpaces, etc.  Objects
+//    with this capability implement the Mappable interface.
+// 2. Objects that are mapped by copying the content into RAM
+//    (disks, remote objects, dynamically generated data, etc.)
+//    These objects implement the Cacheable interface.
+//  
+// Type #1 is simple; all it needs to do is translate mappable offsets
+// into physical.  Type #2 requires an intervening Cache to manage the
+// backing RAM store.  Memory-like type #1 objects should also implement
+// Cacheable so that they can be used remotely.
+//
+// Address spaces may be stacked, such that (for example) a debugger maps
+// an application which maps a file which maps a filesystem which maps a
+// RAID volume which maps several physical disks; only the disks would be
+// represented by Caches, everything else by AddrSpaces.  Changes at any
+// level immediately (well, before the operation finishes) propagate up
+// to the page tables higher levels, and clone() will work at any level
+// (and can make either the old or new address space the anonymous-
+// memory-using "shadow" object).  Stacked address spaces can also be
+// used to implement access control.
+
+struct Region inline {
+       // Both bounds are inclusive
+       ulong start, end;
+};
+
+struct RegionWithOffset inline : Region {
+       ulong offset;
+};
+
+bitfield AccessFlags:3 {
+       // Allow reads to this page.  On some platforms, this is assumed
+       // if writes are allowed, due to hardware limitations.
+       Read,
+               
+       // Allow writes to this page.
+       Write,
+       
+       // Allow execution of code from this page.  On some platforms,
+       // this is assumed if reads are allowed, due to hardware
+       // limitations.
+       Exec
+};
+
+interface BlockObject {
+       guid: "4425AF91-52BE-11DA-BD60-000A95BB581A";
+
+       // Returns the current size in blocks of the object.
+
+       get_size(ulong size out);
+       
+       // Returns the object's block size in bytes.  Requests to map, read,
+       // write, etc. this object must be done with this granularity.  The
+       // block size must be a power of two.
+       
+       get_block_size(ulong block_size out);
+};
+   
+// Finer grained mapping than the block size may be done via the virtual
+// address space, but the AddrSpace will always request object->physical
+// mappings that are a multiple of the block size.
+
+interface Mappable : BlockObject {
+       guid: "B8E7A0DF-EAB6-11D9-BEFB-000A95BB581A";
+       
+};
+
+interface Cacheable : BlockObject {
+       guid: "BB4C729D-EAB6-11D9-ADA2-000A95BB581A";
+
+       // Add the pages in the specified region to the specified cache.
+       // The start and end of the region will be aligned to the larger
+       // of the page size and the block size.
+
+       fill(Cache cache, Region region) async;
+};
+
+// This is the Mappable used to map a Cacheable object.  
+// Calling get_subspace on a Cache returns a pure Mappable; holders of
+// such a reference alone can only map it, not add or remove pages or
+// change its cacheable.
+
+interface Cache : Mappable {
+       guid: "BD9A04F5-EAB6-11D9-A420-000A95BB581A";
+
+       // Get and set the object mapped through this Cache.  Any other
+       // methods when obj is null (or never set) throw an InvalidState
+       // exception.  Calling set_cacheable while this Cache has any
+       // current mappings throws an InvalidState exception.
+
+       set_cacheable(Cacheable obj);
+       get_cacheable(Cacheable obj out);
+       
+       // Add one or more pages to the cache.  Addr must be page-aligned, and
+       // the size of buf must be a multiple of the page size.
+       
+       fill(ulong addr, octet[] buf push) async;
+       
+       // Add one or more zero-filled pages to the cache.  The region must
+       // start and end on a page boundary.
+       
+       fill_zero(Region region) async;
+       
+       // Remove one or more pages from the cache.  The region must start and
+       // end on a page boundary.  It is not an error to remove pages that are
+       // not in the cache.
+       
+       remove(Region region);
+       
+       // Wait until all of the pages in the region have been added to the
+       // cache.  It is the caller's responsibility to ensure that the adding
+       // has been requested, and that blocking will not cause a deadlock. 
+       // If there are multiple pages in the region, it is as if this method
+       // were called sequentially on each page; the kernel will not check
+       // whether previously checked pages have been removed while waiting
+       // for later pages.
+       
+       block_on_region(Region region);
+       
+       // Like block_on_region, but returning a blocker suitable for use
+       // with Thread.block_multi.
+       
+       region_blocker(Region region, Proc.Blocker blocker out);
+};
+
+bitfield AllocFlags {
+       Zero,          // Zero out any freshly allocated memory.
+
+       Insecure,      // It is not necessary to zero out the page after it
+                      // is freed, unless the next allocator requests it.
+
+       Commit,        // Commit the allocation to actual memory or
+                      // swap, failing if this cannot be done.
+
+       Lock,          // Only allocate actual memory, and lock it against
+                      // swapping.  Fails if not enough actual RAM is
+                      // available (either in general or in the caller's
+                      // locked RAM quota).  If Lock is set, Commit is
+                      // ignored.
+       
+       NoZeroLocal,   // Accept secure pages from the current address
+                      // space without first zeroing.  Ignored if Zero is
+                      // specified.  Overmap should be set to None when
+                      // this is used, and every byte should be
+                      // overwritten before data in any such pages is
+                      // passed to another address space.
+
+       Paranoid,      // Zero pages even when going to a NoZeroLocal allocator
+                      // in the current address space.  Use this for pages which
+                      // are particularly likely to contain sensitive data.
+};
+
+bitfield MapFlags {
+       Fixed,         // Fail if the exact starting address is unavailable. 
+                      // Otherwise, if the supplied starting address
+                      // unavailable, the address space manager allocates a
+                      // free region and returns it in "start".  If this
+                      // behavior is explicitly desired (as it ususally is),
+                      // "start" should contain all bits set, which is always
+                      // invalid.  For non-process (stacked) address spaces,
+                      // this flag is treated as always set.
+       
+       Replace,       // Atomically replace any existing mapping with the new
+                      // mapping, rather than fail.  This flag is only
+                      // meaningful if Fixed is set.
+
+       CopyOnWrite,   // Share the mapped object only until it is written to;
+                      // then, before the write takes place, copy the object. 
+                      // It is undefined whether this mapping will receive
+                      // the copy or the original.  Ignored for alloc_and_map.
+       
+       AccessFlags access:3,
+                      // These are the requested read/write/execute
+                      // permissions on the mapping.  A missing permission (or
+                      // unmapped page) at any level in an address space stack
+                      // will cause a MemoryFault, even if the page is mapped
+                      // with the needed permissions at the top level.  The
+                      // map() call will not fail due to such a condition.
+
+       enum OverMap:2 {
+               None,       // Never map non-argument memory to a callee's address
+                           // space.
+
+               ReadOnly,   // Allow read-only mappings of non-argument data within
+                           // the same page as an argument into the callee's
+                           // address space.
+
+               ReadWrite   // Allow all mappings of non-argument data within the
+                           // same page as an argument into the callee's address
+                           // space.
+       } overmap
+};
+
+interface AddrSpace {
+       guid: "BF9D2070-EAB6-11D9-A2D2-000A95BB581A";
+
+       const ulong unspecified_start = 0xffffffffffffffff;
+       
+       // Return the mappable associated with this address space, which can
+       // be used to allow another address space to stack with this one.
+       // The mappable handle only allows pages to be mapped; it does not
+       // allow any changes to the mappings, nor can a handle to the AddrSpace
+       // be obtained from it.  This method must always return the same
+       // Mappable object when called on the same AddrSpace.
+       
+       get_mappable(Mappable ma out);
+       
+       // Create a new AddrSpace that is a copy-on-write clone of this AddrSpace.
+       // This method is used to implement fork() and in-memory file versioning,
+       // and could also be used to assist garbage collection and other purposes.
+       //
+       // By default, the old address space continues to be backed by
+       // whatever Mappables were in use, and pages in the new address space
+       // are backed by anonymous memory when a page in either is written to. 
+       // If old_space_is_anon is true, though, this is reversed, which is useful
+       // when versioning a file to make the new version the one that gets
+       // stored to disk.
+
+       clone(AddrSpace addrspace out, bool clone_is_real);
+       
+       // Create an anonymous RAM mapping.     
+
+       alloc_and_map(ulong len, ulong vstart inout, 
+                     AllocFlags aflags, MapFlags mflags);
+       
+       // Mappable must be implemented by the local kernel, and must hold
+       // read/write/exec permissions appropriate for the MapFlags given.
+       
+       map(Mappable ma, Region region, ulong vstart inout, MapFlags flags);
+       unmap(Region region);
+
+       // Set the flags on all pages in the region.  CopyOnWrite can be
+       // set, but not cleared, using this method.  Fixed is ignored.
+
+       set_mapflags(Region region, MapFlags flags);
+       
+       // Returns the flags on the given region, if all pages have the
+       // same flags (except for CopyOnWrite, which is not returned by
+       // this method, as it can be asynchronously cleared).
+       
+       get_mapflags(Region region, MapFlags flags out, bool all_same out);
+       
+       // Returns the Mappable that covers the specified range, and the offset
+       // into the Mappable that corresponds to the first page in the region. 
+       // If any pages within the range are not mapped, or if more than one
+       // Mappable is mapped within the region, or if the offsets into the
+       // Mappable are not contiguous, then NULL is returned in ma.
+       //
+       // This is used to implement mremap().
+       
+       get_mapping(Region region, Mappable ma out, ulong offset out);
+
+       // Returns the minimum page size (and thus mapping size/alignment)
+       // supported in this address space.  An attempt to create a mapping
+       // that violates this will result in an InvalidArgument exception.
+
+       get_page_size(uint page_size out);
+
+       // Returns the minimum alignment supported for mapping requests;
+       // (vstart % min_align) must equal (region.start % min_align).  This
+       // is at least the minimum page size, but may be more on certain
+       // hardware, such as virtually-indexed-physically-tagged caches, where
+       // larger alignment is needed to ensure the absence of cache aliases. 
+       // An attempt to create a mapping that violates this will result in an
+       // InvalidArgument exception.
+       
+       get_min_align(uint min_align out);
+};
+
+interface AllocHandle {
+       guid: "CB029266-EAB6-11D9-BCA0-000A95BB581A";
+
+       get_regions(Region[] regions out);
+       
+       // Free a portion of an allocation.  To free all of an allocation,
+       // simply release all references to the handle.  Each region shall
+       // start and end on block size boundaries.  Throws OperationNotSupported
+       // if the allocator does not support partial frees.
+       
+       free(Region[] regions);
+};
+
+interface Allocator {
+       guid: "CCF5D83C-EAB6-11D9-8BB7-000A95BB581A";
+
+       const ulong unspecified_start = 0xffffffffffffffff;
+
+       bitfield AllocFlags {
+               // If set, fail if the exact starting address is unavailable (or if
+               // the allocator does not support caller-supplied starting
+               // addresses).
+               //
+               // Otherwise, if the supplied starting address unavailable, the
+               // allocator allocates a free region and returns it in "start".  If
+               // this behavior is explicitly desired (as it ususally is), "start"
+               // should be set to unspecified_start, which is always invalid. 
+               // Using unspecified_start may be faster than specifying other
+               // invalid addresses (as the allocator can check for this value
+               // rather than checking address availability), and zero should not
+               // be used for this purpose as it may be a valid address.
+               
+               Fixed
+       };
+
+       alloc(ulong start inout, ulong len, AllocFlags flags,
+             AllocHandle handle out);
+};
+
+interface GenericAllocator : Allocator {
+       guid: "D033DD3A-EAB6-11D9-96E4-000A95BB581A";
+
+       // Set the minimal block size used by the allocator.
+       // This can only be done before any alloc() or
+       // add_regions() calls; an InvalidState exception may
+       // be thrown otherwise.
+       
+       set_block_size(ulong block_size);
+       
+       // Make one or more regions available for allocations.
+       // The regions may not overlap each other or any existing
+       // regions (whether or not allocated).  Regions may not
+       // be removed once added using this interface; allocators
+       // may optionally provide a mechanism for doing so.
+       
+       add_regions(Region[] regions);
+};
diff --git a/idl/events.idl b/idl/events.idl
new file mode 100644 (file)
index 0000000..30dc0ad
--- /dev/null
@@ -0,0 +1,54 @@
+namespace Events;
+using Notifiers.*;
+
+// A plain Event can be passed to the event source 
+// if no action should be taken.
+
+struct Event virtual {
+       guid: "24585852-2877-11DA-9148-00112431A05E";
+};
+
+struct NotifierEvent : Event {
+       guid: "2FDEB883-2877-11DA-BBC9-00112431A05E";
+       
+       Notifier target;
+       VStruct info;
+};
+
+struct SyncNotifierEvent : Event {
+       guid: "D1F6CC34-2877-11DA-9481-00112431A05E";
+
+       SyncNotifier target;
+       VStruct info;
+};
+
+struct EventInfo virtual {
+       guid: "58FB107A-3693-11DA-B25C-000A95BB581A";
+       
+       VStruct dynamic_info;
+};
+
+struct TrapEvent : Event {
+       guid: "D4DB748C-2877-11DA-8846-00112431A05E";
+
+       Traps.Trappable target;
+       Traps.Trap trap;
+};
+
+// If a trap event has a trap of this base type,
+// dynamic_info will be filled in with the info passed
+// to the trigger.  Otherwise, the dynamic info
+// will be dropped.
+
+struct EventTrap : Traps.Trap {
+       guid: "F884DC14-36D6-11DA-BAEE-000A95BB581A";
+
+       VStruct dynamic_info;
+};
+
+interface EventDispatcher {
+       guid: "8598ADAE-35E9-11DA-A310-000A95BB581A";
+       
+       setup_trigger(Event event, Notifier trigger out);
+       setup_sync_trigger(Event event, SyncNotifier trigger out);
+};
diff --git a/idl/exceptions.idl b/idl/exceptions.idl
new file mode 100644 (file)
index 0000000..17225bd
--- /dev/null
@@ -0,0 +1,289 @@
+// System exceptions
+//
+// These are all of the exceptions that derive from SystemException, 
+// which can implicitly be thrown from any method.
+//
+// Common fields:
+//   exp: explanation string, may be NULL
+//   param: index (starting from 0) of the parameter which is causing
+//          the problem, or -1 if unspecified.  This should only be
+//          specified for parameters of IDL methods; if thrown from a
+//          language function/method which does not know the relevant
+//          parameter in the IDL method, it should use an explanation
+//          string instead (and not use the index of its own parameter).
+
+namespace Exceptions;
+
+// The base Exception struct; all exceptions derive from this.
+struct Exception virtual {
+       guid: "D88F50D2-2877-11DA-84AD-00112431A05E";
+
+       // The previous exception frame (i.e. that which threw this one),
+       // or null for the original exception frame.  Linkage shall be
+       // preserved whenever an exception is either propagated or
+       // raised within an exception handling block.
+          
+       Exception prev;
+
+       // Object and method of the thrower of this frame, if it was rethrown
+       // due to crossing an IDL method boundary.  Otherwise, both fields are
+       // null.
+          
+       Object object;
+//     Method method; // Fix throw_idl when this is uncommented.
+       
+       // Language and machine specific information about whence an exception
+       // was thrown.
+       
+       ExceptionOriginInfo origin;
+};
+
+struct ExceptionOriginInfo virtual {
+       guid: "DBCF8333-2877-11DA-B66E-00112431A05E";
+};
+
+// This struct, or one derived from it, shall be used for an
+// exception's origin field if it is from native code (and
+// thus can refer to instructions by machine address).
+   
+struct NativeCodeExceptionOriginInfo : ExceptionOriginInfo {
+       guid: "E1B3FAC6-2877-11DA-8493-00112431A05E";
+
+       // Address of the faulting or throwing instruction
+       ulong pc;
+};
+
+namespace Std;
+
+struct SystemException : Exception {
+       guid: "E5204D31-2877-11DA-A833-00112431A05E";
+       
+       bitfield Flags:32 {
+               // Exception was thrown by the kernel.  This bit is stripped when
+               // the exception passes through the ORB.  It can be used to verify
+               // that MemoryFault and similar exceptions refer to an actual fault
+               // of the current address space when deciding whether to abort the
+               // process.
+
+               Kernel,
+               
+               // Exception was generated automatically by the ORB, rather than
+               // thrown by a method.  Some types of exceptions have fixed
+               // explanation string formats (and possibly other field usages) for
+               // ORB generated exceptions, but a free form explanation field for
+               // method-thrown exceptions.
+               
+               ORB
+       } flags;
+};
+
+
+// The MemoryFault exception indicates that the throwing method
+// attempted to perform an invalid memory access, and the MemoryFault
+// trap was not handled by the faulting process.
+
+struct MemoryFault : SystemException {
+       guid: "EAA266EC-2877-11DA-9C5D-00112431A05E";
+
+       // Address that the method tried to access
+       ulong addr;             
+
+       // Address of the faulting instruction
+       ulong pc;               
+
+       // Process of faulting method
+       Proc.Process proc;
+
+       // Architecture specific data
+       ArchMemoryFault arch;   
+
+       enum Type {
+               // The fault occured on a memory load
+               Load,
+               
+               // The fault occured on a memory store
+               Store,
+               
+               // The fault occured on an instruction fetch
+               IFetch
+       } type;
+       
+       enum Cause {
+               // The faulting address was not mapped
+               Unmapped,       
+               
+               // The requested operation was prohibited
+               Protected,      
+               
+               // An I/O error occured accessing a memory mapped region, or an
+               // uncorrectable memory error was encountered.
+               IOError         
+       };
+};
+
+struct ArchMemoryFault virtual
+{
+       guid: "EF25EED2-2877-11DA-83EF-00112431A05E";
+};
+
+// The OutOfMemory exception indicates that insufficient physical memory
+// is available to perform the requested operation, including implicit
+// allocations of memory such as memory mapped accesses, shared copy on
+// write regions, and zero pages (though implicit allocation failures
+// cannot happen unless overcommit is enabled).  This exception is only
+// thrown after a System.Traps.ReduceMemoryUsage trap has been delivered
+// and failed to free sufficient memory.
+
+struct OutOfMemory : SystemException {
+       guid: "F21A198C-2877-11DA-92DB-00112431A05E";
+};
+
+// Like OutOfMemory, but indicates that disk space, virtual
+// address space, etc. has been exhausted.
+
+struct OutOfSpace : SystemException {
+       guid: "6267B5C2-D2E4-11DA-831C-00112431A05E";
+       char[] exp immutable;
+};
+
+// Like OutOfSpace, but indicates that a specific requested resource
+// (such as an IRQ number or network port) is already in use.  Use
+// OutOfSpace instead for resources that are allocated rather than
+// claimed.
+
+struct ResourceBusy : SystemException {
+       guid: "0EFB88FC-2878-11DA-AEB9-00112431A05E";
+       
+       int param;  // Index of parameter with problem, or -1 if
+                   // unspecified, or if not due to a bad param.
+       char[] exp immutable;
+};
+
+/* The InstrFault exception indicates that the throwing method tried
+   to execute an invalid or privileged instruction, and the
+   InstrFault trap was not handled by the faulting process. */
+
+struct InstrFault : SystemException {
+       guid: "F5CAE5F8-2877-11DA-BB6C-00112431A05E";
+
+       // Address of the faulting instruction
+       ulong pc;
+
+       // Process of faulting method
+       Proc.Process proc;
+
+       // Architecture specific data
+       ArchInstrFault arch;
+};
+
+struct ArchInstrFault virtual
+{
+       guid: "F9CE2A55-2877-11DA-9C92-00112431A05E";
+};
+
+struct Token {
+};
+
+// The TokenFault exception indicates that the caller does not
+// currently have the necessary token to complete the requested method.
+
+struct TokenFault : SystemException {
+       guid: "011B018D-2878-11DA-9F80-00112431A05E";
+
+       Token token;     // Token that is required but not present
+
+       enum Type {
+               Thrown,      // The TokenFault was explicitly thrown by a 
+                            // called method
+               Call,        // The token was required for a method call
+               Open,        // The token was required to obtain a handle on
+                            // the object
+       } type;
+
+       bitfield Flags:32 {
+               restartable:1   // The method may be restarted if the requested
+                               // token is acquired.  Any TokenFaults with this
+                               // flag set are handled in a transparent manner,
+                               // by either acquiring the token and retrying or
+                               // clearing the bit and propagating.  Any method
+                               // that throws a restartable TokenFault must
+                               // be cleanly restartable with no side effects.
+       };
+
+       // For Call and Open, this is the object associated with the prohibited
+       // operation.  Explicitly thrown token faults may optionally use this
+       // field.
+       
+       Object object;
+
+       // For Call, this is the method that could not be called.  Explicitly
+       // thrown token faults may optionally use this field.
+
+//     Method method; // Fix throw_idl when this is uncommented.
+
+       char[] exp immutable;
+};
+
+// InvalidArgument may be thrown when an argument or combination
+// of arguments is invalid.
+
+struct InvalidArgument : SystemException {
+       guid: "04865176-2878-11DA-9F87-00112431A05E";
+
+       int param;  // Index of problematic parameter, counting from 0,
+                   // or -1 if not specified.
+       char[] exp immutable;
+};
+
+// GeneralFailure, as the name implies, is a general purpose exception
+// to indicate failure conditions not covered by existing exceptions.
+// It is generally preferable to define a specific exception for
+// forseeable causes of failure.
+
+struct GeneralFailure : SystemException {
+       guid: "094C37A1-2878-11DA-914D-00112431A05E";
+       
+       char[] exp immutable;
+};
+
+// InvalidState is thrown when a method is called on an object which
+// is not in a state in which the operation is meaningful.
+
+struct InvalidState : SystemException {
+       guid: "0C361FFF-2878-11DA-ABC1-00112431A05E";
+       
+       char[] exp immutable;
+};
+
+
+struct OperationNotSupported : SystemException {
+       guid: "119C274E-2878-11DA-B3B2-00112431A05E";
+       
+       char[] exp immutable;
+};
+
+// InvalidReference is thrown when a method is called on a reference
+// that no longer points to a valid object, or if such a dangling
+// reference is found in the method's parameters.
+
+struct InvalidReference : SystemException {
+       guid: "16E5E64F-2878-11DA-916E-00112431A05E";
+
+       int param;  // Index of parameter with problem, or -1 if the method
+                   // was invoked on a dangling reference.  If the invalid
+                   // reference is in the object's internal state, then
+                   // InvalidState should be thrown instead.
+                          
+       char[] exp immutable; 
+                   // When thrown by the ORB, NULL if param is -1 or if the
+                   // dangling reference was passed directly as a parameter. 
+                   // If, however, the dangling reference was in a struct,
+                   // this provides the path to the bad parameter (e.g. if
+                   // the third parameter of a method is a struct with a
+                   // member "foo", and "foo" is a struct with a member
+                   // "bar", and bar is a dangling object reference, exp will
+                   // contain "foo.bar").
+                   //
+                   // When not thrown by the ORB, this is a free form
+                   // explanation field (and may be NULL).
+};
diff --git a/idl/io.idl b/idl/io.idl
new file mode 100644 (file)
index 0000000..4ebec6f
--- /dev/null
@@ -0,0 +1,88 @@
+namespace IO;
+using Notifiers.Notifier;
+
+struct IONotifierInfo virtual {
+       guid: "E38D4FC2-36C7-11DA-B2D1-000A95BB581A";
+
+       ulong len;
+       enum Result {
+               Success,
+               NoMoreData,
+               NoMoreSpace,
+               IOError,
+               BadHandle,
+               NoEndpoint
+       } result;
+};
+
+interface IStream {
+       guid: "6A2E42D6-EAB6-11D9-AD6A-000A95BB581A";
+
+       read(octet[] buf out, ulong len inout);
+       read_async(octet[] buf shared, ulong len, Notifier notifier) async;
+};
+
+interface OStream {
+       guid: "76004CA4-EAB6-11D9-A808-000A95BB581A";
+
+       write(octet[] buf, ulong len inout);
+       write_async(octet[] buf, ulong len, Notifier notifier) async;
+};
+
+interface IOStream : IStream, OStream {
+       guid: "76621658-EAB6-11D9-8884-000A95BB581A";
+};
+
+interface File {       
+       guid: "76C2638F-EAB6-11D9-BE5C-000A95BB581A";
+
+       size(ulong size out);
+};
+
+interface ReadableFile : File {
+       guid: "4B46E7A0-E66B-49F5-BB8A-D63833A4D79A";
+
+       read(ulong pos, octet[] buf out, ulong len inout);
+       read_async(ulong pos, octet[] buf shared, 
+                  ulong len, Notifier notifier) async;
+};
+
+interface WriteableFile : File {
+       guid: "310C44B2-D5F8-4439-A7DB-0BDBFD0C306C";
+
+       write(ulong pos, octet[] buf, ulong len inout);
+       write_async(ulong pos, octet[] buf, 
+                   ulong len, Notifier notifier) async;
+};
+
+interface ReadWriteFile : ReadableFile, WriteableFile {
+       guid: "61E259EF-A929-449C-A8B8-1870A744F160";
+};
+
+interface HasFile {
+       guid: "540020D6-23AC-4061-9CD7-EEC4118BBAAC";
+
+       set_file(File f);
+       get_file(File f out);
+};
+
+interface Seekable {
+       guid: "772A2170-EAB6-11D9-BAA4-000A95BB581A";
+
+       enum SeekType {
+               FromBeginning,
+               FromCurrent,
+               FromEnd
+       };
+
+       // If the backing file is changed, the position resets to the
+       // beginning.
+       //
+       // The offset can be considered unsigned if access to the full 2^64
+       // bytes of the underlying file is required.  Overflow and underflow
+       // cause wraparound.
+
+       seek(long offset, SeekType type);
+       get_pos(ulong offset out);
+       size(ulong size out);
+};
diff --git a/idl/io/bus/generic.idl b/idl/io/bus/generic.idl
new file mode 100644 (file)
index 0000000..0c5c9ba
--- /dev/null
@@ -0,0 +1,45 @@
+namespace IO.Bus;
+
+struct Resource {      
+       Mem.Region region;
+       ResourceSpace rspace;
+       char[] description;
+};
+
+// Whether the alloc() and/or dev_alloc() methods are accepted by a given
+// ResourceSpace is defined by the particular subtype.
+
+interface ResourceSpace : Mem.Allocator {
+       guid: "2515CEC8-F7E0-11D9-9DF4-000A95BB581A";
+
+       dev_alloc(ulong start inout, ulong len, Mem.Allocator.AllocFlags flags,
+                 Device dev, Mem.AllocHandle handle out);
+
+       get_resources(Resource[] res out);
+       get_parent(Resource parent out);
+       get_bus(Bus bus out);
+       get_devices(Device[] devs out);
+       get_description(char[] desc out);
+};
+
+interface Device {
+       guid: "1F19CCF2-F7E0-11D9-98CE-000A95BB581A";
+
+       get_resources(Resource[] res out);
+       get_parent(Bus bus out);
+       get_description(char[] desc out);
+};
+
+interface Bus : Device {
+       guid: "196CC482-F7E0-11D9-9AED-000A95BB581A";
+       
+       get_devices(Device[] devs out);
+       get_resource_spaces(ResourceSpace[] resource_spaces out);
+
+       // Try to auto-enumerate new devices.  If the bus is not capable
+       // of auto-enumeration, do nothing.  It is up to the specific bus
+       // type as to whether any action is taken for prevously-enumerated
+       // devices that are no longer detected.
+       
+       scan();
+};
diff --git a/idl/io/bus/pci.idl b/idl/io/bus/pci.idl
new file mode 100644 (file)
index 0000000..6ada673
--- /dev/null
@@ -0,0 +1,19 @@
+namespace IO.Bus.PCI;
+
+interface PCIBus : Bus {
+       guid: "5BB5C0EC-F81D-11D9-BAD0-000A95BB581A";
+       get_interrupt_for_device(PCIDevice dev, uint irqnum out,
+                                Interrupts.InterruptController con out);
+};
+
+interface PCIDevice : Device {
+       guid: "5E2A66A0-F81D-11D9-9BFC-000A95BB581A";
+};
+
+interface HostBridge : PCIBus {
+       guid: "60649040-F81D-11D9-A520-000A95BB581A";
+};
+
+interface SubBridge : PCIBus, PCIDevice {
+       guid: "62EF072A-F81D-11D9-862B-000A95BB581A";
+};
diff --git a/idl/io/console.idl b/idl/io/console.idl
new file mode 100644 (file)
index 0000000..08bf430
--- /dev/null
@@ -0,0 +1,5 @@
+namespace IO.Console;
+
+interface Console : System.IO.IOStream {
+       guid: "889C9109-EA8B-11D9-9153-000A95BB581A";
+};
diff --git a/idl/io/interrupts.idl b/idl/io/interrupts.idl
new file mode 100644 (file)
index 0000000..6e0af09
--- /dev/null
@@ -0,0 +1,30 @@
+namespace IO.Interrupts;
+
+// alloc() and dev_alloc() will throw OperationNotSupported.
+interface InterruptController : Bus.ResourceSpace {
+       guid: "5B67CF12-F7E0-11D9-8D57-000A95BB581A";
+
+       bitfield UserIntFlags {
+       };
+
+       request_userint(uint irqnum, UserInterruptCallback callback,
+                       UserIntFlags flags, Bus.Device device,
+                       UserInterrupt handle out);
+};
+
+interface Interrupt {
+       guid: "E7E400CC-F820-11D9-908F-000A95BB581A";
+
+       get_device(Bus.Device dev out);
+       get_controller(InterruptController con out);
+       get_num(uint irqnum out);
+};
+
+interface UserInterrupt : Interrupt, Mem.AllocHandle {
+       guid: "29939E02-F7E0-11D9-B720-000A95BB581A";
+};
+
+interface UserInterruptCallback {
+       guid: "5017C289-F821-11D9-8B10-000A95BB581A";
+       action(UserInterrupt irq);
+};
diff --git a/idl/namespace.idl b/idl/namespace.idl
new file mode 100644 (file)
index 0000000..c84745a
--- /dev/null
@@ -0,0 +1,12 @@
+interface Namespace {
+       guid: "4C81F0BD-EAB6-11D9-86E1-000A95BB581A";
+
+       // This doesn't work with the current idlc.
+//     typedef char[] ns_component;
+
+       // FIXME: What about namespace members that are not objects,
+       // but rather arrays, structs, and such?
+
+       lookup_delimit(char[] name immutable, char delimiter, Object obj out);
+//     lookup_nodelimit(ns_component[] name, Object obj out);
+};
diff --git a/idl/notifier.idl b/idl/notifier.idl
new file mode 100644 (file)
index 0000000..38e74b6
--- /dev/null
@@ -0,0 +1,14 @@
+namespace Notifiers;
+
+interface Notifier {
+       guid: "AF8F1B5C-EAB6-11D9-98AF-000A95BB581A";
+
+       notify(VStruct info) async;
+};
+
+interface SyncNotifier {
+       guid: "E1AA43FC-2400-11DA-A460-00112431A05E";
+       
+       notify(VStruct info);
+};
+
diff --git a/idl/objmgr.idl b/idl/objmgr.idl
new file mode 100644 (file)
index 0000000..0be5157
--- /dev/null
@@ -0,0 +1,207 @@
+/* The Object Manager
+ *
+ * This is the kernel interface through which objects, interfaces, and
+ * interfaces are opened, created and destroyed.  The instantiated objmgr
+ * object is always id 1.
+ */ 
+
+interface Object {
+       guid: "C227980E-EA8B-11D9-84F1-000A95BB581A";
+};
+
+struct VStruct virtual {
+       guid: "E2E3AF5F-2858-11DA-9FBB-00112431A05E";
+};
+
+namespace Objects {
+       // FIXME: make this a built-in ID type that fixes things
+       // up when moving between systems.  It can be native word
+       // sized, rather than always long.
+
+       typedef ulong ID;
+
+       struct TypeDesc {
+               enum BasicType {
+                       Bool, Short, Int, Long,
+                       UShort, UInt, ULong,
+                       FShort, FLong, Octet, Enum, 
+                       Bitfield, Interface, Struct
+               } type;
+       
+               ID id;            // ID of interface, struct, bitfield, or enum (or 0).
+               ulong length;     // Type length: bits in bitfield, bytes in
+                                 //              struct, max value of enum, or 0. 
+
+               struct Array {
+                       // Inclusive lower and upper bounds of the array.  If the
+                       // array has no upper bound, upper should be -1.  If the
+                       // array has no lower bound, lower should be 0.  If this is
+                       // not an array, both should be 0.
+
+                       long lower, upper;
+               } array;
+       };
+       
+       struct ParamDesc {
+               TypeDesc type;
+       
+               enum Dir {
+                       In, Out, InOut
+               } dir;
+       
+               bitfield Attr:32 {
+                       copy:1,   /* If input argument references memory, a
+                                    copy will be made (possibly via copy-on-write)
+                                    before the method receives control.
+                                    If the parameter is inout, the changes (if any)
+                                    will be copied to the original memory area (or
+                                    the copy-on-write mapping will occur on return
+                                    from the method).  This is an implementation
+                                    attribute and is incompatible with shared. */
+       
+                       shared:1, /* If input argument references memory, the
+                                    method may continue to access the memory
+                                    until it explicitly releases it.  The memory
+                                    region must remain valid until then, and all
+                                    changes to the memory area will be shared by
+                                    both the caller and the callee.  This is an
+                                    interface attribute and is incompatible with
+                                    copy.  The argument should be page aligned
+                                    and its size must be a multiple of the page
+                                    size; otherwise, the caller will have
+                                    access to data outside the region passed. */
+               } attr;
+       };
+       
+       struct MethodDesc {
+               char[] name;
+               ulong entry;      /* Address of method entry point */
+               ParamDesc[] params;
+       
+               bitfield Flags:32 {
+                       async:1,       /* Method is invoked indirectly via message passing,
+                                         and the caller does not wait for completion.  No
+                                         out parameters may be used in such a method. */
+                       
+                       ordered:1,     /* Method requires strong ordering; all ordered methods
+                                         from the same thread will be delivered in order, and
+                                         the next such method will not be delivered until
+                                         the recipient of the previous invocation message
+                                         acknowledges it.  This flag is only valid on async
+                                         methods, as synchronous methods are inherently
+                                         ordered. 
+                                         
+                                         An alternative is to invoke methods through
+                                         a serialization object, which allows finer
+                                         control over the level of serialization.
+                                         This flag may go away in favor of always
+                                         using explicit serialization objects when
+                                         needed. */
+               } flags;
+       };
+       
+       struct ClassDesc {
+               ulong[] super;          /* superinterface(es) */
+               MethodDesc[] methods;
+               ulong instantiate, subinterface, remove;        /* access tokens */
+       };
+       
+       struct DatumDesc {
+               char[] name;
+               TypeDesc type;
+       };
+       
+       struct StructDesc {
+               DatumDesc[] data;
+       };
+
+       interface Class {
+               guid: "C30D0A85-EA8B-11D9-B985-000A95BB581A";
+               query_interface(Interface iface, bool supported out);
+               instantiate(Object instance out);
+       };
+       
+       interface Interface {
+               guid: "C36EBE18-EA8B-11D9-896D-000A95BB581A";
+       };
+       
+       interface Method {
+               guid: "C3D1BA69-EA8B-11D9-9439-000A95BB581A";
+       };
+       
+       interface Struct {
+               guid: "C4384909-EA8B-11D9-B856-000A95BB581A";
+       };
+
+       interface Filter {
+               guid: "C4A89048-EA8B-11D9-BB2C-000A95BB581A";
+
+               // Returns the object from which the filter was created.
+               // This will only succeed if the calling process already
+               // has a reference to the real object.  If the filter
+               // points to another filter, it will return the transitive
+               // real object if possible, or else the filter closest to
+               // the real object for which the process already has a
+               // reference.  If neither the real object nor a closer
+               // filter can be returned, this filter itself is returned.
+               // This should not be used for comparing filter references,
+               // as separately created filters for the same object will
+               // have different IDs and pointers, even if they contain
+               // the same subset of interfaces.
+               
+               get_real_obj(Object obj out);
+               
+               // Returns a local ID of the real object, regardless of whether
+               // the calling process has a reference to it, or whether there
+               // are other intervening filters.  The ID cannot be used to
+               // invoke methods, but it can be used to compare the identities
+               // of the objects behind different filter objects.  If a real
+               // reference to the object is later obtained, it will have
+               // the same local ID.
+               
+               get_real_obj_id(ID id out);
+       };
+       
+       interface ObjectManager {
+               guid: "C28596AB-EA8B-11D9-8DEB-000A95BB581A";
+
+               new_object(ID cla, ID obj out);
+               delete_object(ID obj) async;
+       
+               new_interface(ID cd, ID cla out);
+               delete_interface(ID cla, bool call_del) async;
+       
+               open_object(ID obj, uint handle out);
+               close_object(uint handle) async;
+       
+               // Create a filter object that implements only some of the
+               // interfaces implemented by "obj".  This is useful to create a
+               // more limited reference to pass to less trusted processes.  If
+               // "exclude" is true, then all interfaces but those specified will
+               // be included.  Otherwise, only those interfaces specified will be
+               // included.  A filter with no interfaces may be created to act as
+               // a (mostly) opaque handle.
+               //
+               // A holder of a filter reference can convert it into the real
+               // object if it already has (or later obtains) a reference to to
+               // the real object.  It can also compare the identities of
+               // separately created filters pointing at the same object
+               // regardless of what it has a real reference to.  Thus, filters
+               // should be used only to limit access granted by passing a
+               // reference to another process; it should not be used to hide the
+               // identity of the real object.
+               
+               create_filter(Object obj, bool exclude, Interface[] ifaces,
+                             Filter filter out);
+       };
+       
+       // This is a generic Factory interface; specific factories may
+       // (but do not need to) implement a more specific interface that
+       // guarantees that the generated object will comply with some
+       // particular interface.
+       
+       interface Factory {
+               guid: "9C084DD0-5D69-11DA-BD5A-000A95BB581A";
+               create(Object obj out);
+       };
+}
diff --git a/idl/proc.idl b/idl/proc.idl
new file mode 100644 (file)
index 0000000..7ae03e3
--- /dev/null
@@ -0,0 +1,96 @@
+// Process
+//
+// These are the interfaces through which operations are performed on
+// processes.
+
+namespace Proc;
+
+interface Process {
+       guid: "9C751874-EAB6-11D9-8399-000A95BB581A";
+};
+
+// This interface is implemented by the Process class.
+interface ThreadFactory {
+       guid: "9FDA3678-EAB6-11D9-97D1-000A95BB581A";
+
+       // Create a new thread in this process.  If pc is 0, then the new
+       // thread begins execution immediately after the call to new_thread. 
+       // Otherwise, the new thread begins execution at the address passed in
+       // pc.  If stack is 0, then the new thread begins with the same stack
+       // pointer as the caller.  Otherwise, the new thread begins with the
+       // stack pointer specified in stack.  Various flags can be specified
+       // by the NewThreadFlags bitmask; see the definition above for
+       // details.
+       // 
+       // Upon return to the caller, the newly created thread is placed in
+       // thread.  If pc is 0, the newly created thread will begin at the same
+       // address that the creator thread returns to, returning NULL in thread.
+
+       new_thread(ulong pc, ulong stack, ulong param, Thread thread out);
+       
+       // This method is identical to new_thread except that it is asynchronous,
+       // and thus cannot return thread, and cannot take a value of 0 for
+       // pc or stack.
+
+       new_thread_async(ulong pc, ulong stack, ulong param) async;
+};
+
+// This interface provides control of the binding between Processes
+// and AddrSpaces.  It is implemented by the Process class.
+
+interface ProcAddrSpace {
+       guid: "A3494EFC-EAB6-11D9-955B-000A95BB581A";
+
+       // Return the AddrSpace in which the process executes
+       get_addr_space(Mem.AddrSpace addr_space out);
+       
+       // Attach the process to an AddrSpace.  If the process is currently
+       // attached to an AddrSpace, that address space is first detached.
+
+       set_addr_space(Mem.AddrSpace addr_space);
+
+       // This is a hack to implement Unix-like exec() efficiently.
+       // It atomically sets the address space and jumps to the address
+       // given.  If the current thread is not in this process, an
+       // InvalidState exception is thrown.
+
+       set_addr_space_and_jump(Mem.AddrSpace addr_space, ulong pc);
+};
+
+// This interface is implemented by the Process class. 
+interface ProcTokens { 
+       guid: "A616D69E-EAB6-11D9-967E-000A95BB581A";
+
+       // Sets has_token to true if the process has currently has the
+       // specified token, or false if it does not.
+
+       has_token(ulong token, bool has_token out);
+       
+       // Grants the specified token to the process.  Throws a restartable
+       // TokenFault if the process does not have grant rights to the token.
+
+       grant_token(ulong token);
+};
+
+// This interface is implemented by the Process class. 
+interface ProcTraps {
+       using Traps.Trappable;
+       guid: "A88F3742-EAB6-11D9-B772-000A95BB581A";
+
+       // Returns a reference to the Trappable object for this process.
+       // Traps sent to this object will be sent to all threads in
+       // the process.
+       
+       get_trappable(Trappable trappable out);
+
+       // Returns a reference to the TrapDistributor object for
+       // this process.  Traps sent to the distributor object will
+       // be sent to one arbitrary thread in the process, rather than
+       // all threads which are not ignoring the trap.  A trap will
+       // only be sent to a thread that masks the trap if all threads
+       // mask the trap; likewise, a trap will only be ignored if
+       // all threads ignore it (or if the process itself does).
+       
+       get_trap_distributor(Trappable trap_distributor out);
+};
diff --git a/idl/thread.idl b/idl/thread.idl
new file mode 100644 (file)
index 0000000..822b3da
--- /dev/null
@@ -0,0 +1,50 @@
+using System.*;
+
+namespace Proc;
+
+interface Thread {
+       guid: "AE4A734E-EA8B-11D9-8142-000A95BB581A";
+};
+
+interface ThreadPriv {
+       guid: "A592FD94-5945-11DA-B99F-000A95BB581A";
+
+       get_thread(Thread unpriv out);
+       
+       // Asynchronous blocking (i.e. calling this method from a different
+       // thread) is not currently allowed.  It may be later, using a
+       // separate blocker field in the kernel.  This would be mainly for
+       // supporting debuggers, and would not take effect until the thread
+       // returns to userspace (if currently blocked or executing kernel
+       // code).
+       
+       block(Blocker blocker);
+       
+       // Like block, but with multiple blockers.  The thread wakes when any
+       // of the blockers have been unblocked.
+       
+       block_multi(Blocker[] blocker);
+};
+
+interface ThreadSched {
+       guid: "AEB5CD7C-EA8B-11D9-BE15-000A95BB581A";
+
+       // Set the thread's scheduling policy and parameters.  Throws
+       // InvalidArgument if sched is not a scheduler provided by the
+       // local kernel, or if param is not of the subtype required by
+       // sched.
+       
+       set_sched(Scheduler sched, SchedParams param);
+};
+
+struct SchedParams virtual {
+       guid: "1C406B42-2878-11DA-93B8-00112431A05E";
+};
+
+interface Scheduler {
+       guid: "AF383690-EA8B-11D9-95D8-000A95BB581A";
+};
+
+interface Blocker {
+       guid: "30D19964-5945-11DA-BC3B-000A95BB581A";
+};
diff --git a/idl/time.idl b/idl/time.idl
new file mode 100644 (file)
index 0000000..d6d5f55
--- /dev/null
@@ -0,0 +1,134 @@
+namespace Time;
+
+struct Time inline {
+       long seconds; // 0 is Jan 1, 1970 at midnight UTC; negative values
+                     // can be used for historical dates or intervals
+       uint nanos;   // Values greater than 999,999,999 are errors and
+                     // should cause an exception to be thrown.
+};
+
+struct ITime inline {
+       Time value;
+       Time interval;
+};
+
+// A Clock will generally also implement TimerFactory and ITimerFactory,
+// producing timers that are tied to the clock in question.  The kernel
+// clock objects do not implement ITimerFactory.
+
+interface Clock {
+       guid: "C7F53E18-FD5C-11D9-B176-000A95BB581A";
+
+       // Get the current time according to this clock.
+
+       get_time(Time time out);
+       
+       // Get the clock's resolution.  This is the largest
+       // amount by which the clock may quantize its timekeeping.
+       
+       get_resolution(Time res out);
+       
+};
+
+// FIXME: Implement generic Factory
+interface TimerFactory {
+       guid: "94BB5C90-41B6-11DA-8815-000A95BB581A";
+       new_timer(Timer timer out);
+};
+
+interface ITimerFactory {
+       guid: "97C7A4DA-41B6-11DA-8615-000A95BB581A";
+       new_itimer(ITimer itimer out);
+};
+
+interface SettableClock {
+       guid: "CED0AD0B-FD5C-11D9-954F-000A95BB581A";
+
+       // Set the clock's time.  This may be rounded off to no coarser
+       // a precision than the clock's resolution.
+
+       set_time(Time time);
+       
+       // Return a reference to the read-only Clock interface associated
+       // with this object.
+       
+       get_readonly_clock(Clock clock out);
+};
+
+bitfield TimerFlags {
+       // If set, set_time and get_time use Times relative to the current
+       // time, rather than absolute time.
+       
+       RelTime,
+};
+
+interface Timer {
+       guid: "F78C769F-146E-11DA-897A-000A95BB581A";
+       
+       // Set/get the time at which this timer expires.
+       
+       arm(Time expiry);
+       get_expiry(Time expiry out, bool was_armed out);
+       
+       // Stop the timer from firing.  This may be called regardless of
+       // whether the timer is currently armed.  The timer is guaranteed to
+       // not fire after the call completes, but may fire at any point during
+       // the disarm() call.
+       //
+       // If the firing event is synchronous, it will have completed by the
+       // time the disarm() call completes.  Otherwise, though the final
+       // firing has happened, the handler may still be running after
+       // disarm() returns.
+       
+       disarm();
+
+       // Set the type of action to take when the timer fires.
+       
+       set_action(Events.Event event);
+};
+
+interface ITimer {
+       guid: "D19B0A25-FD5C-11D9-9A2F-000A95BB581A";
+
+       // Arm the timer with the specified expiry.  If expiry.interval is not
+       // zero, then when the timer expires, it will automatically be
+       // re-armed with the time set to previous expiry plus the interval. 
+       // The previous expiry is atomically read and returned in oldexpiry.
+       //
+       // Nothing will happen on timer expiration unless an action has been
+       // specified with set_action.
+       
+       arm(ITime expiry, TimerFlags flags, ITime oldexpiry out);
+       
+       // Return the expiry as with arm, but without setting a new
+       // expiry.
+       
+       get_expiry(TimerFlags flags, ITime expiry out, bool was_armed out);
+       
+       // Disarm the timer.
+       
+       disarm();
+       
+       // Return the number of times since the last call to get_overrun
+       // that a timer has fired without the previous timer action having
+       // completed.
+       //
+       // For synchronous events such as a TrapEvent, this
+       // includes the user trap handler not finishing on time (and in
+       // such a case, new traps will not be generated for overrun firings).
+       //
+       // For asynchronous events such as a NotifierEvent, this only includes
+       // the system's ability to send the notification.  If the user notify
+       // method can't keep up, it will be reflected in a growing queue of
+       // pending messages, not in the overrun count.
+       
+       get_overrun(uint overruns out);
+       
+       // Set the notifier to be used when the timer expires.  For kernel
+       // timers, only sync instances of the kernel event dispatcher may be
+       // used (the sync notifier may be a trigger for an async
+       // NotifierEvent, though).
+       
+       set_notifier(Notifiers.Notifier notifier);
+       set_sync_notifier(Notifiers.SyncNotifier notifier);
+};
diff --git a/idl/traps.idl b/idl/traps.idl
new file mode 100644 (file)
index 0000000..8860de5
--- /dev/null
@@ -0,0 +1,107 @@
+/* System traps
+ *
+ * Traps are events delivered to a process or thread, similar to
+ * POSIX signals.
+ */
+
+namespace Traps;
+
+// The base Trap struct; all traps must derive from this.
+struct Trap virtual {
+       guid: "2B91FBD9-2878-11DA-98E8-00112431A05E";
+};
+
+// The kernel Process and Thread classes implement this interface. 
+// When trap is invoked on a process, a trap is sent to all of the
+// threads in the process.  When invoked on a thread, a trap is sent
+// only to that thread. A trap can be sent to one arbitrary thread in
+// a Process by getting a reference to a TrapDistributor class object
+// for that Process.
+
+interface Trappable {
+       guid: "88661962-EAB6-11D9-9B1D-000A95BB581A";
+
+       // Delivers a synchronous trap according to the rules of the
+       // object on which this method was invoked.  "delivered" is false
+       // if the trap was ignored, if this was a process's trappable and
+       // there are no threads in the process, or if an error occured
+       // that prevented delivery (though that case should generally be
+       // handled with exceptions).  It is *not* false merely because the
+       // trap was masked at the time.
+       
+       trap(Trap trap, bool delivered out);
+};
+
+// The kernel Process and Thread classes provide objects that 
+// implement this interface. It is separate from Trappable for
+// security reasons (plus, the distributor objects don't have their
+// own masks).  If a trap is sent to a thread via its own Trappable,
+// then only the thread's mask will be consulted.  If it is sent via
+// a process's Trappable, either directly or through the distributor,
+// the trap must pass both process and thread masks to be delivered.
+
+interface TrapMaskTable {
+       guid: "8D09635A-EAB6-11D9-A7EC-000A95BB581A";
+
+       enum MaskState {
+               Unmask,     // Accept this trap and its descendents, and
+                           // deliver them immediately.  This is the
+                           // default behavior.
+               Mask,       // Accept this trap and its descendents, but don't
+                           // deliver them until unmasked.
+               Ignore,     // Ignore this trap and its descendents; the trap
+                           // is discarded and will not be delivered even if
+                           // the trap is later unmasked.
+               DeleteEntry // Undo previous calls to set_mask for this trap,
+                           // reverting ignored status to that of the trap's
+                           // parent (or Ignore for the root Trap).  This is
+                           // valid only for set_mask().
+       };
+
+       struct MaskEntry {
+               System.Objects.Struct trap;
+               MaskState state;
+       };
+
+       // Sets the mask status for the trap in question, as well as its
+       // children.  Previous calls to set_ignored on a descendent trap
+       // will not be affected unless override is set to true (in which
+       // case it's as if DeleteEntry were called on all descendents).
+       
+       set_mask(MaskEntry mask, bool override);
+       
+       // This returns the mask state for a given trap type, as well as
+       // the specific trap type whose mask entry was used to determine
+       // this (it will be either the given trap, or the nearest ancestor
+       // with a mask entry).
+       
+       get_mask(System.Objects.Struct trap, MaskEntry mask out);
+
+       // Set the entire mask table as an unsorted linear list of entries.
+       // If override is true, this replaces the existing mask table.
+       // Otherwise, these entries are added to the list, overriding
+       // only exact trap type matches.
+       
+       set_mask_table(MaskEntry[] entries, bool override);
+
+       // Get the entire mask table as an unsorted linear list of entries.
+       get_mask_table(MaskEntry[] entries out);
+};
+
+namespace Std;
+
+// The ReduceMemoryUsage trap is delivered when the system is low on
+// uncommitted virtual memory.  It is delivered to each process using
+// uncommitted memory, in decreasing order of the amount of such memory,
+// until enough memory is freed to accomodate the request.
+
+struct ReduceMemoryUsage : Trap {
+       guid: "40CBFDB6-2878-11DA-B80D-00112431A05E";
+
+       // Amount of memory in bytes that are needed.  A process should
+       // stop looking for memory to free once it has freed this amount
+       // if it would save significant processing time, or if the memory
+       // being freed is still useful as cached data.
+       
+       ulong mem_needed;
+};
diff --git a/idlcomp/BUGS b/idlcomp/BUGS
new file mode 100644 (file)
index 0000000..fc1be9d
--- /dev/null
@@ -0,0 +1,4 @@
+IDLC does not check whether the contents of a bitfield or enum fit
+within the specified size.
+
+IDLC does not check for loops in the inheritance chain.
diff --git a/idlcomp/DONE-TODO b/idlcomp/DONE-TODO
new file mode 100644 (file)
index 0000000..33c795b
--- /dev/null
@@ -0,0 +1,39 @@
+Allow "class a.b" as well as/instead of "namespace a class b" in
+type definitions.
+
+Replace use of ordinary "string" with reference-counted "String".
+
+Implement aliases, that can be used to import other namespaces or
+specific elements thereof as if they were defined in the current
+namespace, optionally with a different name.  They can also be used
+simply to define an alias within a namespace.
+
+Implement typedefs, which are like aliases, but which are treated as
+distinct types for type-checking purposes.  Typedefs should not be
+allowed on namespaces themselves (for now, at least... Maybe when/if
+virtual classes are added that can change).
+
+Finish semantic routines and generate output (mostly done).
+
+Implement the importation of compiled types.
+
+Add implicit System.Object inheritance.
+
+Allow symbols defined in the current and ancestor namespaces to
+override those elsewhere in the search path without ambiguity,
+favoring closer namespaces.
+
+Come up with a proper set of namespace search rules.
+
+Implement loop detection for constant initialization.
+
+C++: Constructors for easy struct initialization
+
+C++: Array<> initializers for constant strings
+
+Implement autorelease pools.  Until then, I'm not going to bother
+fixing some memory leaks.  Partially done -- the interfaces exist,
+but the implementation doesn't (anything autoreleased will just leak
+for now).  Note that before it is implemented, code that assumes a
+newly allocated object has refcount 1 needs to be fixed to
+assume/accept the possibility that it has been autoreleased.
diff --git a/idlcomp/FIXED-BUGS b/idlcomp/FIXED-BUGS
new file mode 100644 (file)
index 0000000..dd46333
--- /dev/null
@@ -0,0 +1,63 @@
+Parameter syntax is ambiguous; does foo(out .bar baz) have an out
+parameter of type .bar, or an in parameter of type out.bar?  Perhaps
+the leading "." should be changed to ".." or something...  
+ - Leading "." is now "..".  This has the side-effect of foo..bar
+   being tokenized as foo ..bar, rather than an error as it was
+   previously.
+ - Attributes now go at the end, rather than the beginning.  Perhaps
+   the global prefix can go back to being a single dot, but I'm not
+   going to look into it right now.
+
+Empty bitfields cause parse errors.
+
+Uncaught InvalidArgument exception when namespaces are implicitly
+declared inside a struct (namespace qualification should not be
+allowed).
+
+Anonymous enumerations cause parse errors.
+
+Parameter type names currently cannot be namespace-qualified.
+
+Parameter output does not currently contain type information.
+compiledtypes.h says that .self is CompiledParam and type is
+CompiledAlias, but the code does not generate a directory.  Instead
+of doing that, I'll stick a CompiledBasicType and a CompiledAlias at
+the end of CompliedParam.
+
+The current array-of-strings representation is kind of stupid;
+instead of a list of file offsets, switch to counted strings (such as
+is used now in CompiledAlias), and let the reader iterate over them.
+
+Alias should work on more than just types.
+
+A lookup can succeed in pass 1 that would yield a different result in
+pass 2.  For instance:
+
+       struct a {
+       };
+
+       struct b {
+               a c;
+               typedef int a;
+       };
+
+Similarly, it's possible for two passes to be insufficient.
+This causes an internal error:
+
+       typedef d b;
+
+       struct d {
+               int f;
+       };
+
+       struct a {
+               b c;
+       };
+
+Alias names get recorded in the type field of Datums (and other
+places) rather than what the alias points to.
+
+Constants are currently not allowed at namespace scope.
+
+Bool constants cannot be initialized.
+
diff --git a/idlcomp/Makefile b/idlcomp/Makefile
new file mode 100644 (file)
index 0000000..2117ab5
--- /dev/null
@@ -0,0 +1,78 @@
+TOP := $(shell dirname `pwd -P`)
+COMP := idlcomp
+BUILDTYPE := build
+ARCHINDEP := y
+include ../Makefile.head
+
+OPT := -O0
+
+RAW_CXXFILES := main namespace types output input util targets
+BUILDCXXFILES += $(RAW_CXXFILES:%=$(DIR)%)
+
+RAW_CXXFILES := idlparse cdlparse scan
+BUILDGENCXXFILES += $(RAW_CXXFILES:%=$(DIR)%)
+
+include languages/c++/Makefile
+
+BUILDDEFS += -D__STDC_FORMAT_MACROS -D__STDC_VERSION__=199901 -I. -DBUILDDIR=$(BUILDDIR)
+
+# Flex's scanner triggers the unused warning.
+BUILDCXXFLAGS += -Wno-unused
+
+TARGETS := $(BUILDDIR)/idlc
+       
+$(BUILDDIR)/scan.cc: scan.lex
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(BUILDDIR)
+       @$(FLEX) -o$(BUILDDIR)/scan.cc scan.lex
+
+# Some versions of bison will generate idlparse.cc.h based on an
+# output filename of idlparse.cc, and others will generate
+# idlparse.hh.  There's no way I can find to specify which to use, so
+# the name has to be canonicalized after bison is run.
+
+$(BUILDDIR)/idlparse.cc: idlparse.y
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(BUILDDIR)
+       @$(BISON) -b idl -p idl_ --verbose -d idlparse.y -o $(BUILDDIR)/idlparse.cc
+       @if [ -e $(BUILDDIR)/idlparse.hh ]; then \
+               $(MV) $(BUILDDIR)/idlparse.hh $(BUILDDIR)/idlparse.cc.h; \
+       fi
+
+$(BUILDDIR)/cdlparse.cc: cdlparse.y
+       @echo $(COMP): "$<"
+       @$(MKDIR) $(BUILDDIR)
+       @$(BISON) -b cdl -p cdl_ --verbose -d cdlparse.y -o $(BUILDDIR)/cdlparse.cc
+       @if [ -e $(BUILDDIR)/cdlparse.hh ]; then \
+               $(MV) $(BUILDDIR)/cdlparse.hh $(BUILDDIR)/cdlparse.cc.h; \
+       fi
+
+# These dependencies need dummy actions, or make will ignore them for
+# some reason.
+
+$(BUILDDIR)/idlparse.cc.h: $(BUILDDIR)/idlparse.cc
+       @
+
+$(BUILDDIR)/cdlparse.cc.h: $(BUILDDIR)/cdlparse.cc
+       @
+
+idlcclean:
+       $(MAKE) -C tests clean
+       $(RM) $(BUILDDIR)/?dlparse.*
+
+EXTRACLEAN += idlcclean
+
+$(BUILDDIR)/scan.o: $(BUILDDIR)/idlparse.cc.h
+
+export IDLC
+.PHONY: tests
+tests:
+       $(MAKE) -C tests
+
+PREDEP := $(BUILDDIR)/idlparse.cc $(BUILDDIR)/cdlparse.cc
+
+include ../Makefile.tail
+
+$(BUILDDIR)/idlc: $(BUILDOBJS)
+       @echo $(COMP): Linking $(COMP)
+       @$(BUILDCXX) -o $(BUILDDIR)/idlc $(BUILDOBJS)
diff --git a/idlcomp/THOUGHTS b/idlcomp/THOUGHTS
new file mode 100644 (file)
index 0000000..c60422b
--- /dev/null
@@ -0,0 +1,310 @@
+5/23/04:
+
+What to do about replacing symbols in later passes?  A complete
+replacement will break existing pointers, and a symbolic reference
+would require substantial code changes.  I'll probably produce output
+after each pass and wipe the symbol table; this is less efficient,
+but similar to how the code was originally intended to work.
+
+How to determine if a pass 2 is needed if the symbol lookup succeded
+due to stale external data that would be overwritten?  Perhaps
+eliminate the --overwrite option; then it wouldn't be possible,
+but it could be annoying to have to wipe everything before each
+compilation.  It would pretty much enforce one batch of source IDLs
+per output directory, though that's not necessarily a bad thing.
+
+In fact, I think I'll just make --overwrite act on the entire output
+directory; if it's not set, you get an error if there's *anything* in
+there.
+
+Now, what happens when you get around to declaring a symbol in a
+later pass, which has been loaded from the fs already?  If the only
+thing the linkage is used for is to generate a name in the output,
+then just replace it and let the old version stay refcounted.  Is
+there enough in the pass1 output for error checking, other than
+constant ranges?  I think so.
+
+When will circular inheritance checks be done?  That'll require the
+ability to compare references, meaning we can't just go replacing
+things.  Other than that, I think it can be done at the end of pass
+2.  So instead of replacing, I'll just add information to existing
+objects (which means I get to go fix all the places where that sort
+of work is done in the constructor).
+
+5/25/04:
+
+In conclusion on the above, no replacement is done for future passes.
+Constructor calls have been replaced with calls to a static declare()
+function, which will either call a constructor or return the existng
+one (or complain if there's a real conflict (i.e. within this pass or
+with an external symbol)), as well as possibly initialize some data.
+
+Still to do:
+
+Implement pass 3
+Finish implementing output (including sanity check for incomplete
+data).
+Nuke --overwrite, and complain if anything is in the target
+directory.
+
+8/1/04:
+
+Most vtable-related design is done.  The GUID pointers will have to
+be dynamically generated somehow (either by the dynamic linker or by
+startup code), to make sure the same pointer is used in all
+components.
+
+The compiled type representation will break badly on a
+case-insensitive filesystem.  This is already seen in the current IDL
+files.  Some sort of alternate mapping will be needed.  Also, it
+looks like the performance of output generation will be fairly poor
+under UFS on OS X; even with the current set of IDL it takes 1/4
+second to generate all output.  Not that it was expected to perform
+well on legacy filesystems, but still, I wonder how long it will take
+on the full set of IDL...
+
+9/21/04:
+
+Enum and bitfield inheritance may be useful...
+
+9/22/04:
+
+YYError() should probably be turned into UserError()...
+
+9/25/04:
+
+Or more specifically, into something like RecoverableUserError().
+
+12/7/04:
+
+Arrays need some more thought, specifically multi-dimensional
+and inline arrays, and how they interact with typedefs.  Currently,
+multi-dimensional arrays are simply not supported, but what happens
+if you typedef an array, and then create an array of that?  It's
+accepted at the moment, and if you accept that, why not regular
+multi-dimensional arrrays?  Plus, with out-of-line arrays
+multi-dimensional arrays cannot be created simply by multiplying the
+sizes of each dimension.  Support should be added.
+
+12/21/04:
+
+A separate type of reference will likely be needed for persistent
+entities, as the overhead would be too much to always do it.  This
+would also allow an entity to be locked against decaching (but not
+ordinary swap-out) by acquiring a non-persistent reference.  
+
+If one is willing to disallow such locking, persistence could simply
+be an attribute of a type, but you'd still have problems with
+references to embedded types; a persistent type should be able to
+contain non-persistent types (either inline or by reference).
+
+One implementation of persistence would be for a persistent reference
+to have two states.  An uncached reference consists of a
+non-persistent reference to a storage object (or perhaps a cache
+object backed by a storage object).  A cached reference is like a
+normal, non-persistent reference.  The state would have to be checked
+on every dereference.  If it is found to be uncached, the entity is
+retrieved (either from storage, or from cache (it may have gotten
+there via another reference)), the state of the reference is changed,
+and the reference is added to a list to be swept when trying to
+decache the object.  Something would need to be done to prevent races
+with asynchronous decaching (perhaps an in-use bit or refcount in the
+reference).  However, implementing such a mechanism would be
+difficult on top of an ordinary language.
+
+An alternative, which is less "automatic" from a programmer's
+perspective, but still much better than the current state of things,
+is to have the programmer always acquire an ordinary reference before
+dereferencing (essentially, the in-use refcount of the previous
+mechanism would be managed manually or by garbage collection).  The
+programmer can choose whether to keep the ordinary reference around
+(which favors simplicity, determinism, speed) or the storage
+reference (which minimizes memory consumption and requires more
+programmer and CPU time to acquire a usable reference more often). 
+
+The difference between this and simply having serialize/deserialize
+methods is that you would receive the same entity address if you
+convert a storage reference multiple times.  This causes a problem if
+you do this from different address spaces, though.  Shared memory is
+a possibility, but it would be unsuitable in many circumstances due
+to either races or memory wastage (you'd pretty much need to allocate
+a page per entity, so that access can be controlled precisely (you
+shouldn't be able to access entity B just because some other process
+has it in the same page as entity A to which you do have access
+rights, and you can't move one of them to another page without
+breaking the other process's references)).
+
+12/25/04:
+
+Security issues need some more thought.  In particular, how to handle
+the case where the rights of multiple processes are needed to do
+something, with no one process fully trusted with all of those
+rights.  If you just pass a handle to one process, and don't have any
+further restrictions, then it can do other things with that handle,
+long after it's returned.  Delegates would allow it to be limited to
+one method, and handle revocation would be nice as well.  However, it
+could still be more privilege than was intended to be granted.
+
+To be fully secure, one-time-use objects could be created that only
+allow a certain, specific operation, but that would have too much
+overhead in many cases.  
+
+12/28/04:
+
+An ordinary reference has certain rights associated with it, and
+these rights are transfered to the callee when the reference is
+passed.  For persistent references, only the namespace lookup
+permission is bypassed; unserializing (or serializing) the object
+requires whatever capability token has been set for the relevant
+operation.  I don't think it would be worthwhile to implement a third
+type of reference that is unserialized but without privilege grant;
+if one wants that, one could make a custom storage object that
+doesn't actually serialize anything, but just hands out the
+real reference upon presentation of the right capability token.
+
+Access revocation is important for making sure the callee doesn't
+hold onto the reference longer than it is supposed to (especially if
+the access rights change after the call).  However, it cannot be
+determined automatically how long to allow a call-granted reference. 
+Many calls may only need it for the duration of the call, but some
+will need to hold the reference longer.  The reference also must be
+revoked if the caller's access to that object is revoked
+(technically, it could remain if the callee has another
+path-to-privilege, but it may not want to, if the action it takes
+assumes that the caller had privilege to carry out the action). 
+
+Implementing access revocation requires that we either say fuck-you
+to the app and make it unserialize again if it does happen to have an
+alternate path-to-privilege (I believe this is what Unix does), or
+somehow link the unserialized entity to the persistent reference, and
+give it a chance to prove that it's allowed to retain the reference. 
+I greatly favor the latter approach; though it's more complicated to
+implement, going the other way will make lots of apps either buggy or
+hideously complicated.
+
+Alternatively, a reference could be more tightly bound to the exact
+path-to-privilege, requiring the app to explicitly specify which
+source(s) of privilege to consider.  This has benefits in avoiding
+odd races where an app would have asked the user for a password to
+elevate privilege, but didn't because it happened to have a given
+authority already for some other reason, but which got revoked before
+the operation completed.  It'd also be nice in general in helping
+server processes manage inherited permissions sanely.  It'd open the
+multiple-references-per-object can of worms, in that a single address
+space could have references to the same object compare unequal (or
+else have a more complicated comparison operation than simply
+checking the reference pointer).
+
+Aah, fuck it.  If you pass a reference to a task, you're trusting it
+not to do bad stuff with it.  If you can't give it that trust, send
+it a more limited reference.  The major time you'd really want to do
+a revocation is when the access rights to an object change, and the
+fuck-you-legitimate-reference-holder approach could be sufficient for
+the case where the owner of the object is pretty sure there are no
+legitimate references remaining.  Existing OSes don't handle anything
+beyond that very well AFAIK, so if I come up with anything better
+it'll be a bonus, but I'm not too worried.
+
+The problem with the trust-it approach is that it's harder to know
+who you're talking to in a polymorphic OS; all you really know
+(without excessive ORB queries) is the interface type.  The trust
+level for the implementation will often be zero, and (just about)
+anything that can be done to limit leakage of privilege is a good
+thing.  Oh well, we'll see how it turns out after further API design. 
+It might turn out to not be such a big deal, and I need to get on
+with making stuff work.
+
+2/1/05: GCC on PPC violates the SYSV ABI by not returning small
+structs in registers.  This could have a noticeable performance
+impact given that object references are really small structs. 
+While fixing it for existing OSes is unlikely due to existing
+binaries, perhaps it should be fixed for this OS while it still
+can be...
+
+3/13/05: Typedefs are not going to be allowed for interfaces.  The
+immediate, selfish reason for this is that allowing them would cause
+some minor ugliness in the idlc code that I'd rather avoid (it's ugly
+enough already).  However, I'm having a hard time thinking of
+legitimate uses for them compared to inheritance.  If such a use
+comes up, it can be re-allowed later.  Or maybe I'll implement it
+soon, and consider it a FIXME until then.
+
+3/19/05: Oops.  There was an ambiguity in the IDL, in that the
+double-dot was used both as a global namespace prefix and as a range
+specifier.  This wasn't caught before, because idlc wasn't allowing
+namespace-qualified constants.  The range specifier is now a
+triple-dot.
+
+3/20/05: The memory management scheme is *still* really screwed up;
+an interface declared in the namespace of a superinterface (and
+similar constructs) will cause reference loops.  I think when it
+finally gets to the point that I try to make memory management
+actually work right (which probably won't be until this code is made
+library-able) I'll just declare the entire tree to be a monolithic
+entity, freed in one go when it is no longer needed.  Reference
+counting could still be used for things that aren't part of the tree,
+like strings and lists.
+
+5/18/05: I'm thinking of allowing actual return values instead of
+using only out parameters (even with overloaded return-the-last-
+out-parameter features of language bindings).  It would more clearly
+express the intent of the programmer to designate one of the out
+parameters as a return value, and it would make it easier to take
+advantage of an ABI's return value registers (instead of always using
+pointers, or continuing the last-out-param hack at the function
+pointer level).
+
+Enums in C++ will be typesafe against assigning one initialized enum
+to an enum of a different type; however, it doesn't look like it will
+be able to be made safe against initializing an enum with a const
+initializer from a different enum type, at least not without breaking
+things like switch.  Languages such as D should be able to do it
+properly with strong typedefs.
+
+GCC is refusing to do CSE on upcasts, even with const all over the
+place; this means that repeatedly calling methods from a derived
+interface will be less efficient than casting once to the parent
+interface and using that.  At some point, this should be resolved,
+but that's an optimization issue which can wait (and it may require
+compiler changes to let the compiler know that the data *really* is
+not going to change, ever, by anyone (apart from initialization which
+happens before the code in question runs)).  Maybe LLVM will do
+better.  CSE on downcasts would be nice too.
+
+5/20/05: I've decided against method return values.  The efficiency
+part is a minor optimization, and would be better handled with an ABI
+that can handle out parameters directly (thus giving you many return
+registers).  Of course, switching ABIs will be painful, but it's
+probably going to happen a few times anyway during the early phases
+of development.  As for "express[ing] the intent of the programmer",
+it's really not that big of a deal.  Eventually, instead of having
+last-out-param hacks like the C++ binding, a language could allow
+some keyword or symbol to replace one (or more) arguments, causing
+them to be treated as return values.
+
+It has nothing to do with me being lazy.  No, not at all.  *whistling
+and walking away*
+
+It should be possible to disable async as an implementation
+attribute, so that in-process wrappers can execute directly (e.g.
+FileStream's async methods directly sending out an async method to
+the file, rather than requiring both steps to be async).
+
+5/23/05: FileStream's methods probably should be async anyway,
+and then either call a sync method, or provide its own notifier. 
+That way, it can keep the position correct if the read or write did
+not fully succeed.  It'll also need to keep all operations strictly
+ordered, so if async calls are used, it needs a message serializer
+object.
+
+See update 7/02/06.
+
+10/04/05: There should be a way to check whether a pointer to a
+virtual struct is of the most derived type.
+
+7/02/06: FileStream no longer exists as an interface; instead, an
+object combining Seekable, HasFile, and the apppropriate stream
+interface(s) (which have both sync and async methods) should be used. 
+This object will usually be local, so async isn't an issue, but it
+can be used remotely if it's really needed to synchronize the file
+offset pointer across multiple address spaces.
diff --git a/idlcomp/TODO b/idlcomp/TODO
new file mode 100644 (file)
index 0000000..a8206fa
--- /dev/null
@@ -0,0 +1,53 @@
+Improve error recovery, so as to show as many errors as possible
+before aborting.
+
+Support UTF-8 identifiers.  This includes deciding which subset of
+UTF-8 characters are valid for such use, and how to map them to
+languages that only support ASCII.
+
+Implement multi-dimensional arrays.
+
+Delegates.
+
+Typedefs and aliases need to support complex types (e.g. typedef
+int[] intarray;).  This ties into multi-dimensional arrays above;
+at the very least, it needs to be possible to declare an array
+of strings.
+
+Be consistent in whether the specified name or the fully qualified
+symbol name is reported in error messages.  The former makes it more
+obvious which usage is referred to in certain cases; the latter makes
+it obvious which symbol was found in lookup.  Adding column
+information as well as line number would address the former concern.
+
+Check for duplicate GUIDs.
+
+Properties with automatic getters and setters (or just getters if
+read-only), which can be overridden with custom getters and setters
+in the class.
+
+Methods can be specified as read-only; a read-only version of the
+interface is then generated, and a secure read-only reference to an
+object can be generated that can only access read-only methods.  This
+would save effort declaring a separate interface and a separate
+class, and would allow the read-write interface to use the read-only
+methods without retreiving the read-only object.
+
+Implement marshalling/unmarshalling stubs.
+
+Implement async methods.
+
+Implement remote-only async methods; these are async only when
+invoked out-of-process, and sync (and thus faster) when invoked
+in-process.  Full async methods would be used when the async
+semantics are truly required; remote-only async methods would be used
+where async isn't required, but the method is async-safe and would
+benefit from the buffering of async out-of-process methods.  This
+would reduce the number of methods that are declared in both sync and
+async versions (and allow the choice between them to be made
+dynamically).
+
+Possibly implement out parameters on remote-only async methods that
+can only be used when invoked as a sync in-process method; the method
+implementation can check to see how it was invoked, and use the out
+parameters to avoid having to make a completion callback.
diff --git a/idlcomp/cdl.h b/idlcomp/cdl.h
new file mode 100644 (file)
index 0000000..d46b2a6
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef CDL_H
+#define CDL_H
+
+#include <idlc.h>
+
+class Class;
+typedef Ref<Class> ClassRef;
+
+extern list<ClassRef> classes;
+
+class Class : public NameSpace {
+       list<InterfaceRef> ifaces;
+
+public:
+       struct ParamInfo : public RefCountable<ParamInfo> {
+               bool copy;
+       };
+
+       typedef Ref<ParamInfo> ParamInfoRef;
+
+private:
+       // C++ associative arrays are much more of a 
+       // pain to use than they should be.
+
+       typedef map<ParamRef, ParamInfoRef> params_map_type;
+       typedef params_map_type::value_type params_valtype;
+       typedef params_map_type::const_iterator params_iter;
+
+public:
+       struct MethodInfo : public RefCountable<MethodInfo> {
+               StrListRef implname;
+               bool copy_params; // True if at least one parameter
+                                 // has the copy attribute.
+
+       private:
+               params_map_type params;
+       
+       public:
+               ParamInfo *get_param(Param *p)
+               {
+                       params_iter ret = params.find(p);
+                       
+                       if (ret != params.end())
+                               return (*ret).second;
+                       
+                       return NULL;
+               }
+               
+               ParamInfo *add_param(Param *p)
+               {
+                       ParamInfo *pi = get_param(p);
+               
+                       if (!pi) {
+                               pi = new ParamInfo;
+                               pi->copy = false;
+                               
+                               pair<params_iter, bool> ret = 
+                                       params.insert(params_valtype(p, pi));
+       
+                               assert(ret.second);
+                       }
+               
+                       return pi;
+               }
+       };
+
+       typedef Ref<MethodInfo> MethodInfoRef;
+
+private:
+       typedef map<MethodRef, MethodInfoRef> method_map_type;
+       typedef method_map_type::value_type methods_valtype;
+       typedef method_map_type::const_iterator methods_iter;
+
+       method_map_type methods;
+
+public:
+       InterfaceRef concrete_iface;
+
+       Class(const String *name) :
+       Symbol(name), concrete_iface(new Interface(new String("<anon>")))
+       {
+               classes.push_back(this);
+       }
+       
+       void add_iface(Interface *iface)
+       {
+               ifaces.push_back(iface);
+               concrete_iface->add_super(iface);
+       }
+       
+       MethodInfo *get_method(Method *m)
+       {
+               methods_iter ret = methods.find(m);
+               
+               if (ret != methods.end())
+                       return (*ret).second;
+               
+               return NULL;
+       }
+       
+       MethodInfo *add_method(Method *m)
+       {
+               MethodInfo *mi = get_method(m);
+
+               if (!mi) {
+                       mi = new MethodInfo;
+                       mi->copy_params = false;
+                       
+                       pair<methods_iter, bool> ret = 
+                               methods.insert(methods_valtype(m, mi));
+
+                       assert(ret.second);
+               }
+               
+               return mi;
+       }
+
+       typedef list<InterfaceRef>::const_iterator ifaces_iterator;
+
+       ifaces_iterator ifaces_begin()
+       {
+               return ifaces.begin();
+       }
+
+       ifaces_iterator ifaces_end()
+       {
+               return ifaces.end();
+       }
+       
+       // FIXME: check for duplicate method implementation names.
+       void finalize()
+       {
+               concrete_iface->finalize_class_iface();
+       }
+};
+
+#endif
diff --git a/idlcomp/cdlparse.y b/idlcomp/cdlparse.y
new file mode 100644 (file)
index 0000000..8896bbb
--- /dev/null
@@ -0,0 +1,470 @@
+%{
+/* cdlparse.y -- parser for the CDL compiler
+ *
+ * Written by Scott Wood <scott@buserror.net>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+
+#include <idlc.h>
+#include <cdl.h>
+
+#define YYDEBUG 1
+
+#define do_yyerror() do { \
+       fprintf(stderr, "YYERROR at %d\n", __LINE__); \
+       throw UserError(); \
+} while (0)
+
+static StrListRef nspace_name;
+static StrListRef cur_strlist;
+static IDListRef cur_idlist;
+static ClassRef cur_class;
+static MethodRef cur_method;
+
+%}
+%union {
+       // The lifetime of any of these pointers is one instance
+       // of the one_input rule.
+       
+       String *string;
+       StrList *strl;
+       IDList *idl;
+       SymList *syml;
+       Symbol *sym;
+       Method *meth;
+       Class *cla;
+       bool boolean;
+
+       struct {                // Used for namespace-qualified type declarations
+               NameSpace *ns;       // Namespace portion -- all but last field
+               const String *ident; // New identifier portion -- last field
+       } decl;
+}
+
+// The token list must be exactly the same as in idlparse.y, so that
+// the same lexer can be used.
+
+%token <string> TOK_IDENT
+%token TOK_IFACE
+%token TOK_STRUCT
+%token TOK_CHAR
+%token TOK_OCTET
+%token <con> TOK_ICON
+%token <con> TOK_FCON
+%token <con> TOK_UCON
+%token <con> TOK_INVALID
+%token TOK_BOOL
+%token TOK_SHORT
+%token TOK_INT
+%token TOK_LONG
+%token TOK_USHORT
+%token TOK_UINT
+%token TOK_ULONG
+%token TOK_FSHORT
+%token TOK_FLONG
+%token TOK_CONST
+%token TOK_BITFIELD
+%token TOK_ENUM
+%token TOK_NAMESPACE
+%token TOK_USING
+%token TOK_ASYNC
+%token TOK_INOUT
+%token TOK_OUT
+%token TOK_3DOT
+%token TOK_2DOT
+%token <string> TOK_STR
+%token TOK_SHARED
+%token TOK_PUSH
+%token TOK_TYPEDEF
+%token TOK_ALIAS
+%token TOK_VIRTUAL
+%token TOK_GUID
+%token TOK_INLINE
+%token TOK_STATIC
+%token TOK_IMMUTABLE
+%token TOK_TRUE
+%token TOK_FALSE
+
+// CDL tokens
+%token TOK_COPY
+%token TOK_METHOD
+%token TOK_CLASS
+%token TOK_NAME
+
+// These are not real tokens, but are used as special values in places that
+// normally accept tokens.
+%token TOK_NONE
+%token TOK_ANON
+%token TOK_DCON
+
+%type <strl>       ids
+%type <string>     ident
+%type <strl>       qualified_ident
+%type <idl>        qualified_idlist
+%type <boolean>    maybe_dbldot
+%type <decl>       qualified_decl
+
+%%
+
+input:
+       /* empty */
+|      input one_input
+;
+
+one_input:
+       one_input_real
+;
+
+one_input_real:
+       class
+|      namespace
+;
+
+namespace_body:
+       ';' {
+               NameSpace *ret = add_nspace(nspace_name, false);
+               nspace_name = NULL;
+
+               if (!ret)
+                       do_yyerror();
+       }
+|      '{' {
+               NameSpace *ret = add_nspace(nspace_name, true);
+               nspace_name = NULL;
+
+               if (!ret)
+                       do_yyerror();
+       } input '}' {
+               pop_nspace();
+       }
+|      {
+               NameSpace *ret = add_nspace(nspace_name, true);
+               nspace_name = NULL;
+
+               if (!ret)
+                       do_yyerror();
+       } one_input {
+               pop_nspace();
+       } 
+;
+
+namespace:
+       TOK_NAMESPACE qualified_ident {
+               nspace_name = $2;
+       } namespace_body
+;
+
+ids_body:
+       ident {
+               cur_strlist->push_back($1);
+       }
+|      ids_body ',' ident {
+               cur_strlist->push_back($3);
+       }
+;
+
+ids:
+       {
+               cur_strlist = new StrList;
+       } ids_body {
+               $$ = cur_strlist;
+               cur_strlist = NULL;
+       }
+;
+
+ident:
+       TOK_IDENT
+|      TOK_ASYNC    {
+               $$ = new String("async", cur_input_file, curline, TOK_ASYNC);
+       }
+|      TOK_INOUT    {
+               $$ = new String("inout", cur_input_file, curline, TOK_INOUT);
+       }
+|      TOK_OUT      {
+               $$ = new String("out", cur_input_file, curline, TOK_OUT);
+       }
+|      TOK_SHARED   {
+               $$ = new String("shared", cur_input_file, curline, TOK_SHARED);
+       }
+|      TOK_PUSH     {
+               $$ = new String("push", cur_input_file, curline, TOK_PUSH);
+       }
+|      TOK_SHORT    {
+               $$ = new String("short", cur_input_file, curline, TOK_SHORT);
+       }
+|      TOK_INT      {
+               $$ = new String("int", cur_input_file, curline, TOK_INT);
+       }
+|      TOK_LONG     {
+               $$ = new String("long", cur_input_file, curline, TOK_LONG);
+       }
+|      TOK_USHORT   {
+               $$ = new String("ushort", cur_input_file, curline, TOK_USHORT);
+       }
+|      TOK_UINT     {
+               $$ = new String("uint", cur_input_file, curline, TOK_UINT);
+       }
+|      TOK_ULONG    {
+               $$ = new String("ulong", cur_input_file, curline, TOK_ULONG);
+       }
+|      TOK_CHAR     {
+               $$ = new String("char", cur_input_file, curline, TOK_CHAR);
+       }
+|      TOK_OCTET    {
+               $$ = new String("octet", cur_input_file, curline, TOK_OCTET);
+       }
+|      TOK_FSHORT   {
+               $$ = new String("fshort", cur_input_file, curline, TOK_FSHORT);
+       }
+|      TOK_FLONG    {
+               $$ = new String("flong", cur_input_file, curline, TOK_FLONG);
+       }
+|      TOK_BOOL     {
+               $$ = new String("bool", cur_input_file, curline, TOK_BOOL);
+       }
+|      TOK_METHOD   {
+               $$ = new String("method", cur_input_file, curline, TOK_METHOD);
+       }
+|      TOK_NAME     {
+               $$ = new String("name", cur_input_file, curline, TOK_NAME);
+       }
+|      TOK_COPY     {
+               $$ = new String("copy", cur_input_file, curline, TOK_COPY);
+       }
+|      TOK_CLASS    {
+               $$ = new String("class", cur_input_file, curline, TOK_CLASS);
+       }
+|      TOK_GUID     {
+               $$ = new String("guid", cur_input_file, curline, TOK_GUID);
+       }
+|      TOK_STATIC   {
+               $$ = new String("static", cur_input_file, curline, TOK_STATIC);
+       }
+|      TOK_IFACE    {
+               $$ = new String("interface", cur_input_file, curline, TOK_IFACE);
+       }
+|      TOK_STRUCT   {
+               $$ = new String("struct", cur_input_file, curline, TOK_STRUCT);
+       }
+|      TOK_CONST    {
+               $$ = new String("const", cur_input_file, curline, TOK_CONST);
+       }
+|      TOK_BITFIELD {
+               $$ = new String("bitfield", cur_input_file, curline, TOK_BITFIELD);
+       }
+|      TOK_ENUM     {
+               $$ = new String("enum", cur_input_file, curline, TOK_ENUM);
+       }
+|      TOK_USING    {
+               $$ = new String("using", cur_input_file, curline, TOK_USING);
+       }
+|      TOK_TYPEDEF  {
+               $$ = new String("typedef", cur_input_file, curline, TOK_TYPEDEF);
+       }
+|      TOK_ALIAS    {
+               $$ = new String("alias", cur_input_file, curline, TOK_ALIAS);
+       }
+|      TOK_VIRTUAL  {
+               $$ = new String("virtual", cur_input_file, curline, TOK_VIRTUAL);
+       }
+|      TOK_INLINE {
+               $$ = new String("inline", cur_input_file, curline, TOK_INLINE);
+       }
+;
+
+qualified_ident_raw:
+       ident {
+               cur_strlist->push_back($1);
+       }
+|      qualified_ident_raw '.' ident {
+               cur_strlist->push_back($3);
+       }
+;
+
+maybe_dbldot:
+       /* empty */ {
+               $$ = false;
+       }
+|      TOK_2DOT {
+               $$ = true;
+       }
+;
+
+/* The mid-rule action is to keep curline correct, as well
+   as creating cur_strlist. */
+qualified_ident:
+       maybe_dbldot {
+               cur_strlist = new StrList;
+               
+               if ($1)
+                       cur_strlist->push_front(new String("", cur_input_file, 
+                                                          curline, TOK_IDENT));
+       } qualified_ident_raw {
+               $$ = cur_strlist;
+               cur_strlist = NULL;
+       }
+;
+
+qualified_ids:
+       qualified_ident {
+               cur_idlist->push_back($1);
+       } 
+|      qualified_ids ',' qualified_ident {
+               cur_idlist->push_back($3);
+       }
+;
+
+qualified_idlist:
+       {
+               cur_idlist = new IDList;
+       } qualified_ids {
+               $$ = cur_idlist;
+               cur_idlist = NULL;
+       }
+;
+
+qualified_decl:
+       maybe_dbldot {
+               if ($1)
+                       yyerrorf("Namespaces cannot be declared "
+                                "with an absolute path.");
+       
+               cur_strlist = new StrList;
+       } qualified_ident_raw {
+               $$.ident = cur_strlist->back();
+               $$.ident->retain();
+               
+               cur_strlist->pop_back();
+               
+               if (!cur_strlist->empty())
+                       $$.ns = add_nspace(cur_strlist, true);
+               else {
+                       $$.ns = cur_nspace;
+                       nspace_stack.push_front(cur_nspace);
+               }
+               
+               cur_strlist = NULL;
+               
+               if (!$$.ns)
+                       do_yyerror();
+       }
+;
+
+class:
+       TOK_CLASS qualified_decl {
+               cur_class = new Class($2.ident);
+               $2.ns->add_user(cur_class);
+       } ':' qualified_idlist {
+               if ($5->empty()) {
+                       yyerrorf("A class must implement at least one interface.");
+                       throw UserError();
+               }
+       
+               for (IDList::const_iterator i = $5->begin(); i != $5->end(); ++i) {
+                       StrList *strl = *i;
+                       Symbol *sym = lookup_sym(toplevel, strl, toplevel);
+                       Interface *iface = dynamic_cast<Interface *>(sym);
+
+                       if (!iface) {
+                               yyerrorfl(cur_input_file, strl->back()->line,
+                                         "\"%s\" is not an interface.", 
+                                         strl->flatten()->c_str());
+
+                               throw UserError();
+                       }
+                       
+                       cur_class->add_iface(iface);
+               }
+       } class_body {
+               pop_nspace();
+               cur_class->finalize();
+               cur_class = NULL;
+       }
+;
+
+class_body:
+       ';'
+|      '{' multi_class_body '}'
+;
+
+multi_class_body:
+       one_class_body
+|      multi_class_body one_class_body
+;
+
+one_class_body:
+       TOK_METHOD qualified_ident {
+               // FIXME: use the set of supported interfaces as a search path
+               Symbol *sym = lookup_sym(toplevel, $2, toplevel);
+               cur_method = dynamic_cast<Method *>(sym);
+               
+               if (!cur_method) {
+                       yyerrorfl(cur_input_file, $2->back()->line,
+                                 "\"%s\" is not a method.", 
+                                 $2->flatten()->c_str());
+                               throw UserError();
+               }
+       } method_body {
+               cur_method = NULL;
+       }
+;
+
+method_body:
+       one_method_body
+|      '{' multi_method_body '}'
+;
+
+multi_method_body:
+       one_method_body
+|      multi_method_body one_method_body
+;
+
+one_method_body:
+       TOK_NAME qualified_ident ';' {
+               Class::MethodInfo *mi = cur_class->add_method(cur_method);
+               mi->implname = $2;
+       }
+|      TOK_COPY ids ';' {
+               Class::MethodInfo *mi = cur_class->add_method(cur_method);
+               
+               for (StrList::const_iterator i = $2->begin(); i != $2->end(); ++i) {
+                       const String *str = *i;
+                       Symbol *sym;
+                       
+                       try {
+                               sym = cur_method->lookup(str);
+                       }
+                       catch (SymbolNotFound) {
+                               yyerrorfl(cur_input_file, str->line,
+                                         "\"%s\" is not a parameter of \"%s\".",
+                                         str->c_str(),
+                                         cur_method->get_fq_name()->flatten()->c_str());
+
+                               throw UserError();
+                       }
+                       
+                       Param *p = dynamic_cast<Param *>(sym);
+                       assert(p);
+                       
+                       Class::ParamInfo *pi = mi->add_param(p);
+                       pi->copy = true;
+                       mi->copy_params = true;
+               }
+       }
+;
+
+%%
+
+static Con dummy_con;
+
+void setup_cdlparse()
+{
+       yylval_con = &dummy_con;
+       yylval_string = &cdl_lval.string;
+}
+
+list<ClassRef> classes;
+
diff --git a/idlcomp/compileddef.h b/idlcomp/compileddef.h
new file mode 100644 (file)
index 0000000..0d9cb0f
--- /dev/null
@@ -0,0 +1,396 @@
+#ifndef IDLC_COMPILEDDEFS_H
+#define IDLC_COMPILEDDEFS_H
+
+#include <stdint.h>
+
+#if !defined(BITFIELD_LE) && !defined(BITFIELD_BE)
+#error Please define your bitfield endianness (BITFIELD_LE or BITFIELD_BE).
+#endif
+
+// This is the compiled representation of definitions when stored on
+// a legacy filesystem (as opposed to native object storage). 
+// Normally, a standard encoding would be used to map objects to
+// directories and files; however, such encodings cannot be used
+// before they exist.  This special mapping allows the system to be
+// bootstrapped from a legacy system.
+//
+// As this is a transitional representation, and IDL management is
+// not performance critical, emphasis has been placed on simplicity
+// rather than efficiency.  In particular, I don't want to write
+// throw-away code to compensate for most filesystems' inability to
+// deal with small files efficiently, nor do I want to muck around
+// with storing the compiled definitions in textual form (thus
+// needing to parse them *again*; I want to keep that out of the
+// ORB).  Dot files and directories are used rather than the more
+// straightforward way of keeping the namespace under its own member
+// in order to make the compiled definitions easier to browse with
+// normal utilities (in the final system, you'll be able to use a
+// custom view that arranges it however you want; however, Unix
+// doesn't have custom filesystem views).
+//
+// I have tried to make the definitions close to what could be
+// emitted by an IDL compiler, minus the hierarchical relationship
+// (which won't exist on a legacy filesystem).  These definitions
+// require C++ (plus C99 stdint.h); C will choke on the assumed
+// struct namespace behavior (among other things).
+//
+// Each definition is represented by a directory.  If the definition is
+// for a non-generic namespace (i.e. it is for a struct, interface,
+// bitfield, etc.), the definition itself is placed in a file called
+// ".self" inside the directory.  The file contains a CompiledDefHeader
+// struct followed by the appropriate type-specific struct.
+//
+// The non-dotted contents of the directory are the contents of the
+// definition's namespace, if any.  Anonymous types are given the
+// name "_anon_<unique>", where <unique> is an arbitrary non-conflicting
+// name.
+// 
+// A compiled definition file may be either big or little endian; if
+// magic_reversed is found, then all integers in the file must be
+// reversed prior to usage.
+//
+// Bitfields, on the other hand, must be arranged in a little endian
+// format (i.e. the first field gets the least significant bits; the byte
+// order of the containing integer is the same as for non-bitfield
+// integers).  Bitfields generated by idlc will contain padding and
+// reversed members if the target has big-endian bitfields.
+
+struct CompiledDefHeader {
+       static const uint32_t magic_normal = 0x2d29c8a9;
+       static const uint32_t magic_reversed = 0xa9c8292d;
+
+       uint32_t magic;        // magic_normal or magic_reversed
+
+       enum Type {
+               NameSpace,
+               BasicType,
+               Datum,
+               Interface,
+               Method,
+               Param,
+               Struct,
+               Enum,
+               BitField,
+               Alias,
+               TypeDef
+       };
+       
+       // One of the above types; the enum isn't used directly, as the
+       // size is not guaranteed.
+       int32_t type;
+};
+
+struct CompiledNameSpace {
+       // The length of the string in bytes (not characters), excluding
+       // the null terminator.
+
+       int32_t length;
+       
+       // The fully qualified name of the namespace follows, as a
+       // null-terminated UTF-8 string.  This allows the namespace
+       // to be mounted by merely supplying its filesystem path,
+       // without having to specify the mount point.
+};
+
+// This struct does not appear standalone, but is included in various
+// structs that can refer to arrays.
+struct CompiledArray {
+       // Inclusive lower and upper bounds of the array.  If the
+       // array has no upper bound, [1] should be -1.  If the array
+       // has no lower bound, [0] should be 0.  If this is not an
+       // array, both should be 0.
+       
+       int64_t bounds[2];
+};
+
+struct CompiledBasicType {
+       // Size of the type, as follows:
+       //   Byte: 8
+       //   Short: 16
+       //   Int: 32
+       //   Long: 64
+       //   FShort: 32
+       //   FLong: 64
+       //   Bool: 0 (actual size is implementation defined)
+       //
+       // Other sizes may be used within bitfields (up to a max of 64 bits).
+
+       int32_t bits;
+
+       // Unsigned, Float, and Bool are mutually exclusive.
+
+       union Flags {
+               struct Field {
+#ifdef BITFIELD_LE
+                       unsigned int Unsigned:1;
+                       unsigned int Float:1;
+                       unsigned int Bool:1;
+                       unsigned int TypeDef:1;
+#else
+                       unsigned int _pad:28;
+
+                       unsigned int TypeDef:1;
+                       unsigned int Bool:1;
+                       unsigned int Float:1;
+                       unsigned int Unsigned:1;
+#endif
+               } field;
+
+               uint32_t raw;
+               
+               struct init {
+                       static const uint32_t Unsigned = 1 << 0;
+                       static const uint32_t Float = 1 << 1;
+                       static const uint32_t Bool = 1 << 2;
+                       static const uint32_t TypeDef = 1 << 3;
+               };
+       } flags;
+       
+       uint64_t guid[2];
+       
+       CompiledArray array;
+};
+
+
+struct CompiledAlias {
+       // The length of the string in bytes (not characters), excluding
+       // the null terminator.
+
+       int32_t length;
+       
+       // The name of the aliased symbol follows, as a null-terminated
+       // UTF-8 string.  This will be the final symbol, and not an alias.
+};
+
+// A CompiledDatum is used for data fields in a structure, bitfield, or
+// enumeration.
+
+struct CompiledDatum {
+       // This is a description of the datum's type, if it is not a named type.
+       // This is ignored (except the array part) if "type" is non-empty.
+
+       CompiledBasicType basictype;
+       
+       // Const value(s); cast to the appropriate type.  For Bool, cast to
+       // Byte; 0 is false, 1 is true.  Extend all types to 64-bits.
+       // ucon is also used for bitfield element sizes.
+
+       union {
+               int64_t icon;
+               uint64_t ucon;
+               double fcon;
+               char data[8];
+       };
+
+       // Const can only be used for basic types (both named and unnamed);
+       // Immutable is used for non-initialized const fields.  Invalid is
+       // only used internally by idlc; it is reserved in the file format.
+       
+       union Flags {
+               struct Field {
+#ifdef BITFIELD_LE
+                       unsigned int Const:1;
+                       unsigned int Invalid:1;
+                       unsigned int Inline:1;
+                       unsigned int Immutable:1;
+#else
+                       unsigned int _pad:28;
+
+                       unsigned int Immutable:1;
+                       unsigned int Inline:1;
+                       unsigned int Invalid:1;
+                       unsigned int Const:1;
+#endif
+               } field;
+
+               uint32_t raw;
+
+               struct init {
+                       static const uint32_t Const = 1 << 0;
+                       static const uint32_t Invalid = 1 << 1;
+                       static const uint32_t Inline = 1 << 2;
+                       static const uint32_t Immutable = 1 << 3;
+               };
+       } flags;
+
+       // If it's a named type, this points to the type.
+       CompiledAlias type;
+};
+
+// Methods, member types, member constants, etc. go in the namespace. 
+// Methods are ordered; all others are unordered. 
+
+struct CompiledInterface {
+       int32_t num_methods;
+       int32_t num_supers;
+       
+       uint64_t guid[2];
+       
+       // An array of num_methods + num_supers length of CompiledAliases
+       // representing methods in declared order follows, and then
+       // superclasses in declared order, follows.  The names of metods shall
+       // consist only of the final component, with no namespaces prepended. 
+       // The names of superclasses are fully namespace-qualified.  Each
+       // CompiledAlias shall begin on a 4-byte boundary.
+};
+
+// Parameters go in the namespace, and are ordered according to the
+// list contained in this struct.  Exceptions thrown are unordered
+// Aliases in a subdirectory called ".exceptions", with arbitrary
+// names.
+
+struct CompiledMethod {
+       union Flags {
+               struct Field {
+#ifdef BITFIELD_LE
+                       unsigned int Async:1;
+#else
+                       unsigned int _pad:31;
+
+                       unsigned int Async:1;
+#endif
+               } field;
+
+               uint32_t raw;
+
+               struct init {
+                       static const uint32_t Async = 1 << 0;
+               };
+       } flags;
+
+       int32_t num_entries;
+       
+       // An array of num_entries length of CompiledAliases representing
+       // parameters in declared order follows.  Each name shall be as in
+       // CompiledInterface, without namespace qualification.
+};
+
+// A param isn't a namespace; however, it is a directory.  In addition to
+// the ".self" file, the directory contains a "type" Alias file.
+struct CompiledParam {
+       // This is a description of the parameter's type, if it is not a
+       // named type.  This is ignored (except the array part) if "type"
+       // is non-empty.
+
+       CompiledBasicType basictype;
+
+       union Flags {
+               struct Field {
+#ifdef BITFIELD_LE
+                       unsigned int In:1;
+                       unsigned int Out:1;
+                       
+                       unsigned int Shared:1;
+                       unsigned int Push:1;
+                       unsigned int Inline:1;
+                       unsigned int Immutable:1;
+#else
+                       unsigned int _pad:26;
+
+                       unsigned int Immutable:1;
+                       unsigned int Inline:1;
+                       unsigned int Push:1;
+                       unsigned int Shared:1;
+
+                       unsigned int Out:1;
+                       unsigned int In:1;
+#endif
+               } field;
+               
+               uint32_t raw;
+
+               struct init {
+                       static const uint32_t In = 1 << 0;
+                       static const uint32_t Out = 1 << 1;
+                       static const uint32_t Shared = 1 << 2;
+                       static const uint32_t Push = 1 << 3;
+                       static const uint32_t Inline = 1 << 4;
+                       static const uint32_t Immutable = 1 << 5;
+               };
+       } flags;
+
+       
+       // If it's a named type, this points to the type.
+       CompiledAlias type;
+};
+
+// Fields, member types, member constants, etc. go in the namespace. 
+// All but fields are unordered.
+
+struct CompiledStruct {
+       union Flags {
+               struct Field {
+#ifdef BITFIELD_LE
+                       // Struct has a superstruct.
+
+                       unsigned int Super:1;
+
+                       // Struct has run-time type information.  This requires that
+                       // Super be set.
+
+                       unsigned int Virtual:1;
+                       
+                       // Struct defaults to "inline" when declared in a struct.
+                       // This is mandatory for anonymous structs.
+                       
+                       unsigned int Inline:1;
+#else
+                       unsigned int _pad:29;
+
+                       unsigned int Inline:1;
+                       unsigned int Virtual:1;
+                       unsigned int Super:1;
+#endif
+               } field;
+               
+               uint32_t raw;
+
+               struct init {
+                       static const uint32_t Super = 1 << 0;
+                       static const uint32_t Virtual = 1 << 1;
+                       static const uint32_t Inline = 1 << 2;
+               };
+       } flags;
+
+       int32_t num_entries;
+
+       uint64_t guid[2];
+       
+       // An array of num_entries length of CompiledAliases representing
+       // fields in declared order follows.  Each name shall be as in
+       // CompiledMethod.  If the Super flag is set, then another
+       // fully-qualified CompiledAlias is placed at the end of the array.
+};
+
+// Enum entries are unsigned BasicDatums of the specified size.
+
+struct CompiledEnum {
+       // Size of enumeration type
+       int32_t bits;
+       
+       int32_t num_entries;
+       
+       uint64_t guid[2];
+
+       // An array of num_entries length of CompiledAliases representing
+       // values in declared order follows.  Each name shall be as in
+       // CompiledMethod.
+};
+
+// BitField fields are unsigned BasicDatums and Enum Datums of
+// arbitrary size, which must add up to at most "bits".
+
+struct CompiledBitField {
+       // Width of bitfield
+       int32_t bits;
+       
+       int32_t num_entries;
+
+       uint64_t guid[2];
+
+       // An array of num_entries length of CompiledAliases representing
+       // fields in declared order follows.  Each name shall be as in
+       // CompiledMethod.
+};
+
+#endif
diff --git a/idlcomp/idlc.h b/idlcomp/idlc.h
new file mode 100644 (file)
index 0000000..94f7ae2
--- /dev/null
@@ -0,0 +1,2227 @@
+// idlc.h -- Definitions used throughout idlc. 
+// A lot of this should probably be factored out into more specific headers.
+//
+// This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal with
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+// 
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimers.
+// 
+//     * Redistributions in binary form must reproduce the above copyright notice,
+//       this list of conditions and the following disclaimers in the
+//       documentation and/or other materials provided with the distribution.
+// 
+//     * The names of the Software's authors and/or contributors
+//       may not be used to endorse or promote products derived from
+//       this Software without specific prior written permission.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+// SOFTWARE.
+
+#ifndef IDLC_H
+#define IDLC_H
+
+// inttypes.h on OSX assumes it can use restrict, but C++ doesn't have it.
+// Hopefully C++ will catch up to C soon...
+
+#define restrict
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string>
+#include <stack>
+#include <list>
+#include <map>
+#include <vector>
+
+#include "compileddef.h"
+
+using std::list;
+using std::stack;
+using std::string;
+using std::pair;
+using std::map;
+using std::vector;
+
+int yylex();
+int idl_lex();
+#define cdl_lex idl_lex
+void idl_error(char *s);
+
+#define cdl_error idl_error
+
+void setup_idlparse();
+void setup_cdlparse();
+
+#ifdef __GNUC__
+void yyerrorf(const char *s, ...) __attribute__((format(printf, 1, 2)));
+void yyerrorfl(const char *file, int line, const char *s, ...)
+__attribute__((format(printf, 3, 4)));
+#else
+void yyerrorf(const char *s, ...);
+void yyerrorfl(const char *file, int line, const char *s, ...);
+#endif
+
+int idl_parse();
+int cdl_parse();
+int finish_lex();
+
+extern int idl_debug, cdl_debug, curline, impl, num_err;
+extern int current_pass;
+extern FILE *yyin;
+extern unsigned int enum_pos;
+extern const char *cur_input_file;
+extern int compiling_idl, compiling_cdl;
+extern bool makedep;
+
+struct InternalError
+{
+       // Location in the IDLC source of the BUG()
+       const char *file;
+       int line;
+       
+       InternalError(const char *file, int line) :
+       file(file), line(line)
+       {
+       }
+};
+
+#define BUG() do { \
+       throw InternalError(__FILE__, __LINE__); \
+} while (0)
+
+#undef assert
+
+#define assert(x) do { \
+       if (!(x)) BUG(); \
+} while (0)
+
+class Releasable {
+public:
+       mutable int count;
+       
+       virtual ~Releasable()
+       {
+       }
+       
+       Releasable(int COUNT) : count(COUNT)
+       {
+       }
+
+       void release() const
+       {
+               if (count <= 0) {
+                       fprintf(stderr, "Reference count is %d in release\n", count);
+                       BUG();
+               }   
+               
+               if (!--count)
+                       delete this;
+       }
+};
+
+class AutoReleasePool {
+       list<const Releasable *> pool;
+
+public:
+       void add(const Releasable *obj)
+       {
+               pool.push_back(obj);
+       }
+       
+       void clean()
+       {
+               for (list<const Releasable *>::iterator i = pool.begin();
+                    i != pool.end(); ++i)
+               {
+                       const Releasable *obj = *i;
+                       obj->release();
+               }
+               
+               pool.clear();
+       }
+};
+
+extern AutoReleasePool autorelease_pool;
+
+template <typename T>
+class RefCountable : public Releasable {
+private:
+       // RefCountable objects should never be assigned to,
+       // as there could be references to the object remaining.
+       // The private assignment operator prevents this, unless
+       // a subclass defines its own assigment operator (don't
+       // do that).
+       void operator =(const RefCountable &rc)
+       {
+               BUG();
+       }
+
+       RefCountable(const RefCountable &rc) : Releasable(1)
+       {
+               BUG();
+       }
+       
+public:
+       RefCountable() : Releasable(1)
+       {
+               // Normally, this wouldn't be automatic, but this is what most
+               // things in IDLC are going to want, and it elimanates problems
+               // with needing to cast the return type of autorelease().
+               //
+               // The automatic autorelease() means that all refcountable objects
+               // must be allocated with "new", not on the stack, as global
+               // data, or as a class member.
+               
+               autorelease();
+       }
+       
+       virtual ~RefCountable()
+       {
+       }
+
+       const T *retain() const
+       {
+               if (count <= 0) {
+                       fprintf(stderr, "Reference count is %d in retain\n", count);
+                       BUG();
+               }   
+               
+               count++;
+               return static_cast<const T *>(this);
+       }
+
+       T *retain()
+       {
+               if (count <= 0) {
+                       fprintf(stderr, "Reference count is %d in retain\n", count);
+                       BUG();
+               }   
+               
+               count++;
+               return static_cast<T *>(this);
+       }
+       
+       const T *autorelease() const
+       {
+               autorelease_pool.add(static_cast<Releasable *>(this));
+               return static_cast<T *>(this);
+       }
+
+       T *autorelease()
+       {
+               autorelease_pool.add(static_cast<Releasable *>(this));
+               return static_cast<T *>(this);
+       }
+       
+       // This is only here because C++ obnoxiously requires it to
+       // be just because it's "called" from code excluded with an
+       // if (0) in a template.  No code is ever generated that calls
+       // it, but it still must exist.
+       
+       bool operator < (const RefCountable &rc)
+       {
+               BUG();
+       }
+};
+
+// T must be RefCountable
+template<typename T, bool compare_ptrs = true>
+class Ref {
+       // STL containers like to use const on the items they
+       // contain; the mutable allows such containers to hold
+       // pointers to non-const data.  For truly const Refs,
+       // make T const, as in StringRef.  Unfortunately,
+       // it cannot be done in a more fine-grained manner,
+       // AFAICT.
+
+public:        
+       mutable T *data;
+       
+public:
+       Ref()
+       {
+               data = NULL;
+       }
+
+       Ref(T *data) : data(data)
+       {
+               if (data)
+                       data->retain();
+       }
+       
+       Ref(Ref &le) : data(le.data)
+       {
+               if (data)
+                       data->retain();
+       }
+
+       Ref &operator =(const Ref &le)
+       {
+               // The retain must come first, in case both Refs are the same
+               if (le.data)
+                       le.data->retain();
+               if (data)
+                       data->release();
+
+               data = le.data;
+               
+               return *this;
+       }
+
+       Ref &operator =(T *new_data)
+       {
+               // The retain must come first, in case both Refs are the same
+               if (new_data)
+                       new_data->retain();
+               if (data)
+                       data->release();
+
+               data = new_data;
+               
+               return *this;
+       }
+       
+       ~Ref()
+       {
+               if (data)
+                       data->release();
+       }
+       
+       operator T *() const
+       {
+               return data;
+       }
+       
+       operator T &() const
+       {
+               return *data;
+       }
+       
+       T *operator *() const
+       {
+               return data;
+       }
+
+       T *operator ->() const
+       {
+               return data;
+       }
+       
+       bool operator == (const Ref &le) const
+       {
+               if (compare_ptrs)
+                       return data == le.data;
+               else
+                       return *data == *le.data;
+       }
+
+       bool operator != (const Ref &le) const
+       {
+               if (compare_ptrs)
+                       return data != le.data;
+               else
+                       return *data != *le.data;
+       }
+
+       bool operator < (const Ref &le) const
+       {
+               if (compare_ptrs)
+                       return reinterpret_cast<intptr_t>(data) < 
+                              reinterpret_cast<intptr_t>(le.data);
+               else
+                       return *data < *le.data;
+       }
+};
+
+class String : public string, public RefCountable<String> {
+public:
+       // Origin of the string, if from the IDL file.
+       // Otherwise, fill both values with zero.
+
+       const char *file;
+       int line;
+       int token;
+
+       String(const char *s = "") : string(s)
+       {
+               file = "";
+               line = 0;
+               token = 0;
+       }
+
+       String(const String &s) : string(s)
+       {
+               file = s.file;
+               line = s.line;
+               token = s.token;
+       }
+
+       String(const char *s, const char *file, int line, int token) :
+              string(s), file(file), line(line), token(token)
+       {}
+};
+
+extern String **yylval_string;
+typedef Ref<const String, false> StringRef;
+
+/* If a StrList is used for a namespace-qualified identifier, and
+   said identifier begins with ".." (i.e. starts from the root
+   namespace), the leading ".." is represented by a zero-length
+   String. 
+   
+   Note that list doesn't have a virtual destructor, so all deletions
+   should happen through either RefCountable or the wrapper List
+   class. */
+
+class StrList : public list<StringRef>, public RefCountable<StrList> {
+public:
+       StrList()
+       {
+       }
+       
+       // Parse a flat String into a StrList, using the specified delimeter.
+       StrList(const String *input, char delimiter = '.');
+
+       // Turn a StrList into a flat String, using the specified delimiter.
+       String *flatten(const char *delimiter = ".");
+};
+
+typedef Ref<StrList> StrListRef;
+
+// ConList is like StrList, but with constant initializers
+
+class Datum;
+
+struct Con {
+       union {
+               int64_t icon;
+               uint64_t ucon;
+               StrList *dcon;
+               
+               // FIXME: handle platforms with weird floating point endianness
+               double fcon;
+               
+               char data[8];
+       } con;
+       
+       // TOK_ICON, TOK_UCON, TOK_FCON, TOK_BOOL, TOK_DCON, 
+       // TOK_INVALID, or TOK_NONE
+       // Constants are stored as signed (ICON) unless too
+       // large to fit in a signed 64-bit integer.  Additional size and
+       // signedness checks are mode when down casting to a smaller size
+       // to fit into a particular datum; such constants will have a
+       // value of zero.
+       //
+       // TOK_NONE is valid for maybeconst and size.  TOK_INVALID
+       // indicates a previously detected error; don't emit any further
+       // errors due to this constant.
+       //
+       // TOK_DCON is used for symbolic constants, whose value may
+       // not yet be known.
+       
+       int type;
+};
+
+extern Con *yylval_con;
+
+struct ConInit {
+       StringRef str;
+       Con con;
+
+       ConInit(const String *str, Con &con) : str(str), con(con)
+       {
+       }
+
+       ConInit(const ConInit &coninit)
+       {
+               *this = coninit;
+       }
+};
+
+class ConList : public list<ConInit>, public RefCountable<ConList> {};
+typedef Ref<ConList> ConListRef;
+
+// Like StrList, but is a list of possibly namespace-qualified identifiers.
+class IDList : public list<StrListRef>, public RefCountable<IDList> {};
+typedef Ref<IDList> IDListRef;
+
+
+class NameSpace;
+class LangCallback;
+
+// This is incremented when a chain of symbols is traversed to reset
+// detection of already-visited symbols.  It is assumed that this
+// will not have to happen billions of times.
+
+extern int traversal;
+
+class Symbol : public RefCountable<Symbol> {
+       NameSpace *ns;
+       
+public:
+       StringRef name;
+
+       // Symbol was loaded externally
+       bool external;
+       
+       // If set, the symbol is private, and will not be available
+       // for lookups except those made directly in the context of
+       // the containing namespace.  Private symbols do not get
+       // outputted.  They are used to implement imports of specific
+       // symbols (as aliases), rather than entire namespaces.
+       
+       bool priv;
+       
+       // This is set to ::traversal when this symbol is visited along a chain.
+       // If a target needs more than 8 simultaneous chains, increase the size
+       // of the array.  These traversals are reserved for language binding use.
+       
+       int traversed[8];
+
+       Symbol()
+       {
+               ns = NULL;
+               external = false;
+               priv = false;
+               
+               memset(traversed, 0, sizeof(traversed));
+       }
+       
+       Symbol(const String *_name) : name(_name)
+       {
+               if (_name)
+                       name->retain();
+
+               ns = NULL;
+               external = false;
+               priv = false;
+
+               memset(traversed, 0, sizeof(traversed));
+       }
+
+       virtual ~Symbol();
+
+       NameSpace *get_ns() const
+       {
+               return ns;
+       }
+
+       const String *get_name() const
+       {
+               return name;
+       }
+       
+       // If append is non-NULL, it is appended to non-user namespaces,
+       // to facilitate a language binding that cannot place nested types
+       // in the same language construct as the actual interface.  The
+       // recommended suffix is "_ns", which is a reserved ending in the
+       // IDL.  No suffix is placed on the final component of the name,
+       // even if it is a non-user namespace.  The not_last field is used
+       // to detect whether it is the final component; it is only used
+       // internally, and should always be false when called externally.
+       //
+       // This function does *NOT* add a null first element to indicate
+       // that the name is fully qualified.  If you need that, you have
+       // to add it yourself (call 'push_front(new String(""))' on the
+       // result).
+       
+       StrList *get_fq_name(const char *append = NULL,
+                            bool not_last = false) const;
+       
+       virtual void lookup_imports()
+       {
+       }
+       
+       virtual void lookup_chain()
+       {
+       }
+
+       virtual void lookup_misc()
+       {
+       }
+       
+       virtual void final_analysis()
+       {
+       }
+
+       // These two methods must be implemented by all IDL symbols, but are
+       // not needed in CDL symbols (and therefore are not pure virtual).
+
+       virtual void output(const char *root)
+       {
+       }
+
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL)
+       {
+       }
+       
+       // Find and return the topmost symbol other than a user namespace
+       // containing this symbol.  If this symbol's parent is a user
+       // namespace, it returns itself.  May not be called on the
+       // toplevel namespace.
+       
+       Symbol *find_toplevel_type();
+
+       // Get the true type of the symbol, regardless of whether it is an
+       // alias.
+       
+       virtual Symbol *get_concrete_sym(bool follow_typedefs = true)
+       {
+               return this;
+       }
+       
+       friend class NameSpace;
+};
+
+typedef Ref<Symbol> SymbolRef;
+
+class SymList : public list<SymbolRef>, public RefCountable<SymList> {};
+typedef Ref<SymList> SymListRef;
+
+struct SymbolNotFound {
+};
+
+struct DuplicateSymbol {
+};
+
+struct InvalidArgument {
+};
+
+struct UserError {
+};
+
+typedef Ref<NameSpace> NameSpaceRef;
+
+class NameSpace : public virtual Symbol {
+protected:
+       // Filesystem path to the external symbol storage, or NULL
+       // if not an import namespace.
+       //
+       // Import namespaces are read-only namespaces which are created
+       // for the importation of externally-declared symbols.  One is
+       // initially created for each "mount point" specified by the user;
+       // whenever such a namespace is searched, it checks the external
+       // storage, and if the lookup succeeds, the symbol is loaded and
+       // added to the namespace.  Failed lookups could be cached with a
+       // special BadSymbol or some such, as the imported namespace is
+       // assumed to be constant, but I don't think such an optimization
+       // is worthwhile, at least at this point.
+       
+       StringRef path;
+
+       // Load a symbol from external storage, constructing the relevant type
+       // of object, and adding it to this namespace.  Only called for
+       // import namespaces.
+
+       Symbol *load(const String *symname);
+
+       typedef map<StringRef, SymbolRef> tbl_type;
+       tbl_type tbl;
+       
+       list<StrListRef> import_strs;
+       list<NameSpaceRef> imports;
+
+public:
+       // This is set in the destructor, so the contents of the namespace
+       // don't try to remove themselves from the namespace when destructed
+       // due to the destruction of map.
+       int dying;
+       
+       // This is a counter for generating unique names for anonymous
+       // members of the namespace.
+       int anon;
+       
+       NameSpace() : dying(0), anon(0)
+       {
+       }
+       
+       virtual ~NameSpace()
+       {
+               dying = 1;
+       }
+       
+       // Return a description of the type of namespace, for
+       // error messages.
+       virtual const char *description()
+       {
+               return "namespace";
+       }
+
+       virtual void output(const char *root);
+
+       typedef tbl_type::const_iterator const_iterator;
+       typedef tbl_type::value_type value_type;
+
+       // Derived classes can throw InvalidArgument if you give them
+       // a type of Symbol that they don't accept; see their comments
+       // for more details.  Unfortunately, this cannot be done
+       // with static type-checking, as there are places that know
+       // they've been given a namespace that can accept a particular
+       // type of symbol, but they don't know exactly what kind of
+       // namespace it is.  C++'s type system is not sufficient to
+       // express this (at least not while retaining any semblance
+       // of sanity).
+
+       // DuplicateSymbol is thrown if sym already exists in this namespace.
+       virtual void add(Symbol *sym, bool from_import)
+       {
+               if (path && !from_import)
+                       throw InvalidArgument();
+               
+               if (path)
+                       sym->external = true;
+               
+               if (!sym->name.data)
+                       BUG();
+
+               pair<const_iterator, bool> ret = tbl.insert(value_type(sym->name, sym));
+               
+               if (ret.second)
+                       sym->ns = this;
+               else {
+                       throw DuplicateSymbol();
+               }
+       }
+
+       // Add the symbol to this namespace, handling duplicate symbols by
+       // printing an error and throwing a UserError().  This should not be
+       // done in the symbol's constructor, as the parent may not accept
+       // the symbol until it is fully constructed (the RTTI information
+       // changes, and in general partially constructed objects shouldn't
+       // be exposed to the rest of the system).
+
+       void add_user(Symbol *sym);
+
+       // Like add_user, but used by the import code.  Duplicate
+       // symbols result in internal errors, and the add is done with
+       // from_import set to true.  InvalidArgument results in an error
+       // message and a reraise as UserError.
+       //
+       // All conditions checked by the parent namespace's add() method
+       // (such as constness of data) must be satisfied prior to calling
+       // add_import().  On the other hand, add_import() must be called
+       // before any recursive importation is done which could
+       // conceivably try to import the current symbol (thus causing
+       // infinite recursion).
+
+       void add_import(Symbol *sym, const char *filename);
+
+       // SymbolNotFound is thrown if sym is not in this namespace.
+       virtual void del(Symbol *sym)
+       {
+               fprintf(stderr, "Removing symbol %s\n",
+                       sym->get_fq_name()->flatten()->c_str());
+       
+               if (tbl.erase(sym->name) == 0)
+                       throw SymbolNotFound();
+               
+               sym->ns = NULL;
+       }
+
+private:       
+       Symbol *lookup_noex_noimport(const String *symname)
+       {
+               const_iterator ret = tbl.find(symname);
+               
+               if (ret != tbl.end())
+                       return (*ret).second;
+               
+               return NULL;
+       }
+
+public:
+       Symbol *lookup_noex(const String *symname, bool priv_ok = false)
+       {
+               Symbol *ret = NameSpace::lookup_noex_noimport(symname);
+
+               if (path && !ret)
+                       ret = load(symname);
+               
+               if (ret && !priv_ok && ret->priv)
+                       return NULL;
+       
+               return ret;
+       }
+
+       Symbol *lookup(const String *symname, bool priv_ok = false)
+       {
+               Symbol *ret = lookup_noex(symname, priv_ok);
+               
+               if (!ret)
+                       throw SymbolNotFound();
+               
+               return ret;
+       }
+       
+       // Like lookup_noex, but also checks imported namespaces,
+       // and returns the namespace containing the match rather
+       // than the match itself.
+       
+       NameSpace *search(const String *name, Symbol *exclude);
+       
+       void add_search(StrList *ns)
+       {
+               import_strs.push_back(ns);
+       }
+
+       // Return a string containing case information manglement.
+       // See input.cc for more information.
+
+       static const String *mangle(const String *name);
+
+       const String *get_path()
+       {
+               return path;
+       }
+       
+       // Import all members of this namespace.  A no-op if not an import
+       // namespace.  
+       void import_all();
+       
+       // As import_all, but also recursively applies to any sub-namespaces.
+       void import_all_recursive();
+       
+       const_iterator begin()
+       {
+               return tbl.begin();
+       }
+
+       const_iterator end()
+       {
+               return tbl.end();
+       }
+
+       virtual void lookup_imports();
+
+       virtual void lookup_chain()
+       {
+               for (const_iterator i = begin(); i != end(); ++i) {
+                       Symbol *sym = (*i).second;
+                       sym->lookup_chain();
+               }
+       }
+
+       virtual void lookup_misc()
+       {
+               for (const_iterator i = begin(); i != end(); ++i) {
+                       Symbol *sym = (*i).second;
+                       sym->lookup_misc();
+               }
+       }
+
+       virtual void final_analysis()
+       {
+               for (const_iterator i = begin(); i != end(); ++i) {
+                       Symbol *sym = (*i).second;
+                       sym->final_analysis();
+               }
+       }
+};
+
+
+extern NameSpaceRef cur_nspace;
+extern list<NameSpaceRef> nspace_stack;
+       
+typedef std::vector<StringRef> StringVec;
+
+string *stringvec_to_path(StringVec &stringvec, const char *prepend);
+
+// lookup_sym and lookup_type throw UserError on user error
+// The context namespace is required for the proper
+// set of namespaces to be searched.
+
+Symbol *lookup_sym(NameSpace *topns, StrList *name, NameSpace *ctx,
+                   Symbol *exclude = NULL);
+
+class Def {
+       const char *self;   // Pointer to the type-specific struct
+       int self_len;       // Length of the type-specific struct
+       
+protected:
+       CompiledDefHeader hdr;
+
+       // sym is the symbol from which to get the path/name, and
+       // dir is true if it should be "name/.self" rather than 
+       // "name".
+       void output_self(const char *dir, Symbol *sym, bool dir);
+
+public:
+       Def(const char *self, int self_len, CompiledDefHeader::Type type) :
+       self(self), self_len(self_len)
+       {
+               hdr.magic = CompiledDefHeader::magic_normal;
+               hdr.type = type;
+       }
+       
+       virtual ~Def()
+       {
+       }
+       
+       // Specific types may override this to output extra data
+       // to the .self file without having to reopen the file.
+       //
+       // Returns false on error.
+       
+       virtual bool output_extra(FILE *f)
+       {
+               return true;
+       }
+};
+
+// Internal context struct used by input.cc to avoid passing
+// lots of parameters around
+struct ImportContext;
+
+// This represents an actual IDL namespace {}, rather than
+// a derived namespace such as a Struct or Interface.
+
+class UserNameSpace : public NameSpace, public Def {
+public:
+       CompiledNameSpace def;
+       StringRef mountpoint_name;
+
+       UserNameSpace(const String *name = NULL) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::NameSpace)
+       {
+               mountpoint_name = get_fq_name()->flatten();
+               def.length = mountpoint_name->length();
+       }
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+       
+       static void declare_import(const char *path);
+
+       static UserNameSpace *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+};
+
+typedef Ref<UserNameSpace> UserNameSpaceRef;
+extern UserNameSpaceRef toplevel, cdl_toplevel;
+extern UserNameSpace *output_ns;
+
+class Type : public virtual Symbol {
+public:
+       Type()
+       {
+       }
+       
+       virtual ~Type()
+       {
+       }
+       
+       virtual int get_default_bf_size()
+       {
+               // Only allow types in bitfields that explicitly
+               // support it.
+               
+               return -1;
+       }
+};
+
+typedef Ref<Type> TypeRef;
+
+// ctx can be NULL if basic_types_only is true
+Type *lookup_type(StrList *sl, NameSpace *ctx, bool basic_types_only = false);
+
+class BasicType : public Type, public Def {
+public:
+       CompiledBasicType def;
+       bool complete;
+       
+       BasicType(const String *name) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::BasicType)
+       {
+               complete = false;
+               memset(&def, 0, sizeof(def));
+       }
+
+       void init(CompiledBasicType &DEF)
+       {
+               assert(!complete);
+       
+               def = DEF;
+               complete = true;
+       }
+
+       static BasicType *declare(const String *name, NameSpace *parent,
+                                 CompiledBasicType &DEF)
+       {
+               BasicType *bt = new BasicType(name);
+               bt->init(DEF);
+       
+               if (parent)
+                       parent->add_user(bt);
+
+               return bt;
+       }
+
+       virtual void output(const char *root);
+
+       static BasicType *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+};
+
+static inline bool is_array(CompiledBasicType &bt)
+{
+       return bt.array.bounds[0] || bt.array.bounds[1];
+}
+
+static inline bool is_array(CompiledBasicType *bt)
+{
+       return is_array(*bt);
+}
+
+typedef Ref<Datum> DatumRef;
+
+class Array : public RefCountable<Array>
+{
+       NameSpace *lookup_ctx;
+       DatumRef datums[2];
+
+       // lower is [0], upper is [1]
+       StrListRef dcons[2];
+       Con cons[2];
+       
+       // Strings for error reporting on each constant.  If the constant
+       // is symbolic, then this is the fully qualified symbol name. 
+       // Otherwise, it is the numerical value converted to a string.  In
+       // each case, the file/line shall correspond to where the array
+       // bound was specified.
+       
+       StringRef strs[2];
+       
+public:
+       // ca is not valid until after final_analysis() is called.
+       CompiledArray ca;
+
+       Array(NameSpace *LOOKUP_CTX);
+       void set_bound(Con &con, int bound);
+       void final_analysis();
+
+       void set_unbounded();
+};
+
+typedef Ref<Array> ArrayRef;
+
+class Datum : public Symbol, public Def {
+       StrListRef type_name;
+       SymbolRef type_sym;
+       StringRef type_fq_name;
+
+       StrListRef const_val_name;
+       SymbolRef const_val_sym;
+       DatumRef const_val;
+
+       bool basic;      // Datum is of a BasicType
+       bool complete;
+       bool const_init; // Datum's constant has been initialized; this is
+                        // true after a successful verify_const().
+       CompiledBasicType *cbt;
+       
+       ArrayRef array;
+
+       int chain_traversed;
+
+       // Recursively retrieve the actual value of a const datum
+       // initialized with another named const datum.  Returns
+       // the "end" of an infinite loop, if one is found.  Once
+       // the full infinite loop has been printed, UserError is
+       // thrown.
+
+       Datum *resolve_constant_chain();
+
+       void init_const_early(Con *con);
+       
+       void use_anon_type(const CompiledBasicType &CBT)
+       {
+               def.basictype = CBT;
+               cbt = &def.basictype;
+               basic = true;
+               type = NULL;
+               def.type.length = 0;
+       }
+       
+       void process_type();
+       
+       void set_array(Array *ARRAY)
+       {
+               if (ARRAY)
+                       array = ARRAY;
+       }
+
+public:
+       CompiledDatum def;
+       TypeRef type;
+
+       int con_type;    // Used to store the TOK_[IUF]CON of the Con struct
+                        // for type checking once the type is known.
+
+       Datum(const String *name) :
+       Symbol(name), 
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::Datum)
+       {
+               complete = false;
+               const_init = false;
+               chain_traversed = 0;
+               memset(&def, 0, sizeof(def));
+       }
+
+       void init(StrList *type, Array *ARRAY, Con *con = NULL);
+       void init(CompiledBasicType &cbt, Array *ARRAY, Con *con = NULL);
+
+       static Datum *declare(const String *name, NameSpace *parent,
+                             StrList *type, Array *ARRAY,
+                             Con *con = NULL)
+       {
+               assert(parent);
+               
+               Datum *d = new Datum(name);
+               d->init(type, ARRAY, con);
+
+               parent->add_user(d);
+               return d;
+       }
+
+       static Datum *declare(const String *name, NameSpace *parent,
+                             CompiledBasicType &type, 
+                             Array *ARRAY, Con *con = NULL)
+       {
+               assert(parent);
+       
+               Datum *d = new Datum(name);
+               d->init(type, ARRAY, con);
+
+               parent->add_user(d);
+               return d;
+       }
+       
+       void set_inline()
+       {
+               def.flags.field.Inline = 1;
+       }
+       
+       bool is_inline()
+       {
+               return def.flags.field.Inline;
+       }
+       
+       void set_immutable()
+       {
+               def.flags.field.Immutable = 1;
+       }
+       
+       bool is_immutable()
+       {
+               return def.flags.field.Immutable;
+       }
+
+       // Returns true if the constant was acceptable, false otherwise (an
+       // error is also output to the user in this case).
+       
+       bool verify_const();
+
+       virtual void lookup_chain()
+       {
+               assert(complete);
+
+               if (const_val_name)
+                       const_val_sym = lookup_sym(toplevel, const_val_name, get_ns());
+
+               if (type_name) {
+                       assert(!basic);
+                       type_sym = lookup_type(type_name, get_ns(), true);
+                       if (!type_sym)
+                               type_sym = lookup_sym(toplevel, type_name, get_ns());
+               } else {
+                       assert(basic);
+               }
+       }
+
+       virtual void lookup_misc()
+       {
+               if (def.flags.field.Const) {
+                       if (!const_init) {
+                               assert(def.flags.field.Const);
+       
+                               traversal++;
+                               Datum *d = resolve_constant_chain();
+                               assert(!d);
+                       }
+               
+                       assert(const_init);
+               } else {
+                       process_type();
+               }
+       }
+       
+       virtual void final_analysis();
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static Datum *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+
+       uint64_t get_ucon(const String *err_str)
+       {
+               if (!def.flags.field.Const) {
+                       yyerrorfl(err_str->file, err_str->line, 
+                                 "\"%s\" is not a const Datum.\n",
+                                 get_fq_name()->flatten()->c_str());
+                       throw UserError();
+               }
+               
+               assert(const_init);
+               return def.ucon;
+       }
+
+       bool is_array()
+       {
+               return ::is_array(def.basictype);
+       }
+};
+
+template<typename T>
+bool output_list(T *sym, FILE *f);
+
+class BitField : public NameSpace, public Type, public Def {
+       list<DatumRef> entries;
+
+       void add_elem(Datum *d);
+
+public:
+       CompiledBitField def;
+       int cur_pos;
+
+       BitField(const String *name) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::BitField)
+       {
+               memset(&def, 0, sizeof(def));
+       }
+
+       void init(int bits, NameSpace *parent)
+       {
+               if (bits < 0 || bits > 64) {
+                       yyerrorf("\"%s\" has invalid bitfield size %d", 
+                                name->c_str(), bits);
+
+                       bits = bits < 0 ? 0 : 64;
+               }
+       
+               def.bits = bits;
+       }
+
+       static BitField *declare(const String *name, NameSpace *parent,
+                                int bits)
+       {
+               assert(parent);
+               
+               BitField *bf = new BitField(name);
+               bf->init(bits, parent);
+       
+               parent->add_user(bf);
+               return bf;
+       }
+
+       // Only integral Datums, Enums, and BitFields can be added.
+
+       void add(Symbol *sym, bool from_import);
+
+       virtual const char *description()
+       {
+               return "bitfield";
+       }
+
+       virtual void lookup_misc()
+       {
+               NameSpace::lookup_misc();
+       }
+       
+       virtual void final_analysis()
+       {
+               // FIXME: check total size of elements
+       
+               NameSpace::final_analysis();
+       }
+
+       int get_default_bf_size()
+       {
+               return def.bits;
+       }
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static BitField *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+
+       typedef list<DatumRef>::const_iterator entries_iterator;
+       typedef list<DatumRef>::const_reverse_iterator entries_reverse_iterator;
+
+       entries_iterator entries_begin()
+       {
+               return entries.begin();
+       }
+
+       entries_iterator entries_end()
+       {
+               return entries.end();
+       }
+
+       entries_reverse_iterator entries_rbegin()
+       {
+               return entries.rbegin();
+       }
+
+       entries_reverse_iterator entries_rend()
+       {
+               return entries.rend();
+       }
+};
+
+class Struct;
+typedef Ref<Struct> StructRef;
+extern Struct *System_VStruct;
+
+// FIXME: typedefed superstructs
+class Struct : public NameSpace, public Type, public Def {
+       list<DatumRef> entries;
+       StructRef super;
+       SymbolRef supersym;
+       StrListRef supername;
+       bool attrs_resolved;
+       
+       void add_elem(Datum *d);
+
+       void resolve_attrs()
+       {
+               if (attrs_resolved)
+                       return;
+               
+               if (super && !super->attrs_resolved)
+                       super->resolve_attrs();
+               
+               if (super && super->def.flags.field.Virtual)
+                       def.flags.field.Virtual = 1;
+               
+               attrs_resolved = true;
+       }
+       
+public:
+       CompiledStruct def;
+       
+       // This is not maintained by the generic code, but can be
+       // used by language bindings to cache the result of the
+       // summation. 
+       
+       int chainlen;
+       
+       Struct(const String *name) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::Struct)
+       {
+               memset(&def, 0, sizeof(def));
+               attrs_resolved = 0;
+       }
+       
+       void init(StrList *SUPERNAME)
+       {
+               supername = SUPERNAME;
+       }
+
+       static Struct *declare(const String *name, NameSpace *parent,
+                              StrList *SUPERNAME)
+       {
+               assert(parent);
+       
+               Struct *st = new Struct(name);
+               st->init(SUPERNAME);
+
+               parent->add_user(st);
+               return st;
+       }
+       
+       void set_virtual()
+       {
+               def.flags.field.Virtual = 1;
+       }
+
+       void set_inline()
+       {
+               def.flags.field.Inline = 1;
+       }
+
+       bool is_virtual()
+       {
+               return def.flags.field.Virtual;
+       }
+
+       bool is_inline()
+       {
+               return def.flags.field.Inline;
+       }
+       
+       // Only Datums and Types can be added.
+
+       void add(Symbol *sym, bool from_import);
+
+       virtual const char *description()
+       {
+               return "struct";
+       }
+       
+       Struct *get_super()
+       {
+               assert(current_pass >= 4);
+               return super;
+       }
+
+       virtual void lookup_chain()
+       {
+               if (supername) {
+                       supersym = lookup_sym(toplevel, supername, get_ns());
+                       assert(supersym);
+               }
+
+               NameSpace::lookup_chain();
+       }
+
+private:
+       void lookup_super()
+       {
+               if (supersym && !super) {
+                       super = dynamic_cast<Struct *>(supersym->get_concrete_sym());
+               
+                       if (!super) {
+                               const String *str = supername->back();
+                               yyerrorfl(str->file, str->line,
+                                         "\"%s\" is not a struct.",
+                                         supersym->get_fq_name()->flatten()->c_str());
+                       }
+                       
+                       def.flags.field.Super = 1;
+                       super->lookup_super();
+                       
+                       if (super->is_virtual())
+                               set_virtual();
+               }
+               
+               if (is_virtual() && !supersym && !super) {
+                       assert(System_VStruct);
+                       if (this != System_VStruct) {
+                               def.flags.field.Super = 1;
+                               super = System_VStruct;
+                       }
+               }
+       }
+       
+public:
+       virtual void lookup_misc()
+       {
+               lookup_super();
+
+               if (is_virtual() && def.guid[0] == 0 && def.guid[1] == 0)
+                       yyerrorfl(name->file, name->line,
+                                 "Virtual struct \"%s\" is missing a GUID.",
+                                 get_fq_name()->flatten()->c_str());
+
+               NameSpace::lookup_misc();
+       }
+       
+       virtual void final_analysis()
+       {
+               // FIXME: check for infinite loops in struct inheritance
+
+               resolve_attrs();
+               NameSpace::final_analysis();
+       }
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static Struct *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+
+       typedef list<DatumRef>::const_iterator entries_iterator;
+
+       entries_iterator entries_begin()
+       {
+               return entries.begin();
+       }
+
+       entries_iterator entries_end()
+       {
+               return entries.end();
+       }
+
+       void set_guid(uint64_t guid[2])
+       {
+               if (def.guid[0] || def.guid[1])
+                       yyerrorf("\"%s\" already has a GUID.",
+                                get_fq_name()->flatten()->c_str());
+       
+               def.guid[0] = guid[0];
+               def.guid[1] = guid[1];
+       }
+};
+
+class Param : public Symbol, public Def {
+       StrListRef type_name;
+       StringRef type_fq_name;
+
+       bool basic;      // Datum is of a BasicType
+       bool complete;
+       
+       ArrayRef array;
+
+       void use_named_type(BasicType *bt)
+       {
+               assert(!bt || bt->def.flags.field.TypeDef);
+               
+               basic = false;
+
+               type_fq_name = type->get_fq_name()->flatten();
+               def.type.length = type_fq_name->length();
+       }
+       
+       void use_anon_type(const CompiledBasicType &cbt)
+       {
+               def.basictype = cbt;
+               basic = true;
+               type = NULL;
+       }
+
+       void set_array(Array *ARRAY)
+       {
+               if (ARRAY)
+                       array = ARRAY;
+       }
+
+public:
+       CompiledParam def;
+       TypeRef type;
+
+       Param(const String *name) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::Param)
+       {
+               memset(&def, 0, sizeof(def));
+       }
+
+       void init(StrList *TYPE, CompiledParam::Flags flags, Array *ARRAY)
+       {
+               type_name = TYPE;
+               def.flags = flags;
+               set_array(ARRAY);
+       }
+
+       static Param *declare(const String *name, NameSpace *parent,
+                             StrList *TYPE, CompiledParam::Flags flags,
+                             Array *ARRAY);
+
+       virtual void lookup_misc()
+       {
+               type = lookup_type(type_name, get_ns());
+       }
+       
+       virtual void final_analysis()
+       {
+               BasicType *bt = dynamic_cast<BasicType *>(type->get_concrete_sym());
+
+               if (bt && !bt->def.flags.field.TypeDef) {
+                       use_anon_type(bt->def);
+               } else {
+                       use_named_type(bt);
+
+                       Struct *str = dynamic_cast<Struct *>(*type);
+                       if (str && str->is_inline())
+                               set_inline();
+                       
+                       if (!str && is_inline()) {
+                               yyerrorfl(name->file, name->line,
+                                         "\"%s\" is static but not a struct.",
+                                         get_fq_name()->flatten()->c_str());
+                       }
+               }
+       
+               if (array) {
+                       array->final_analysis();
+                       def.basictype.array = array->ca;
+               } else {
+                       def.basictype.array.bounds[0] = 0;
+                       def.basictype.array.bounds[1] = 0;
+               }
+       }
+
+       void set_inline()
+       {
+               def.flags.field.Inline = 1;
+       }
+
+       bool is_inline()
+       {
+               return def.flags.field.Inline;
+       }
+       
+       bool is_in()
+       {
+               return def.flags.field.In;
+       }
+
+       bool is_out()
+       {
+               return def.flags.field.Out;
+       }
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static Param *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+       
+       bool is_array()
+       {
+               return ::is_array(def.basictype);
+       }
+};
+
+typedef Ref<Param> ParamRef;
+
+class Method : public NameSpace, public Def {
+       list<ParamRef> entries;
+
+       void add_elem(Param *p);
+
+public:
+       CompiledMethod def;
+       
+       Method(const String *name) :
+       Symbol(name), 
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::Method)
+       {
+               memset(&def, 0, sizeof(def));
+       }
+
+       void set_async()
+       {
+               def.flags.field.Async = 1;
+       }
+       
+       bool is_async()
+       {
+               return def.flags.field.Async;
+       }
+       
+       static Method *declare(const String *name, NameSpace *parent);
+
+       void add(Symbol *sym, bool from_import);
+
+       virtual const char *description()
+       {
+               return "method";
+       }
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static Method *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+
+       typedef list<ParamRef>::const_iterator entries_iterator;
+
+       entries_iterator entries_begin()
+       {
+               return entries.begin();
+       }
+
+       entries_iterator entries_end()
+       {
+               return entries.end();
+       }
+};
+
+typedef Ref<Method> MethodRef;
+
+class Interface;
+typedef Ref<Interface> InterfaceRef;
+
+extern Interface *System_Object;
+
+// FIXME: typedefed superinterfaces
+class Interface : public NameSpace, public Type, public Def {
+       list<MethodRef> methods;
+       list<InterfaceRef> supers;
+       IDListRef supernames;
+
+       void add_elem(Method *m);
+
+       // This is like Symbol::traversed[], but used internally by the
+       // for_each_super function to ensure that common ancestors are only
+       // visited once.
+       
+       int traversed_all_supers;
+       static int all_supers_traversal;
+       
+public:
+       typedef void (*callback)(Interface *i, void *arg);
+       
+private:
+       template<callback cb>
+       void for_each_super_internal(void *arg)
+       {
+               for (supers_iterator i = supers_begin(); i != supers_end(); ++i) {
+                       Interface *iface = *i;
+                       
+                       if (iface->traversed_all_supers < all_supers_traversal) {
+                               iface->traversed_all_supers = all_supers_traversal;
+                               cb(iface, arg);
+                               iface->for_each_super_internal<cb>(arg);
+                       }
+               }
+       }
+
+       // All interfaces in the map and vector are supers of this
+       // interface, and thus retained that way, so plain pointers
+       // can be used here.
+
+       typedef map<Interface *, int> chain_map_type;
+       typedef chain_map_type::value_type chain_valtype;
+       typedef chain_map_type::const_iterator chain_iter;
+
+       chain_map_type super_to_chain_map;
+
+private:
+       int num_chains;
+       vector<Interface *> chain_heads;
+
+public:        
+       int super_to_chain(Interface *iface, bool must_find_it = true)
+       {
+               chain_iter ret = super_to_chain_map.find(iface);
+               
+               if (ret == super_to_chain_map.end()) {
+                       assert(!must_find_it);
+                       return -1;
+               }
+
+               return (*ret).second;
+       }
+
+       Interface *get_chain_head(int chain)
+       {
+               return chain_heads[chain];
+       }
+
+       int get_num_chains()
+       {
+               return num_chains;
+       }
+       
+private:       
+       void set_chain(Interface *iface, int chain)
+       {
+               pair<chain_iter, bool> ret = 
+                       super_to_chain_map.insert(chain_valtype(iface, chain));
+               assert(ret.second);
+       }
+       
+       // This is the inner depth-first search, which terminates
+       // at each level upon finding that the node it had previously
+       // recursed into found an unchained node.
+       
+       void pick_chain(Interface *iface, int chain)
+       {
+               assert(super_to_chain(iface, false) == -1);
+               chain_heads.push_back(iface);
+       
+               do {
+                       set_chain(iface, chain);
+                       
+                       if (iface->supers.empty())
+                               break;
+                       
+                       iface = iface->supers.front();
+               } while (super_to_chain(iface, false) == -1);
+       }
+
+       // This is the outer breadth-first-search, making sure every
+       // super is assigned to a chain.
+
+       void sort_chains()
+       {
+               list<Interface *> bfs;
+               num_chains = 0;
+
+               bfs.push_back(this);
+               
+               while (!bfs.empty()) {
+                       Interface *iface = bfs.front();
+                       bfs.pop_front();
+               
+                       for (supers_iterator i = iface->supers_begin();
+                            i != iface->supers_end(); ++i)
+                               bfs.push_back(*i);
+               
+                       if (super_to_chain(iface, false) == -1)
+                               pick_chain(iface, num_chains++);
+               }
+       }
+
+public:
+       // Do not call after lookup_misc
+       void add_super(Interface *i)
+       {
+               assert(current_pass != 1);
+       
+               supers.push_back(i);
+               def.num_supers++;
+       }
+
+       CompiledInterface def;
+       
+       Interface(const String *name) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::Interface)
+       {
+               memset(&def, 0, sizeof(def));
+               traversed_all_supers = 0;
+       }
+       
+       void init(IDList *SUPERNAMES)
+       {
+               supernames = SUPERNAMES;
+       }
+
+       static Interface *declare(const String *name, NameSpace *parent, 
+                                 IDList *SUPERNAMES)
+       {
+               assert(parent);
+
+               Interface *i = new Interface(name);
+               i->init(SUPERNAMES);
+               
+               parent->add_user(i);
+               return i;
+       }
+       
+       // Only Methods, Types, and const BasicType Datums can be added.
+
+       void add(Symbol *sym, bool from_import);
+
+       virtual const char *description()
+       {
+               return "interface";
+       }
+
+private:
+       void add_object_super()
+       {
+               assert(System_Object);
+               if (this != System_Object && supers.empty())
+                       add_super(System_Object);
+       }
+
+public:        
+       virtual void lookup_misc()
+       {
+               if (def.guid[0] == 0 && def.guid[1] == 0)
+                       yyerrorfl(name->file, name->line,
+                                 "Interface \"%s\" is missing a GUID.",
+                                 get_fq_name()->flatten()->c_str());
+       
+               if (supernames) {
+                       for (IDList::iterator i = supernames->begin(); 
+                            i != supernames->end(); ++i)
+                       {
+                               Symbol *sym = lookup_sym(toplevel, *i, get_ns());
+                               Interface *iface = 
+                                       dynamic_cast<Interface *>(sym->get_concrete_sym());
+                               
+                               if (!iface) {
+                                       const String *str = (*i)->back();
+                                       yyerrorfl(str->file, str->line,
+                                                 "\"%s\" is not an interface.\n",
+                                                 sym->get_fq_name()->flatten()->c_str());
+       
+                                       throw UserError();
+                               }
+                               
+                               add_super(iface);
+                       }
+               }
+               
+               add_object_super();
+               NameSpace::lookup_misc();
+       }
+       
+       virtual void final_analysis()
+       {
+               // FIXME: check for infinite loops in inheritance
+       
+               sort_chains();
+               NameSpace::final_analysis();
+       }
+       
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static Interface *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+
+       typedef list<MethodRef>::const_iterator methods_iterator;
+       typedef list<InterfaceRef>::const_iterator supers_iterator;
+
+       supers_iterator supers_begin()
+       {
+               assert(current_pass != 1);
+               return supers.begin();
+       }
+
+       supers_iterator supers_end()
+       {
+               return supers.end();
+       }
+       
+       bool supers_empty()
+       {
+               assert(current_pass != 1);
+               return supers.empty();
+       }
+
+       methods_iterator methods_begin()
+       {
+               return methods.begin();
+       }
+
+       methods_iterator methods_end()
+       {
+               return methods.end();
+       }
+       
+       template<callback cb>
+       void for_each_super(void *arg)
+       {
+               assert(current_pass >= 4);
+       
+               all_supers_traversal++;
+               for_each_super_internal<cb>(arg);
+       }
+
+       void finalize_class_iface()
+       {
+               add_object_super();
+               sort_chains();
+       }
+       
+       void set_guid(uint64_t guid[2])
+       {
+               if (def.guid[0] || def.guid[1])
+                       yyerrorf("\"%s\" already has a GUID.",
+                                get_fq_name()->flatten()->c_str());
+       
+               def.guid[0] = guid[0];
+               def.guid[1] = guid[1];
+       }
+};
+
+class IFaceList : public list<InterfaceRef>,
+                  public RefCountable<IFaceList> {};
+
+class Enum : public NameSpace, public Type, public Def {
+       list<DatumRef> entries;
+
+       void add_elem(Datum *d);
+
+public:
+       unsigned int next_val;
+       CompiledEnum def;
+
+       Enum(const String *name) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::Enum),
+       next_val(0)
+       {
+               memset(&def, 0, sizeof(def));
+       }
+
+       void init(int bits)
+       {
+               if (bits < 0 || bits > 64) {
+                       yyerrorf("\"%s\" has invalid enum size %d", 
+                                name->c_str(), bits);
+
+                       bits = bits < 0 ? 0 : 64;
+               }
+       
+               def.bits = bits;
+       }
+
+       static Enum *declare(const String *name, NameSpace *parent,
+                            int bits)
+       {
+               assert(parent);
+               
+               Enum *e = new Enum(name);
+               e->init(bits);
+               
+               parent->add_user(e);
+               return e;
+       }
+
+       // Only const unsigned integer BasicType Datums are allowed.
+
+       void add(Symbol *sym, bool from_import);
+
+       virtual const char *description()
+       {
+               return "enumeration";
+       }
+
+       int get_default_bf_size()
+       {
+               return def.bits;
+       }
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static Enum *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+
+       typedef list<DatumRef>::const_iterator entries_iterator;
+
+       entries_iterator entries_begin()
+       {
+               return entries.begin();
+       }
+
+       entries_iterator entries_end()
+       {
+               return entries.end();
+       }
+};
+
+class Alias : public Symbol, public Def {
+       bool lookup_begun;
+       
+       struct Cycle {
+               Alias *end;
+               
+               Cycle(Alias *END) : end(END)
+               {
+               }
+       };
+
+public:
+       CompiledAlias def;
+       
+       SymbolRef real_sym;
+       StringRef sym_fq_name;
+       StrListRef sym_name;
+       
+       Alias(const String *name) :
+       Symbol(name),
+       Def((const char *)&def, sizeof(def), CompiledDefHeader::Alias)
+       {
+               memset(&def, 0, sizeof(def));
+               lookup_begun = false;
+       }
+       
+       void init(StrList *symname, bool is_private)
+       {
+               sym_name = symname;
+               priv = is_private;
+       }
+
+       static Alias *declare(const String *name, NameSpace *parent,
+                             StrList *symname, bool is_private = false)
+       {
+               assert(parent);
+               Alias *a = new Alias(name);
+               a->init(symname, is_private);
+
+               parent->add_user(a);
+               return a;
+       }
+       
+       void resolve_chain()
+       {       
+               if (!real_sym) {
+                       if (lookup_begun) {
+                               yyerrorfl(name->file, name->line,
+                                         "Alias loop defining \"%s\"",
+                                         get_fq_name()->flatten()->c_str());
+                       
+                               throw Cycle(this);
+                       }
+                       
+                       lookup_begun = true;
+                       
+                       try {
+                               real_sym = lookup_sym(toplevel, sym_name, get_ns(), this);
+                       }
+                       
+                       catch (Cycle &c) {
+                               yyerrorfl(name->file, name->line, "   ...referenced by \"%s\"",
+                                         get_fq_name()->flatten()->c_str());
+
+                               if (c.end == this)
+                                       throw UserError();
+
+                               throw c;
+                       }
+               }
+       }
+
+       virtual Symbol *get_concrete_sym(bool follow_typedefs = true)
+       {
+               resolve_chain();
+               return real_sym->get_concrete_sym(follow_typedefs);
+       }
+
+       virtual void lookup_chain()
+       {
+               get_concrete_sym(true);
+       }
+
+       virtual void lookup_misc()
+       {
+               real_sym = real_sym->get_concrete_sym(false);
+               sym_fq_name = real_sym->get_fq_name()->flatten();
+               
+               def.length = sym_fq_name->length();
+       }
+
+       virtual void output(const char *root);
+       bool output_extra(FILE *f);
+
+       static Alias *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+};
+
+class TypeDef : public Alias {
+public:
+       TypeDef(const String *name) : Alias(name)
+       {
+               memset(&def, 0, sizeof(def));
+               hdr.type = CompiledDefHeader::TypeDef;
+       }
+       
+       static TypeDef *declare(const String *name, NameSpace *parent,
+                               StrList *symname)
+       {
+               assert(parent);
+               TypeDef *td = new TypeDef(name);
+               td->init(symname, false);
+               
+               parent->add_user(td);
+               return td;
+       }
+
+       virtual Symbol *get_concrete_sym(bool follow_typedefs = true)
+       {
+               if (follow_typedefs) {
+                       resolve_chain();
+                       return real_sym->get_concrete_sym(follow_typedefs);
+               }
+               
+               return this;
+       }
+
+       static TypeDef *import(ImportContext &ctx);
+       virtual void output_lang(LangCallback *lcb, int arg = 0, void *arg2 = NULL);
+};
+
+NameSpace *add_nspace(StrList *name, bool push);
+void pop_nspace();
+
+// Declare an instance of "type" in "ns" for each element of "ids".
+// This function will report any errors, but not throw UserError.
+
+void declare_data(NameSpace *ns, StrList *ids, StrList *type,
+                  Array *array, StrList *attr);
+void declare_aliases(NameSpace *ns, StrList *ids, StrList *type,
+                     bool is_typedef);
+void declare_basictypes(NameSpace *ns, StrList *ids,
+                        BasicType *type, bool is_typedef);
+
+// You'd think they'd have standard functions to do this these days.
+// All I could find that come close are the network-byte-order
+// functions, and they're no-ops on big-endian machines.
+
+static inline uint32_t swap32(uint32_t in, bool swap)
+{
+       if (swap)
+               return ((in & 0x000000ff) << 24) |
+                      ((in & 0x0000ff00) <<  8) |
+                      ((in & 0x00ff0000) >>  8) |
+                      ((in & 0xff000000) >> 24);
+       
+       return in;
+}
+
+static inline uint64_t swap64(uint64_t in, bool swap)
+{
+       if (swap)
+               return (((uint64_t)swap32((uint32_t)in, true)) << 32) |
+                      swap32((uint32_t)(in >> 32), true);
+
+       return in;
+}
+
+struct File {
+       FILE *f;
+       
+       File()
+       {
+               f = NULL;
+       }
+       
+       File(FILE *F)
+       {
+               f = F;
+       }
+       
+       File *operator =(FILE *F)
+       {
+               f = F;
+               return this;
+       }
+       
+       ~File()
+       {
+               if (f)
+                       fclose(f);
+       }
+       
+       operator FILE *()
+       {
+               return f;
+       }
+};
+
+// Verify that a prospective new import (or output namespace)
+// does not overlap an existing import.  Returns the conflicting
+// import, or NULL if none found.
+
+NameSpace *check_for_imports(NameSpace *ns);
+
+#endif
diff --git a/idlcomp/idlparse.y b/idlcomp/idlparse.y
new file mode 100644 (file)
index 0000000..75c6a89
--- /dev/null
@@ -0,0 +1,1034 @@
+%{
+/* idlparse.y -- parser for the IDL compiler
+ *
+ * Written by Scott Wood <scott@buserror.net>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+
+#include <idlc.h>
+#include <util.h>
+#define YYDEBUG 1
+
+#if 1
+#define do_yyerror() do { \
+       fprintf(stderr, "YYERROR at %d\n", __LINE__); \
+       throw UserError(); \
+} while (0)
+#else
+#define do_yyerror() YYERROR
+#endif
+
+static StrListRef nspace_name;
+static StrListRef cur_strlist;
+static ConListRef cur_conlist;
+static IDListRef cur_idlist;
+
+%}
+%union {
+       // The lifetime of any of these pointers is one instance
+       // of the one_input rule.
+       
+       String *string;
+       StrList *strl;
+       IDList *idl;
+
+       struct Decl {           // Used for namespace-qualified type declarations
+               NameSpace *ns;       // Namespace portion -- all but last field
+               const String *ident; // New identifier portion -- last field
+       } decl;
+       
+       SymList *syml;
+       Symbol *sym;
+       Enum *en;
+       Struct *str;
+       BitField *bf;
+       Interface *iface;
+       Method *meth;
+       Datum *datum;
+       bool boolean;
+       IFaceList *ifl;
+
+       Con con;
+       ConList *conl;
+       Array *array;
+
+       struct {
+               StrList *type;
+               Array *array;
+       } type;
+       
+       int val;
+
+       struct {
+               Decl decl;
+               StrList *attr;
+       } struct_decl_attr;
+
+       struct {
+               StrList *ids;
+               StrList *attr;
+       } ids_attr;
+
+       uint64_t guid[2];
+}
+
+// The token list must be exactly the same as in idlparse.y, so that
+// the same lexer can be used.
+
+%token <string> TOK_IDENT
+%token TOK_IFACE
+%token TOK_STRUCT
+%token TOK_CHAR
+%token TOK_OCTET
+%token <con> TOK_ICON
+%token <con> TOK_FCON
+%token <con> TOK_UCON
+%token <con> TOK_INVALID
+%token TOK_BOOL
+%token TOK_SHORT
+%token TOK_INT
+%token TOK_LONG
+%token TOK_USHORT
+%token TOK_UINT
+%token TOK_ULONG
+%token TOK_FSHORT
+%token TOK_FLONG
+%token TOK_CONST
+%token TOK_BITFIELD
+%token TOK_ENUM
+%token TOK_NAMESPACE
+%token TOK_USING
+%token TOK_ASYNC
+%token TOK_INOUT
+%token TOK_OUT
+%token TOK_3DOT
+%token TOK_2DOT
+%token <string> TOK_STR
+%token TOK_SHARED
+%token TOK_PUSH
+%token TOK_TYPEDEF
+%token TOK_ALIAS
+%token TOK_VIRTUAL
+%token TOK_GUID
+%token TOK_INLINE
+%token TOK_STATIC
+%token TOK_IMMUTABLE
+%token TOK_TRUE
+%token TOK_FALSE
+
+// CDL tokens
+%token TOK_COPY
+%token TOK_METHOD
+%token TOK_CLASS
+%token TOK_NAME
+
+// These are not real tokens, but are used as special values in places that
+// normally accept tokens.
+%token TOK_NONE
+%token TOK_ANON
+%token TOK_DCON
+
+%type <strl>       basictype
+%type <type>       type
+%type <con>        const
+%type <con>        maybeconst
+%type <array>      arraybounds
+%type <con>        constnominus
+%type <datum>      bfelem
+%type <syml>       bfelems
+%type <syml>       maybe_bfelems
+%type <strl>       maybe_ids
+%type <strl>       ids
+%type <conl>       ideqs
+%type <string>     ident
+%type <strl>       strlist
+%type <strl>       maybe_strlist
+%type <strl>       end_strlist
+%type <con>        size
+%type <datum>      bftype
+%type <strl>       qualified_ident
+%type <strl>       qualified_ident_nodbldot
+%type <decl>       qualified_decl
+%type <decl>       maybe_qualified_decl
+%type <decl>       anon_decl
+%type <idl>        qualified_idlist
+%type <boolean>    maybe_dot_star
+%type <strl>       inherit_struct
+%type <str>        struct
+%type <bf>         bitfield
+%type <en>         enum
+%type <iface>      iface
+%type <idl>        inherit_ifaces
+%type <boolean>    typedef_or_alias_keyword
+%type <guid>       guid
+%type <struct_decl_attr> struct_decl_and_attr
+%type <ids_attr>   ids_attr
+
+%%
+
+input:
+       /* empty */
+|      input one_input
+;
+
+one_input_real:
+       using ';'
+|      namespace
+|      enum ';' {}
+|      bitfield ';' {}
+|      struct ';' {}
+|      iface ';' {}
+|      typedef_or_alias ';' {}
+|      const_datum ';' {}
+;
+
+one_input:
+       one_input_real
+;
+
+namespace_body:
+       ';' {
+               NameSpace *ret = add_nspace(nspace_name, false);
+               nspace_name = NULL;
+
+               if (!ret)
+                       do_yyerror();
+       }
+|      '{' {
+               NameSpace *ret = add_nspace(nspace_name, true);
+               nspace_name = NULL;
+
+               if (!ret)
+                       do_yyerror();
+       } input '}' {
+               pop_nspace();
+       }
+|      {
+               NameSpace *ret = add_nspace(nspace_name, true);
+               nspace_name = NULL;
+
+               if (!ret)
+                       do_yyerror();
+       } one_input {
+               pop_nspace();
+       } 
+;
+
+namespace:
+       TOK_NAMESPACE qualified_ident {
+               nspace_name = $2;
+       } namespace_body
+;
+
+const_datum:
+       TOK_CONST type ideqs {
+               ConList *cl = $3;
+               
+               for (ConList::iterator i = cl->begin(); i != cl->end(); ++i)
+                       Datum::declare((*i).str, cur_nspace, $2.type, $2.array, &(*i).con);
+       }
+;
+
+ids_attr:
+       /* empty */ {
+               $$.ids = NULL;
+               $$.attr = NULL;
+       }
+|      ids maybe_strlist {
+               $$.ids = $1;
+               $$.attr = $2;
+       }
+;
+
+struct_elem:
+       const_datum ';' 
+|      type ids maybe_strlist ';' {
+               declare_data(cur_nspace, $2, $1.type, $1.array, $3);
+       }
+|      enum maybe_ids ';' {
+               if (!$2 && $1->name->token == TOK_ANON)
+                       yyerrorfl($1->name->file, $1->name->line,
+                                 "An anonymous type must declare a datum.");
+
+               if ($2) {
+                       StrList *strl = $1->get_fq_name();
+                       strl->push_front(new String(""));
+                       declare_data(cur_nspace, $2, strl, NULL, NULL);
+               }
+       }
+|      bitfield maybe_ids ';' {
+               if (!$2 && $1->name->token == TOK_ANON)
+                       yyerrorfl($1->name->file, $1->name->line,
+                                 "An anonymous type must declare a datum.");
+
+               if ($2) {
+                       StrList *strl = $1->get_fq_name();
+                       strl->push_front(new String(""));
+                       declare_data(cur_nspace, $2, strl, NULL, NULL);
+               }
+       }
+|      struct ids_attr ';' {
+               if (!$2.ids && $1->name->token == TOK_ANON)
+                       yyerrorfl($1->name->file, $1->name->line,
+                                 "An anonymous type must declare a datum.");
+
+               if ($2.ids) {
+                       StrList *strl = $1->get_fq_name();
+                       strl->push_front(new String(""));
+                       declare_data(cur_nspace, $2.ids, strl, NULL, $2.attr);
+               }
+       }
+|      typedef_or_alias ';'
+|      using ';'
+|      guid ';' {
+               Struct *str = dynamic_cast<Struct *>(*cur_nspace);
+               assert(str);
+               
+               str->set_guid($1);
+       }
+;
+
+struct_body:
+       /* empty */
+|      struct_elem struct_body
+;
+
+inherit_struct:
+       /* empty */ {
+               $$ = NULL;
+       }
+|      ':' qualified_ident {
+               $$ = $2;
+       }
+;
+
+struct_decl_and_attr: 
+       anon_decl {
+               // Anonymous structs have no attributes.
+               $$.decl = $1;
+               $$.attr = new StrList;
+       }
+|      qualified_decl maybe_strlist {
+               $$.decl = $1;
+               $$.attr = $2;
+       }
+;
+
+struct:
+       TOK_STRUCT struct_decl_and_attr inherit_struct '{' {
+               $<str>$ = Struct::declare($2.decl.ident, $2.decl.ns, $3);
+               
+               for (StrList::iterator i = $2.attr->begin(); i != $2.attr->end(); ++i) {
+                       const String *attr = *i;
+
+                       switch (attr->token) {
+                               case TOK_VIRTUAL:
+                                       $<str>$->set_virtual();
+                                       break;
+                               
+                               case TOK_INLINE:
+                                       $<str>$->set_inline();
+                                       break;
+                               
+                               default:
+                                       yyerrorfl(attr->file, attr->line, "Invalid attribute \"%s\"", 
+                                                 (*i)->c_str());
+                       }
+               }
+
+               nspace_stack.push_front(cur_nspace);
+               cur_nspace = $<str>$;
+       }
+
+       struct_body '}' {
+               // One for the struct's namespace, and one for the
+               // namespace it was declared in.
+               pop_nspace();
+               pop_nspace();
+               $$ = $<str>5;
+       }
+;
+
+param:
+       type strlist {
+               const String *name = $2->front();
+               $2->pop_front();
+               
+               CompiledParam::Flags flags = {};
+               int dirs = 0;
+               
+               flags.field.In = 1; 
+               
+               for (StrList::iterator i = $2->begin(); i != $2->end(); ++i) {
+                       const String *attr = *i;
+                       
+                       switch (attr->token) {
+                               case TOK_OUT:
+                                       if (dirs++ > 0) {
+                                               yyerrorf("Only one direction attribute may be given.");
+                                       } else {
+                                               flags.field.In = 0;
+                                               flags.field.Out = 1;
+                                       }
+                                       
+                                       break;
+                               
+                               case TOK_INOUT:
+                                       if (dirs++ > 0) {
+                                               yyerrorf("Only one direction attribute may be given.");
+                                       } else {
+                                               flags.field.In = 1;
+                                               flags.field.Out = 1;
+                                       }
+                                       
+                                       break;
+                               
+                               case TOK_SHARED:
+                                       flags.field.Shared = 1;
+                                       break;
+                               
+                               case TOK_PUSH:
+                                       flags.field.Push = 1;
+                                       break;
+                               
+                               case TOK_INLINE:
+                                       flags.field.Inline = 1;
+                                       break;
+                               
+                               case TOK_IMMUTABLE:
+                                       flags.field.Immutable = 1;
+                                       break;
+                                       
+                               default:
+                                       yyerrorfl(attr->file, attr->line, 
+                                                 "Invalid attribute \"%s\"", (*i)->c_str());
+                       }
+               }
+
+               Param::declare(name, cur_nspace, $1.type, flags, $1.array);
+       }                       
+;
+
+more_params:
+       /* empty */
+|      ',' param_list
+;
+
+param_list:
+       /* empty */
+|      param more_params
+;
+
+method:
+       ident '(' {
+               $<meth>$ = Method::declare($1, cur_nspace);
+               
+               nspace_stack.push_front(cur_nspace);
+               cur_nspace = $<meth>$;
+       } param_list ')' maybe_strlist {
+               for (StrList::iterator i = $6->begin(); i != $6->end(); ++i) {
+                       const String *attr = *i;
+
+                       switch (attr->token) {
+                               case TOK_ASYNC:
+                                       $<meth>3->set_async();
+                                       break;
+                               
+                               default:
+                                       yyerrorfl(attr->file, attr->line,
+                                                 "Invalid attribute \"%s\".", (*i)->c_str());
+                       }
+               }
+               pop_nspace();
+       }
+;
+
+iface_body:
+       /* empty */
+|      method ';' iface_body
+|      enum ';' iface_body {}
+|      bitfield ';' iface_body {}
+|      struct ';' iface_body {}
+|      iface ';' iface_body {}
+|      typedef_or_alias ';' iface_body
+|      const_datum ';' iface_body
+|      guid ';' iface_body {
+               Interface *iface = dynamic_cast<Interface *>(*cur_nspace);
+               assert(iface);
+               
+               iface->set_guid($1);
+       }
+|      using ';' iface_body
+;
+
+guid:
+       TOK_GUID ':' TOK_STR {
+               parse_guid($3->c_str(), (char *)$$);
+       }
+;
+       
+
+inherit_ifaces:
+       /* empty */ {
+               $$ = new IDList;
+       }
+|      ':' qualified_idlist {
+               $$ = $2;
+       }
+;
+
+iface:
+       TOK_IFACE qualified_decl inherit_ifaces {
+               $<iface>$ = Interface::declare($2.ident, $2.ns, $3);
+
+               nspace_stack.push_front(cur_nspace);
+               cur_nspace = $<iface>$;
+       } '{' iface_body '}' {
+               pop_nspace();
+               pop_nspace();
+               $$ = $<iface>3;
+       }
+;
+
+maybe_namespace:
+               /* empty */
+       |       TOK_NAMESPACE
+;
+
+using_list_entry:
+       maybe_namespace qualified_ident {
+               const String *end = $2->back();
+               bool ns = false;
+               
+               if (end->token == '*') {
+                       ns = true;
+                       $2->pop_back();
+               }
+               
+               if (ns) {
+                       cur_nspace->add_search($2);
+               } else {
+                       Alias::declare(end, cur_nspace, $2, true);
+               }
+       }
+;
+
+using_list:
+               using_list_entry
+       |       using_list_entry ',' using_list
+;
+
+using: TOK_USING using_list
+;
+
+ids_body:
+       ident {
+               cur_strlist->push_back($1);
+       }
+|      ids_body ',' ident {
+               cur_strlist->push_back($3);
+       }
+;
+
+ids:
+       ids_body {
+               $$ = cur_strlist;
+               cur_strlist = new StrList;
+       }
+;
+
+maybe_ids:
+       /* empty */ {
+               $$ = NULL;
+       }
+|      ids {
+               $$ = $1;
+       }
+;
+
+coninit:
+       ident '=' const {
+               cur_conlist->push_back(ConInit($1, $3));
+       }
+;
+
+/* ideqs is ids with a constant initializer for each element. */
+
+ideqs_body:
+       coninit
+|      ids_body ',' coninit
+;
+
+ideqs:
+       ideqs_body {
+               $$ = cur_conlist;
+               cur_conlist = new ConList;
+       }
+;
+
+ident:
+       TOK_IDENT
+|      TOK_ASYNC    {
+               $$ = new String("async", cur_input_file, curline, TOK_ASYNC);
+       }
+|      TOK_INOUT    {
+               $$ = new String("inout", cur_input_file, curline, TOK_INOUT);
+       }
+|      TOK_OUT      {
+               $$ = new String("out", cur_input_file, curline, TOK_OUT);
+       }
+|      TOK_SHARED   {
+               $$ = new String("shared", cur_input_file, curline, TOK_SHARED);
+       }
+|      TOK_PUSH     {
+               $$ = new String("push", cur_input_file, curline, TOK_PUSH);
+       }
+|      TOK_SHORT    {
+               $$ = new String("short", cur_input_file, curline, TOK_SHORT);
+       }
+|      TOK_INT      {
+               $$ = new String("int", cur_input_file, curline, TOK_INT);
+       }
+|      TOK_LONG     {
+               $$ = new String("long", cur_input_file, curline, TOK_LONG);
+       }
+|      TOK_USHORT   {
+               $$ = new String("ushort", cur_input_file, curline, TOK_USHORT);
+       }
+|      TOK_UINT     {
+               $$ = new String("uint", cur_input_file, curline, TOK_UINT);
+       }
+|      TOK_ULONG    {
+               $$ = new String("ulong", cur_input_file, curline, TOK_ULONG);
+       }
+|      TOK_CHAR     {
+               $$ = new String("char", cur_input_file, curline, TOK_CHAR);
+       }
+|      TOK_OCTET    {
+               $$ = new String("octet", cur_input_file, curline, TOK_OCTET);
+       }
+|      TOK_FSHORT   {
+               $$ = new String("fshort", cur_input_file, curline, TOK_FSHORT);
+       }
+|      TOK_FLONG    {
+               $$ = new String("flong", cur_input_file, curline, TOK_FLONG);
+       }
+|      TOK_BOOL     {
+               $$ = new String("bool", cur_input_file, curline, TOK_BOOL);
+       }
+|      TOK_METHOD   {
+               $$ = new String("method", cur_input_file, curline, TOK_METHOD);
+       }
+|      TOK_NAME     {
+               $$ = new String("name", cur_input_file, curline, TOK_NAME);
+       }
+|      TOK_COPY     {
+               $$ = new String("copy", cur_input_file, curline, TOK_COPY);
+       }
+|      TOK_CLASS    {
+               $$ = new String("class", cur_input_file, curline, TOK_CLASS);
+       }
+|      TOK_GUID     {
+               $$ = new String("guid", cur_input_file, curline, TOK_GUID);
+    &nb