diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1998-03-03 21:37:38 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1998-03-03 21:37:38 +0000 |
commit | c826e751760901de543f4640467e3a08df7f49bf (patch) | |
tree | 6a2323a78664ba45b78df1709ff3cc077db63ccd /gnu/usr.bin/gcc/objc | |
parent | a4511fe6a5dc69a771b5ffdf64ba574c959cf37d (diff) |
GCC 2.8.0 merge
Diffstat (limited to 'gnu/usr.bin/gcc/objc')
46 files changed, 14486 insertions, 483 deletions
diff --git a/gnu/usr.bin/gcc/objc/Make-lang.in b/gnu/usr.bin/gcc/objc/Make-lang.in new file mode 100644 index 00000000000..97119a78cf8 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/Make-lang.in @@ -0,0 +1,305 @@ +# Top level makefile fragment for GNU Objective-C +# Copyright (C) 1997 Free Software Foundation, Inc. + +#This file is part of GNU CC. + +#GNU CC is free software; you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation; either version 2, or (at your option) +#any later version. + +#GNU CC is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. + +#You should have received a copy of the GNU General Public License +#along with GNU CC; see the file COPYING. If not, write to +#the Free Software Foundation, 59 Temple Place - Suite 330, +#Boston, MA 02111-1307, USA. + +# This file provides the language dependent support in the main Makefile. +# Each language makefile fragment must provide the following targets: +# +# foo.all.build, foo.all.cross, foo.start.encap, foo.rest.encap, +# foo.info, foo.dvi, +# foo.install-normal, foo.install-common, foo.install-info, foo.install-man, +# foo.uninstall, foo.distdir, +# foo.mostlyclean, foo.clean, foo.distclean, foo.extraclean, +# foo.maintainer-clean, foo.stage1, foo.stage2, foo.stage3, foo.stage4 +# +# where `foo' is the name of the language. +# +# It should also provide rules for: +# +# - making any compiler driver (eg: g++) +# - the compiler proper (eg: cc1plus) +# - define the names for selecting the language in LANGUAGES. +# +# Extra flags to pass to recursive makes. +OBJC_FLAGS_TO_PASS = \ + "OBJC_FOR_BUILD=$(OBJC_FOR_BUILD)" \ + "OBJCFLAGS=$(OBJCFLAGS)" \ + "OBJC_FOR_TARGET=$(OBJC_FOR_TARGET)" \ + +# Actual names to use when installing a native compiler. +#OBJC_INSTALL_NAME = `t='$(program_transform_name)'; echo c++ | sed $$t` + +# Actual names to use when installing a cross-compiler. +#OBJC_CROSS_NAME = `t='$(program_transform_cross_name)'; echo c++ | sed $$t` + +# +# Define the names for selecting Objective-C in LANGUAGES. +OBJC objc: cc1obj objc-runtime +OBJECTIVE-C objective-c: cc1obj objc-runtime + +# Tell GNU make to ignore these if they exist. +.PHONY: objective-c objc ObjC + +# The Objective C thread file +OBJC_THREAD_FILE=thr-$(GCC_THREAD_FILE) + +# Language-specific object files for Objective C. +OBJC_OBJS = objc-parse.o objc-act.o $(C_AND_OBJC_OBJS) + +cc1obj: $(P) $(OBJC_OBJS) $(OBJS) $(BC_OBJS) $(LIBDEPS) + $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ $(OBJC_OBJS) $(OBJS) \ + $(BC_OBJS) $(LIBS) + +# Objective C language specific files. + +objc-parse.o : objc-parse.c $(CONFIG_H) $(TREE_H) c-lex.h c-tree.h input.h \ + flags.h output.h $(srcdir)/objc/objc-act.h + $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -I$(srcdir)/objc \ + -c objc-parse.c + +objc-parse.c : objc-parse.y + $(BISON) $(BISONFLAGS) -o objc-parse.c objc-parse.y + +objc-parse.y: $(srcdir)/c-parse.in + echo '/*WARNING: This file is automatically generated!*/' >tmp-objc-prs.y + sed -e "/^ifc$$/,/^end ifc$$/d" \ + -e "/^ifobjc$$/d" -e "/^end ifobjc$$/d" \ + $(srcdir)/c-parse.in >>tmp-objc-prs.y + sh $(srcdir)/move-if-change tmp-objc-prs.y objc-parse.y + +objc-act.o : $(srcdir)/objc/objc-act.c \ + $(CONFIG_H) $(TREE_H) $(RTL_H) \ + $(srcdir)/c-tree.h $(srcdir)/c-lex.h \ + $(srcdir)/flags.h $(srcdir)/objc/objc-act.h $(srcdir)/input.h \ + $(srcdir)/function.h $(srcdir)/output.h c-parse.h + $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -I$(srcdir)/objc \ + -c $(srcdir)/objc/objc-act.c + +objc-runtime: objc-headers libobjc.a + +# copy objc header files into build directory +objc-headers: stmp-fixinc + if [ -d include ]; then true; else mkdir include; fi + cd objc; \ + if [ -f Makefile ]; then \ + $(MAKE) copy-headers \ + tooldir=$(tooldir) \ + AR="$(AR)" AR_FLAGS="$(AR_FLAGS)" \ + GCC_FOR_TARGET="../xgcc -B../" \ + GCC_CFLAGS="$(GCC_CFLAGS)" incinstalldir=../include; \ + fi + touch objc-headers + +# Objective C runtime library specific files. + +OBJC_O = objc/hash.o objc/sarray.o \ + objc/class.o objc/sendmsg.o \ + objc/init.o objc/archive.o \ + objc/encoding.o objc/selector.o \ + objc/objects.o objc/misc.o \ + objc/NXConstStr.o objc/Object.o \ + objc/Protocol.o objc/nil_method.o \ + objc/thr.o objc/linking.o \ + objc/$(OBJC_THREAD_FILE).o + +objc/hash.o: $(srcdir)/objc/hash.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/hash.c -o $@ +objc/sarray.o: $(srcdir)/objc/sarray.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/sarray.c -o $@ +objc/class.o: $(srcdir)/objc/class.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/class.c -o $@ +objc/sendmsg.o: $(srcdir)/objc/sendmsg.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) -Iobjc \ + -c $(srcdir)/objc/sendmsg.c -o $@ +objc/init.o: $(srcdir)/objc/init.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/init.c -o $@ +objc/archive.o: $(srcdir)/objc/archive.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/archive.c -o $@ +objc/encoding.o: $(srcdir)/objc/encoding.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/encoding.c -o $@ +objc/selector.o: $(srcdir)/objc/selector.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/selector.c -o $@ +objc/objects.o: $(srcdir)/objc/objects.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/objects.c -o $@ +objc/misc.o: $(srcdir)/objc/misc.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/misc.c -o $@ +objc/NXConstStr.o: $(srcdir)/objc/NXConstStr.m $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -fgnu-runtime -c $(srcdir)/objc/NXConstStr.m -o $@ +objc/Object.o: $(srcdir)/objc/Object.m $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -fgnu-runtime -c $(srcdir)/objc/Object.m -o $@ +objc/Protocol.o: $(srcdir)/objc/Protocol.m $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -fgnu-runtime -c $(srcdir)/objc/Protocol.m -o $@ +objc/thr.o: $(srcdir)/objc/thr.h $(srcdir)/objc/thr.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/thr.c -o $@ +objc/$(OBJC_THREAD_FILE).o: $(srcdir)/objc/$(OBJC_THREAD_FILE).c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/$(OBJC_THREAD_FILE).c -o $@ +objc/nil_method.o: $(srcdir)/objc/nil_method.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/nil_method.c -o $@ +objc/linking.o: $(srcdir)/objc/linking.m $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -fgnu-runtime -c $(srcdir)/objc/linking.m -o $@ + +objc/libobjc_entry.o: $(srcdir)/objc/libobjc_entry.c $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) \ + -c $(srcdir)/objc/libobjc_entry.c -o $@ + +# Build the Objective C runtime library. +libobjc.a: cc1obj specs stmp-int-hdrs libgcc2.ready \ + $(USE_COLLECT2) $(EXTRA_PARTS) objc/runtime-info.h $(OBJC_O) + -rm -f libobjc.a + $(AR) $(AR_FLAGS) libobjc.a $(OBJC_O) + -if $(RANLIB_TEST) ; then $(RANLIB) libobjc.a; else true; fi + +libobjc_s.a: libobjc.a + mv libobjc.a libobjc_s.a + +# Create a relocatable DLL +libobjc.dll: libobjc_s.a objc/libobjc_entry.o + $(GCC_FOR_TARGET) -mdll -Wl,--base-file -Wl,libobjc.base \ + -o libobjc.dll libobjc_s.a \ + objc/libobjc_entry.o -lkernel32 + $(DLLTOOL) --dllname libobjc.dll --def $(srcdir)/objc/libobjc.def \ + --base-file libobjc.base --output-exp libobjc.exp + $(GCC_FOR_TARGET) -mdll -Wl,--base-file libobjc.base libobjc.exp \ + -o libobjc.dll libobjc_s.a \ + objc/libobjc_entry.o -lkernel32 + $(DLLTOOL) --dllname libobjc.dll --def $(srcdir)/objc/libobjc.def \ + --base-file libobjc.base --output-exp libobjc.exp + $(GCC_FOR_TARGET) libobjc.exp -mdll \ + -o libobjc.dll libobjc_s.a \ + objc/libobjc_entry.o -lkernel32 + $(DLLTOOL) --dllname libobjc.dll --def $(srcdir)/objc/libobjc.def \ + --output-lib libobjc.a + +# Platform generated information needed by ObjC runtime +objc/runtime-info.h: cc1obj + echo "" > emptyfile + echo "/* This file is automatically generated */" >$@ + ./cc1obj -print-objc-runtime-info emptyfile >>$@ + +# +# Build hooks: + +objc.all.build: +objc.all.cross: +objc.start.encap: +objc.rest.encap: + +objc.info: +objc.dvi: + +# +# Install hooks: +# cc1obj is installed elsewhere as part of $(COMPILERS). + +objc.install-normal: installdirs + -if [ -f libobjc.a ] ; then \ + rm -f $(libsubdir)/libobjc.a; \ + $(INSTALL_DATA) libobjc.a $(libsubdir)/libobjc.a; \ + if $(RANLIB_TEST) ; then \ + (cd $(libsubdir); $(RANLIB) libobjc.a); else true; fi; \ + chmod a-x $(libsubdir)/libobjc.a; \ + else true; fi + -if [ -f libobjc_s.a ] ; then \ + rm -f $(libsubdir)/libobjc_s.a; \ + $(INSTALL_DATA) libobjc_s.a $(libsubdir)/libobjc_s.a; \ + if $(RANLIB_TEST) ; then \ + (cd $(libsubdir); $(RANLIB) libobjc_s.a); else true; fi; \ + chmod a-x $(libsubdir)/libobjc_s.a; \ + else true; fi + -if [ -f libobjc.dll ] ; then \ + rm -f $(bindir)/libobjc.dll; \ + $(INSTALL_DATA) libobjc.dll $(bindir)/libobjc.dll; \ + else true; fi + +objc.install-common: + +objc.install-info: + +objc.install-man: + +objc.uninstall: +# +# Clean hooks: +# A lot of the ancillary files are deleted by the main makefile. +# We just have to delete files specific to us. +objc.mostlyclean: + -rm -f tmp-objc-prs.y + -rm -f objc/*$(objext) objc/xforward objc/fflags + -rm -f objc/runtime-info.h + -rm -f libobjc.a libobjc_s.a libobjc.dll + -rm -f libobjc.base libobjc.exp + -rm -f objc-parse.y + -rm -f objc-parse.c objc-parse.output +objc.clean: objc.mostlyclean + -rm -rf objc-headers +objc.distclean: + -rm -f objc/Makefile objc/Make-host objc/Make-target + -rm -f objc/config.status objc/config.cache + -rm -f objc-parse.output +objc.extraclean: +objc.maintainer-clean: + +# +# Stage hooks: + +objc.stage1: + -mv objc/*$(objext) stage1/objc + -mv cc1obj$(exeext) stage1 + -mv libobjc.a stage1 +objc.stage2: + -mv objc/*$(objext) stage2/objc + -mv cc1obj$(exeext) stage2 + -mv libobjc.a stage2 +objc.stage3: + -mv objc/*$(objext) stage3/objc + -mv cc1obj$(exeext) stage3 + -mv libobjc.a stage3 +objc.stage4: + -mv objc/*$(objext) stage4/objc + -mv cc1obj$(exeext) stage4 + -mv libobjc.a stage4 + +# +# Maintenance hooks: + +# This target creates the files that can be rebuilt, but go in the +# distribution anyway. It then copies the files to the distdir directory. +objc.distdir: + mkdir tmp/objc + cd objc ; $(MAKE) $(FLAGS_TO_PASS) objc-parse.c + cd objc; \ + for file in *[0-9a-zA-Z+]; do \ + ln $$file ../tmp/objc >/dev/null 2>&1 || cp $$file ../tmp/objc; \ + done diff --git a/gnu/usr.bin/gcc/objc/Makefile.in b/gnu/usr.bin/gcc/objc/Makefile.in new file mode 100644 index 00000000000..9d521f54cdc --- /dev/null +++ b/gnu/usr.bin/gcc/objc/Makefile.in @@ -0,0 +1,91 @@ +# GNU Objective C Runtime Makefile +# Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. +# +# This file is part of GNU CC. +# +# GNU CC is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2, or (at your option) any later version. +# +# GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# GNU CC; see the file COPYING. If not, write to the Free Software +# Foundation, 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# This makefile is run by the parent dir's makefile. +# thisdir1=`pwd`; \ +# srcdir1=`cd $(srcdir); pwd`; \ +# cd objc; \ +# $(MAKE) $(MAKEFLAGS) -f $$srcdir1/objc/Makefile libobjc.a \ +# srcdir=$$srcdir1 tooldir=$(tooldir) AR="$(AR)" AR_FLAGS="$(AR_FLAGS)" \ +# GCC_FOR_TARGET="$$thisdir1/xgcc -B$$thisdir1/" \ +# GCC_CFLAGS="$(GCC_CFLAGS)" incinstalldir=$$thisdir1/include +# OBJC_THREAD_FILE="$(OBJC_THREAD_FILE)" +# Two targets are used by ../Makefile: `all' and `mostlyclean'. + +SHELL=/bin/sh + +.SUFFIXES: .m + +OPTIMIZE= -O + +srcdir = . +VPATH = $(srcdir) + +AR = ar +AR_FLAGS = rc + +# Define this as & to perform parallel make on a Sequent. +# Note that this has some bugs, and it seems currently necessary +# to compile all the gen* files first by hand to avoid erroneous results. +P = + +# Definition of `all' is here so that new rules inserted by sed +# do not specify the default target. +all: all.indirect + +# sed inserts variable overrides after the following line. +####target overrides +####host overrides +####cross overrides +####build overrides +# + +OBJC_H = hash.h objc-list.h sarray.h objc.h objc-api.h \ + NXConstStr.h Object.h Protocol.h encoding.h typedstream.h thr.h + +# Now figure out from those variables how to compile and link. +all.indirect: Makefile compiler objc-runtime + +compiler: + cd ..; $(MAKE) cc1obj + +objc-runtime: + cd ..; $(MAKE) libobjc.a + +# copy objc headers to installation include directory +copy-headers: + -rm -fr $(incinstalldir)/objc + -mkdir $(incinstalldir)/objc + for file in $(OBJC_H); do \ + realfile=$(srcdir)/$${file}; \ + cp $${realfile} $(incinstalldir)/objc; \ + chmod a+r $(incinstalldir)/objc/$${file}; \ + done + +Makefile: $(srcdir)/Makefile.in $(srcdir)/../configure + cd ..; $(SHELL) config.status + +mostlyclean: + -rm -f *.o libobjc.a xforward fflags +clean: mostlyclean +distclean: mostlyclean +extraclean: mostlyclean + +# For Sun VPATH. + diff --git a/gnu/usr.bin/gcc/objc/NXConstStr.h b/gnu/usr.bin/gcc/objc/NXConstStr.h index 6899c74070b..b2c013e5c38 100644 --- a/gnu/usr.bin/gcc/objc/NXConstStr.h +++ b/gnu/usr.bin/gcc/objc/NXConstStr.h @@ -19,6 +19,12 @@ along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* As a special exception, if you link this library with files + compiled with GCC to produce an executable, this does not cause + the resulting executable to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + #ifndef __nxconstantstring_INCLUDE_GNU #define __nxconstantstring_INCLUDE_GNU diff --git a/gnu/usr.bin/gcc/objc/NXConstStr.m b/gnu/usr.bin/gcc/objc/NXConstStr.m index d3b21174366..d4db76880cd 100644 --- a/gnu/usr.bin/gcc/objc/NXConstStr.m +++ b/gnu/usr.bin/gcc/objc/NXConstStr.m @@ -19,6 +19,12 @@ along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* As a special exception, if you link this library with files + compiled with GCC to produce an executable, this does not cause + the resulting executable to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + #include "objc/NXConstStr.h" @implementation NXConstantString diff --git a/gnu/usr.bin/gcc/objc/Object.m b/gnu/usr.bin/gcc/objc/Object.m index 518d02a3a67..d6a10019773 100644 --- a/gnu/usr.bin/gcc/objc/Object.m +++ b/gnu/usr.bin/gcc/objc/Object.m @@ -1,5 +1,5 @@ /* The implementation of class Object for Objective-C. - Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1994, 1995, 1997 Free Software Foundation, Inc. This file is part of GNU CC. @@ -29,8 +29,6 @@ Boston, MA 02111-1307, USA. */ #include "objc/Protocol.h" #include "objc/objc-api.h" -extern void (*_objc_error)(id object, const char *format, va_list); - extern int errno; #define MAX_CLASS_NAME_LEN 256 @@ -215,7 +213,7 @@ extern int errno; } } - if (parent = [self superClass]) + if ((parent = [self superClass])) return [parent conformsTo: aProtocol]; else return NO; @@ -337,7 +335,7 @@ extern size_t strlen(const char*); object_is_instance(self)?"instance":"class", (aString!=NULL)?aString:""); va_start(ap, aString); - (*_objc_error)(self, fmt, ap); + objc_error(self, OBJC_ERR_UNKNOWN, fmt, ap); va_end(ap); return nil; #undef FMT diff --git a/gnu/usr.bin/gcc/objc/README.threads b/gnu/usr.bin/gcc/objc/README.threads new file mode 100644 index 00000000000..2cafb4ea7cf --- /dev/null +++ b/gnu/usr.bin/gcc/objc/README.threads @@ -0,0 +1,50 @@ +============================================================================== +README - Wed Nov 29 15:16:24 EST 1995 +------------------------------------------------------------------------------ + +Limited documentation is available in the THREADS file. + +This version has been tested on Sun Solaris, SGI Irix, and Windows NT. +It should also work on any single threaded system. + +Thanks go to the following people for help test and debug the library: + + Scott Christley, scottc@ocbi.com + Andrew McCallum, mccallum@cs.rochester.edu + +galen +gchunt@cs.rochester.edu + +Any questions, bug reports, etc should be directed to: + +Scott Christley, scottc@ocbi.com + +Please do not bug Galen with email as he no longer supports the code. + +============================================================================== +Changes from prior releases (in revered chronological order): +------------------------------------------------------------------------------ + +* Fixed bug in copy part of sarray_realloc. I had an < which should + have been <=. (Bug report from Scott). + +------------------------------------------------------------------------------ + +* Support for DEC OSF/1 is definitely broken. My programs always + seg-fault when I link with libpthreads.a. + +* Thread id's are no longer int's, but are instead of type + _objc_thread_t which is typedef'ed from a void *. An invalid thread + id is denoted by NULL and not -1 as before. + +------------------------------------------------------------------------------ + +* Renamed thread-winnt.c to thread-win32.c to better reflect support + for the API on both Windows NT and Windows 95 platforms. + (Who knows, maybe even Win32s :-). + +* Fixed bugs in Win32 support as per report from Scott Christley. + +* Fixed bug in sarray_get as per report from Scott Christley. + + diff --git a/gnu/usr.bin/gcc/objc/THREADS b/gnu/usr.bin/gcc/objc/THREADS new file mode 100644 index 00000000000..9dfbbed97af --- /dev/null +++ b/gnu/usr.bin/gcc/objc/THREADS @@ -0,0 +1,374 @@ +This file describes in little detail the modifications to the +Objective-C runtime needed to make it thread safe. + +First off, kudos to Galen Hunt who is the author of this great work. + +If you have an comments or just want to know where to +send me money to express your undying gratitude for threading the +Objective-C runtime you can reach Galen at: + + gchunt@cs.rochester.edu + +Any questions, comments, bug reports, etc. should send email either to the +GCC bug account or to: + + Scott Christley <scottc@net-community.com> + +* Sarray Threading: + +The most critical component of the Objective-C runtime is the sparse array +structure (sarray). Sarrays store object selectors and implementations. +Following in the tradition of the Objective-C runtime, my threading +support assumes that fast message dispatching is far more important +than *ANY* and *ALL* other operations. The message dispatching thus +uses *NO* locks on any kind. In fact, if you look in sarray.h, you +will notice that the message dispatching has not been modified. +Instead, I have modified the sarray management functions so that all +updates to the sarray data structure can be made in parallel will +message dispatching. + +To support concurrent message dispatching, no dynamically allocated +sarray data structures are freed while more than one thread is +operational. Sarray data structures that are no longer in use are +kept in a linked list of garbage and are released whenever the program +is operating with a single thread. The programmer can also flush the +garbage list by calling sarray_remove_garbage when the programmer can +ensure that no message dispatching is taking place concurrently. The +amount of un-reclaimed sarray garbage should normally be extremely +small in a real program as sarray structures are freed only when using +the "poseAs" functionality and early in program initialization, which +normally occurs while the program is single threaded. + +****************************************************************************** +* Static Variables: + +The following variables are either statically or globally defined. This list +does not include variables which are internal to implementation dependent +versions of thread-*.c. + +The following threading designations are used: + SAFE : Implicitly thread safe. + SINGLE : Must only be used in single thread mode. + MUTEX : Protected by single global mutex objc_runtime_mutex. + UNUSED : Not used in the runtime. + +Variable Name: Usage: Defined: Also used in: +=========================== ====== ============ ===================== +__objc_class_hash MUTEX class.c +__objc_class_links_resolved UNUSED class.c runtime.h +__objc_class_number MUTEX class.c +__objc_dangling_categories UNUSED init.c +__objc_module_list MUTEX init.c +__objc_selector_array MUTEX selector.c +__objc_selector_hash MUTEX selector.c +__objc_selector_max_index MUTEX selector.c sendmsg.c runtime.h +__objc_selector_names MUTEX selector.c +__objc_thread_exit_status SAFE thread.c +__objc_uninstalled_dtable MUTEX sendmsg.c selector.c +_objc_load_callback SAFE init.c objc-api.h +_objc_lookup_class SAFE class.c objc-api.h +_objc_object_alloc SINGLE objects.c objc-api.h +_objc_object_copy SINGLE objects.c objc-api.h +_objc_object_dispose SINGLE objects.c objc-api.h +frwd_sel SAFE2 sendmsg.c +idxsize MUTEX sarray.c sendmsg.c sarray.h +initialize_sel SAFE2 sendmsg.c +narrays MUTEX sarray.c sendmsg.c sarray.h +nbuckets MUTEX sarray.c sendmsg.c sarray.h +nindices MUTEX sarray.c sarray.h +previous_constructors SAFE1 init.c +proto_class SAFE1 init.c +unclaimed_categories MUTEX init.c +unclaimed_proto_list MUTEX init.c +uninitialized_statics MUTEX init.c + +Notes: +1) Initialized once in unithread mode. +2) Initialized value will always be same, guaranteed by lock on selector + hash table. + + +****************************************************************************** +* Frontend/Backend design: + +The design of the Objective-C runtime thread and mutex functions utilizes a +frontend/backend implementation. + +The frontend, as characterized by the files thr.h and thr.c, is a set +of platform independent structures and functions which represent the +user interface. Objective-C programs should use these structures and +functions for their thread and mutex work if they wish to maintain a +high degree of portability across platforms. + +The backend is composed of a file with the necessary code to map the ObjC +thread and mutex to a platform specific implementation. For example, the +file thr-solaris.c contains the implementation for Solaris. When you +configure GCC, it attempts to pick an appropriate backend file for the +target platform; however, you can override this choice by assign the +OBJC_THREAD_FILE make variable to the basename of the backend file. This +is especially useful on platforms which have multiple thread libraries. +For example: + + make OBJC_THREAD_FILE=thr-posix + +would indicate that the generic posix backend file, thr-posix.c, should be +compiled with the ObjC runtime library. If your platform does not support +threads then you should specify the OBJC_THREAD_FILE=thr-single backend file +to compile the ObjC runtime library without thread or mutex support; note +that programs which rely upon the ObjC thread and mutex functions will +compile and link correctly but attempting to create a thread or mutex will +result in an error. + +It is questionable whether it is really necessary to have both a +frontend and backend function for all available functionality. On the +one hand, it provides a clear, consistent differentiation between what +is public and what is private with the downside of having the overhead +of multiple functions calls. For example, the function to have a thread +yield the processor is objc_thread_yield; in the current implementation +this produces a function call set: + +objc_thread_yield() -> __objc_thread_yield() -> system yield function + +This has two extra function calls over calling the platform specific function +explicitly, but the issue is whether only the overhead of a single function +is necessary. + +objc_thread_yield() -> system yield function + +This breaks the public/private dichotomy between the frontend/backend +for the sake of efficiency. It is possible to just use a preprocessor +define so as to eliminate the extra function call: + +#define objc_thread_yield() __objc_thread_yield() + +This has the undesirable effect that if objc_thread_yield is actually +turned into a function based upon future need; then ObjC programs which +access the thread functions would need to be recompiled versus just +being relinked. + +****************************************************************************** +* Threads: + +The thread system attempts to create multiple threads using whatever +operating system or library thread support is available. It does +assume that all system functions are thread safe. Notably this means +that the system implementation of malloc and free must be thread safe. +If a system has multiple processors, the threads are configured for +full parallel processing. + +* Backend initialization functions + +__objc_init_thread_system(void), int + Initialize the thread subsystem. Called once by __objc_exec_class. + Return -1 if error otherwise return 0. + +__objc_close_thread_system(void), int + Closes the thread subsystem, not currently guaranteed to be called. + Return -1 if error otherwise return 0. + +***** +* Frontend thread functions +* User programs should use these functions. + +objc_thread_detach(SEL selector, id object, id argument), objc_thread_t + Creates and detaches a new thread. The new thread starts by + sending the given selector with a single argument to the + given object. + +objc_thread_set_priority(int priority), int + Sets a thread's relative priority within the program. Valid + options are: + + OBJC_THREAD_INTERACTIVE_PRIORITY + OBJC_THREAD_BACKGROUND_PRIORITY + OBJC_THREAD_LOW_PRIORITY + +objc_thread_get_priority(void), int + Query a thread's priority. + +objc_thread_yield(void), void + Yields processor to another thread with equal or higher + priority. It is up to the system scheduler to determine if + the processor is taken or not. + +objc_thread_exit(void), int + Terminates a thread. If this is the last thread executing + then the program will terminate. + +objc_thread_id(void), int + Returns the current thread's id. + +objc_thread_set_data(void *value), int + Set a pointer to the thread's local storage. Local storage is + thread specific. + +objc_thread_get_data(void), void * + Returns the pointer to the thread's local storage. + +***** +* Backend thread functions +* User programs should *NOT* directly call these functions. + +__objc_thread_detach(void (*func)(void *arg), void *arg), objc_thread_t + Spawns a new thread executing func, called by objc_thread_detach. + Return NULL if error otherwise return thread id. + +__objc_thread_set_priority(int priority), int + Set the thread's priority, called by objc_thread_set_priority. + Return -1 if error otherwise return 0. + +__objc_thread_get_priority(void), int + Query a thread's priority, called by objc_thread_get_priority. + Return -1 if error otherwise return the priority. + +__objc_thread_yield(void), void + Yields the processor, called by objc_thread_yield. + +__objc_thread_exit(void), int + Terminates the thread, called by objc_thread_exit. + Return -1 if error otherwise function does not return. + +__objc_thread_id(void), objc_thread_t + Returns the current thread's id, called by objc_thread_id. + Return -1 if error otherwise return thread id. + +__objc_thread_set_data(void *value), int + Set pointer for thread local storage, called by objc_thread_set_data. + Returns -1 if error otherwise return 0. + +__objc_thread_get_data(void), void * + Returns the pointer to the thread's local storage. + Returns NULL if error, called by objc_thread_get_data. + + +****************************************************************************** +* Mutexes: + +Mutexes can be locked recursively. Each locked mutex remembers +its owner (by thread id) and how many times it has been locked. The +last unlock on a mutex removes the system lock and allows other +threads to access the mutex. + +***** +* Frontend mutex functions +* User programs should use these functions. + +objc_mutex_allocate(void), objc_mutex_t + Allocates a new mutex. Mutex is initially unlocked. + Return NULL if error otherwise return mutex pointer. + +objc_mutex_deallocate(objc_mutex_t mutex), int + Free a mutex. Before freeing the mutex, makes sure that no + one else is using it. + Return -1 if error otherwise return 0. + +objc_mutex_lock(objc_mutex_t mutex), int + Locks a mutex. As mentioned earlier, the same thread may call + this routine repeatedly. + Return -1 if error otherwise return 0. + +objc_mutex_trylock(objc_mutex_t mutex), int + Attempts to lock a mutex. If lock on mutex can be acquired + then function operates exactly as objc_mutex_lock. + Return -1 if failed to acquire lock otherwise return 0. + +objc_mutex_unlock(objc_mutex_t mutex), int + Unlocks the mutex by one level. Other threads may not acquire + the mutex until this thread has released all locks on it. + Return -1 if error otherwise return 0. + +***** +* Backend mutex functions +* User programs should *NOT* directly call these functions. + +__objc_mutex_allocate(objc_mutex_t mutex), int + Allocates a new mutex, called by objc_mutex_allocate. + Return -1 if error otherwise return 0. + +__objc_mutex_deallocate(objc_mutex_t mutex), int + Free a mutex, called by objc_mutex_deallocate. + Return -1 if error otherwise return 0. + +__objc_mutex_lock(objc_mutex_t mutex), int + Locks a mutex, called by objc_mutex_lock. + Return -1 if error otherwise return 0. + +__objc_mutex_trylock(objc_mutex_t mutex), int + Attempts to lock a mutex, called by objc_mutex_trylock. + Return -1 if failed to acquire lock or error otherwise return 0. + +__objc_mutex_unlock(objc_mutex_t mutex), int + Unlocks the mutex, called by objc_mutex_unlock. + Return -1 if error otherwise return 0. + +****************************************************************************** +* Condition Mutexes: + +Mutexes can be locked recursively. Each locked mutex remembers +its owner (by thread id) and how many times it has been locked. The +last unlock on a mutex removes the system lock and allows other +threads to access the mutex. + +* +* Frontend condition mutex functions +* User programs should use these functions. +* + +objc_condition_allocate(void), objc_condition_t + Allocate a condition mutex. + Return NULL if error otherwise return condition pointer. + +objc_condition_deallocate(objc_condition_t condition), int + Deallocate a condition. Note that this includes an implicit + condition_broadcast to insure that waiting threads have the + opportunity to wake. It is legal to dealloc a condition only + if no other thread is/will be using it. Does NOT check for + other threads waiting but just wakes them up. + Return -1 if error otherwise return 0. + +objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex), int + Wait on the condition unlocking the mutex until objc_condition_signal() + or objc_condition_broadcast() are called for the same condition. The + given mutex *must* have the depth 1 so that it can be unlocked + here, for someone else can lock it and signal/broadcast the condition. + The mutex is used to lock access to the shared data that make up the + "condition" predicate. + Return -1 if error otherwise return 0. + +objc_condition_broadcast(objc_condition_t condition), int + Wake up all threads waiting on this condition. It is recommended that + the called would lock the same mutex as the threads in + objc_condition_wait before changing the "condition predicate" + and make this call and unlock it right away after this call. + Return -1 if error otherwise return 0. + +objc_condition_signal(objc_condition_t condition), int + Wake up one thread waiting on this condition. + Return -1 if error otherwise return 0. + +* +* Backend condition mutex functions +* User programs should *NOT* directly call these functions. +* + +__objc_condition_allocate(objc_condition_t condition), int + Allocate a condition mutex, called by objc_condition_allocate. + Return -1 if error otherwise return 0. + +__objc_condition_deallocate(objc_condition_t condition), int + Deallocate a condition, called by objc_condition_deallocate. + Return -1 if error otherwise return 0. + +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex), int + Wait on the condition, called by objc_condition_wait. + Return -1 if error otherwise return 0 when condition is met. + +__objc_condition_broadcast(objc_condition_t condition), int + Wake up all threads waiting on this condition. + Called by objc_condition_broadcast. + Return -1 if error otherwise return 0. + +__objc_condition_signal(objc_condition_t condition), int + Wake up one thread waiting on this condition. + Called by objc_condition_signal. + Return -1 if error otherwise return 0. diff --git a/gnu/usr.bin/gcc/objc/THREADS.MACH b/gnu/usr.bin/gcc/objc/THREADS.MACH new file mode 100644 index 00000000000..55de6637866 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/THREADS.MACH @@ -0,0 +1,23 @@ +This readme refers to the file thr-mach.c. + +Under mach, thread priorities are kinda strange-- any given thread has +a MAXIMUM priority and a BASE priority. The BASE priority is the +current priority of the thread and the MAXIMUM is the maximum possible +priority the thread can assume. The developer can lower, but never +raise the maximum priority. + +The gcc concept of thread priorities is that they run at one of three +levels; interactive, background, and low. + +Under mach, this is translated to: + +interactive -- set priority to maximum +background -- set priority to 2/3 of maximum +low -- set priority to 1/3 of maximum + +This means that it is possible for a thread with the priority of +interactive to actually run at a lower priority than another thread +with a background, or even low, priority if the developer has modified +the maximum priority. + + diff --git a/gnu/usr.bin/gcc/objc/archive.c b/gnu/usr.bin/gcc/objc/archive.c index 0d5515270b4..af9591b25ec 100644 --- a/gnu/usr.bin/gcc/objc/archive.c +++ b/gnu/usr.bin/gcc/objc/archive.c @@ -1,5 +1,5 @@ /* GNU Objective C Runtime archiving - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -24,10 +24,15 @@ Boston, MA 02111-1307, USA. */ however invalidate any other reasons why the executable file might be covered by the GNU General Public License. */ +#include "config.h" #include "runtime.h" #include "typedstream.h" #include "encoding.h" +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + extern int fflush(FILE*); #define ROUND(V, A) \ @@ -37,11 +42,6 @@ extern int fflush(FILE*); #define PTR2LONG(P) (((char*)(P))-(char*)0) #define LONG2PTR(L) (((char*)0)+(L)) -#define __objc_fatal(format, args...) \ - { fprintf(stderr, "archiving: "); \ - fprintf(stderr, format, ## args); \ - fprintf(stderr, "\n"); abort(); } - /* Declare some functions... */ static int @@ -141,7 +141,8 @@ __objc_code_unsigned_short (unsigned char* buf, unsigned short val) } int -objc_write_unsigned_short (struct objc_typed_stream* stream, unsigned short value) +objc_write_unsigned_short (struct objc_typed_stream* stream, + unsigned short value) { unsigned char buf[sizeof (unsigned short)+1]; int len = __objc_code_unsigned_short (buf, value); @@ -252,7 +253,8 @@ __objc_code_unsigned_long (unsigned char* buf, unsigned long val) } int -objc_write_unsigned_long (struct objc_typed_stream* stream, unsigned long value) +objc_write_unsigned_long (struct objc_typed_stream* stream, + unsigned long value) { unsigned char buf[sizeof(unsigned long)+1]; int len = __objc_code_unsigned_long (buf, value); @@ -315,7 +317,8 @@ objc_write_string_atomic (struct objc_typed_stream* stream, } static int -objc_write_register_common (struct objc_typed_stream* stream, unsigned long key) +objc_write_register_common (struct objc_typed_stream* stream, + unsigned long key) { unsigned char buf[sizeof (unsigned long)+2]; int len = __objc_code_unsigned_long (buf+1, key); @@ -359,7 +362,11 @@ __objc_write_extension (struct objc_typed_stream* stream, unsigned char code) return (*stream->write)(stream->physical, &buf, 1); } else - abort(); + { + objc_error(nil, OBJC_ERR_BAD_OPCODE, + "__objc_write_extension: bad opcode %c\n", code); + return -1; + } } __inline__ int @@ -392,9 +399,10 @@ objc_write_object_reference (struct objc_typed_stream* stream, id object) int objc_write_root_object (struct objc_typed_stream* stream, id object) { - int len; + int len = 0; if (stream->writing_root_p) - __objc_fatal ("objc_write_root_object called recursively") + objc_error (nil, OBJC_ERR_RECURSE_ROOT, + "objc_write_root_object called recursively"); else { stream->writing_root_p = 1; @@ -488,7 +496,8 @@ objc_write_selector (struct objc_typed_stream* stream, SEL selector) else { int length; - hash_add (&stream->stream_table, LONG2PTR(key=PTR2LONG(sel_name)), (char*)sel_name); + hash_add (&stream->stream_table, + LONG2PTR(key=PTR2LONG(sel_name)), (char*)sel_name); if ((length = objc_write_register_common (stream, key))) return __objc_write_selector (stream, selector); return length; @@ -520,8 +529,9 @@ objc_read_char (struct objc_typed_stream* stream, char* val) } else - __objc_fatal("expected 8bit signed int, got %dbit int", - (int)(buf&_B_NUMBER)*8); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected 8bit signed int, got %dbit int", + (int)(buf&_B_NUMBER)*8); } return len; } @@ -541,8 +551,9 @@ objc_read_unsigned_char (struct objc_typed_stream* stream, unsigned char* val) len = (*stream->read)(stream->physical, val, 1); else - __objc_fatal("expected 8bit unsigned int, got %dbit int", - (int)(buf&_B_NUMBER)*8); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected 8bit unsigned int, got %dbit int", + (int)(buf&_B_NUMBER)*8); } return len; } @@ -562,7 +573,8 @@ objc_read_short (struct objc_typed_stream* stream, short* value) int pos = 1; int nbytes = buf[0] & _B_NUMBER; if (nbytes > sizeof (short)) - __objc_fatal("expected short, got bigger (%dbits)", nbytes*8); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected short, got bigger (%dbits)", nbytes*8); len = (*stream->read)(stream->physical, buf+1, nbytes); (*value) = 0; while (pos <= nbytes) @@ -590,7 +602,8 @@ objc_read_unsigned_short (struct objc_typed_stream* stream, int pos = 1; int nbytes = buf[0] & _B_NUMBER; if (nbytes > sizeof (short)) - __objc_fatal("expected short, got int or bigger"); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected short, got int or bigger"); len = (*stream->read)(stream->physical, buf+1, nbytes); (*value) = 0; while (pos <= nbytes) @@ -616,7 +629,7 @@ objc_read_int (struct objc_typed_stream* stream, int* value) int pos = 1; int nbytes = buf[0] & _B_NUMBER; if (nbytes > sizeof (int)) - __objc_fatal("expected int, got bigger"); + objc_error(nil, OBJC_ERR_BAD_DATA, "expected int, got bigger"); len = (*stream->read)(stream->physical, buf+1, nbytes); (*value) = 0; while (pos <= nbytes) @@ -643,7 +656,7 @@ objc_read_long (struct objc_typed_stream* stream, long* value) int pos = 1; int nbytes = buf[0] & _B_NUMBER; if (nbytes > sizeof (long)) - __objc_fatal("expected long, got bigger"); + objc_error(nil, OBJC_ERR_BAD_DATA, "expected long, got bigger"); len = (*stream->read)(stream->physical, buf+1, nbytes); (*value) = 0; while (pos <= nbytes) @@ -663,7 +676,7 @@ __objc_read_nbyte_uint (struct objc_typed_stream* stream, unsigned char buf[sizeof(unsigned int)+1]; if (nbytes > sizeof (int)) - __objc_fatal("expected int, got bigger"); + objc_error(nil, OBJC_ERR_BAD_DATA, "expected int, got bigger"); len = (*stream->read)(stream->physical, buf, nbytes); (*val) = 0; @@ -699,7 +712,7 @@ __objc_read_nbyte_ulong (struct objc_typed_stream* stream, unsigned char buf[sizeof(unsigned long)+1]; if (nbytes > sizeof (long)) - __objc_fatal("expected long, got bigger"); + objc_error(nil, OBJC_ERR_BAD_DATA, "expected long, got bigger"); len = (*stream->read)(stream->physical, buf, nbytes); (*val) = 0; @@ -747,7 +760,7 @@ objc_read_string (struct objc_typed_stream* stream, case _B_SSTR: { int length = buf[0]&_B_VALUE; - (*string) = (char*)__objc_xmalloc(length+1); + (*string) = (char*)objc_malloc(length+1); if (key) hash_add (&stream->stream_table, LONG2PTR(key), *string); len = (*stream->read)(stream->physical, *string, length); @@ -760,7 +773,7 @@ objc_read_string (struct objc_typed_stream* stream, char *tmp; len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key); tmp = hash_value_for_key (stream->stream_table, LONG2PTR (key)); - *string = __objc_xmalloc (strlen(tmp) + 1); + *string = objc_malloc (strlen(tmp) + 1); strcpy (*string, tmp); } break; @@ -770,7 +783,7 @@ objc_read_string (struct objc_typed_stream* stream, unsigned int nbytes = buf[0]&_B_VALUE; len = __objc_read_nbyte_uint(stream, nbytes, &nbytes); if (len) { - (*string) = (char*)__objc_xmalloc(nbytes+1); + (*string) = (char*)objc_malloc(nbytes+1); if (key) hash_add (&stream->stream_table, LONG2PTR(key), *string); len = (*stream->read)(stream->physical, *string, nbytes); @@ -780,7 +793,8 @@ objc_read_string (struct objc_typed_stream* stream, break; default: - __objc_fatal("expected string, got opcode %c\n", (buf[0]&_B_CODE)); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected string, got opcode %c\n", (buf[0]&_B_CODE)); } } @@ -825,13 +839,14 @@ objc_read_object (struct objc_typed_stream* stream, id* object) /* check null-byte */ len = (*stream->read)(stream->physical, buf, 1); if (buf[0] != '\0') - __objc_fatal("expected null-byte, got opcode %c", buf[0]); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected null-byte, got opcode %c", buf[0]); } else if ((buf[0]&_B_CODE) == _B_UCOMM) { if (key) - __objc_fatal("cannot register use upcode..."); + objc_error(nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key); (*object) = hash_value_for_key (stream->object_table, LONG2PTR(key)); } @@ -840,20 +855,24 @@ objc_read_object (struct objc_typed_stream* stream, id* object) { struct objc_list* other; len = objc_read_unsigned_long (stream, &key); - other = (struct objc_list*)hash_value_for_key (stream->object_refs, LONG2PTR(key)); - hash_add (&stream->object_refs, LONG2PTR(key), (void*)list_cons(object, other)); + other = (struct objc_list*)hash_value_for_key (stream->object_refs, + LONG2PTR(key)); + hash_add (&stream->object_refs, LONG2PTR(key), + (void*)list_cons(object, other)); } else if (buf[0] == (_B_EXT | _BX_OBJROOT)) /* a root object */ { if (key) - __objc_fatal("cannot register root object..."); + objc_error(nil, OBJC_ERR_BAD_KEY, + "cannot register root object..."); len = objc_read_object (stream, object); __objc_finish_read_root_object (stream); } else - __objc_fatal("expected object, got opcode %c", buf[0]); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected object, got opcode %c", buf[0]); } return len; } @@ -881,7 +900,7 @@ objc_read_class (struct objc_typed_stream* stream, Class* class) /* get class */ len = objc_read_string (stream, &class_name); (*class) = objc_get_class(class_name); - free (class_name); + objc_free(class_name); /* register */ if (key) @@ -894,15 +913,17 @@ objc_read_class (struct objc_typed_stream* stream, Class* class) else if ((buf[0]&_B_CODE) == _B_UCOMM) { if (key) - __objc_fatal("cannot register use upcode..."); + objc_error(nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key); (*class) = hash_value_for_key (stream->stream_table, LONG2PTR(key)); if (!*class) - __objc_fatal("cannot find class for key %lu", key); + objc_error(nil, OBJC_ERR_BAD_CLASS, + "cannot find class for key %lu", key); } else - __objc_fatal("expected class, got opcode %c", buf[0]); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected class, got opcode %c", buf[0]); } return len; } @@ -936,7 +957,7 @@ objc_read_selector (struct objc_typed_stream* stream, SEL* selector) } else (*selector) = sel_get_any_uid(selector_name); - free (selector_name); + objc_free(selector_name); /* register */ if (key) @@ -946,13 +967,15 @@ objc_read_selector (struct objc_typed_stream* stream, SEL* selector) else if ((buf[0]&_B_CODE) == _B_UCOMM) { if (key) - __objc_fatal("cannot register use upcode..."); + objc_error(nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); len = __objc_read_nbyte_ulong(stream, (buf[0] & _B_VALUE), &key); - (*selector) = hash_value_for_key (stream->stream_table, LONG2PTR(key)); + (*selector) = hash_value_for_key (stream->stream_table, + LONG2PTR(key)); } else - __objc_fatal("expected selector, got opcode %c", buf[0]); + objc_error(nil, OBJC_ERR_BAD_DATA, + "expected selector, got opcode %c", buf[0]); } return len; } @@ -1019,13 +1042,15 @@ objc_write_type(TypedStream* stream, const char* type, const void* data) break; case _C_ATOM: - return objc_write_string_atomic (stream, *(char**)data, strlen(*(char**)data)); + return objc_write_string_atomic (stream, *(char**)data, + strlen(*(char**)data)); break; case _C_ARY_B: { int len = atoi(type+1); - while (isdigit(*++type)); + while (isdigit(*++type)) + ; return objc_write_array (stream, type, len, data); } break; @@ -1034,8 +1059,9 @@ objc_write_type(TypedStream* stream, const char* type, const void* data) { int acc_size = 0; int align; - while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */ - while (*type != _C_STRUCT_E); + while (*type != _C_STRUCT_E && *type++ != '=') + ; /* skip "<name>=" */ + while (*type != _C_STRUCT_E) { align = objc_alignof_type (type); /* padd to alignment */ acc_size += ROUND (acc_size, align); @@ -1047,8 +1073,11 @@ objc_write_type(TypedStream* stream, const char* type, const void* data) } default: - fprintf(stderr, "objc_write_type: cannot parse typespec: %s\n", type); - abort(); + { + objc_error(nil, OBJC_ERR_BAD_TYPE, + "objc_write_type: cannot parse typespec: %s\n", type); + return 0; + } } } @@ -1116,7 +1145,8 @@ objc_read_type(TypedStream* stream, const char* type, void* data) case _C_ARY_B: { int len = atoi(type+1); - while (isdigit(*++type)); + while (isdigit(*++type)) + ; return objc_read_array (stream, type, len, data); } break; @@ -1125,8 +1155,9 @@ objc_read_type(TypedStream* stream, const char* type, void* data) { int acc_size = 0; int align; - while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */ - while (*type != _C_STRUCT_E); + while (*type != _C_STRUCT_E && *type++ != '=') + ; /* skip "<name>=" */ + while (*type != _C_STRUCT_E) { align = objc_alignof_type (type); /* padd to alignment */ acc_size += ROUND (acc_size, align); @@ -1138,8 +1169,11 @@ objc_read_type(TypedStream* stream, const char* type, void* data) } default: - fprintf(stderr, "objc_read_type: cannot parse typespec: %s\n", type); - abort(); + { + objc_error(nil, OBJC_ERR_BAD_TYPE, + "objc_read_type: cannot parse typespec: %s\n", type); + return 0; + } } } @@ -1229,17 +1263,18 @@ objc_write_types (TypedStream* stream, const char* type, ...) { int len = atoi(c+1); const char* t = c; - while (isdigit(*++t)); + while (isdigit(*++t)) + ; res = objc_write_array (stream, t, len, va_arg(args, void*)); t = objc_skip_typespec (t); if (*t != _C_ARY_E) - __objc_fatal("expected `]', got: %s", t); + objc_error(nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t); } break; default: - fprintf(stderr, "objc_write_types: cannot parse typespec: %s\n", type); - abort(); + objc_error(nil, OBJC_ERR_BAD_TYPE, + "objc_write_types: cannot parse typespec: %s\n", type); } } va_end(args); @@ -1320,17 +1355,18 @@ objc_read_types(TypedStream* stream, const char* type, ...) { int len = atoi(c+1); const char* t = c; - while (isdigit(*++t)); + while (isdigit(*++t)) + ; res = objc_read_array (stream, t, len, va_arg(args, void*)); t = objc_skip_typespec (t); if (*t != _C_ARY_E) - __objc_fatal("expected `]', got: %s", t); + objc_error(nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t); } break; default: - fprintf(stderr, "objc_read_types: cannot parse typespec: %s\n", type); - abort(); + objc_error(nil, OBJC_ERR_BAD_TYPE, + "objc_read_types: cannot parse typespec: %s\n", type); } } va_end(args); @@ -1379,12 +1415,6 @@ objc_read_array (TypedStream* stream, const char* type, return 1; } -static void -__objc_free (void* p) -{ - free (p); -} - static int __objc_fread(FILE* file, char* data, int len) { @@ -1406,13 +1436,15 @@ __objc_feof(FILE* file) static int __objc_no_write(FILE* file, char* data, int len) { - __objc_fatal ("TypedStream not open for writing"); + objc_error (nil, OBJC_ERR_NO_WRITE, "TypedStream not open for writing"); + return 0; } static int __objc_no_read(FILE* file, char* data, int len) { - __objc_fatal ("TypedStream not open for reading"); + objc_error (nil, OBJC_ERR_NO_READ, "TypedStream not open for reading"); + return 0; } static int @@ -1422,10 +1454,12 @@ __objc_read_typed_stream_signature (TypedStream* stream) int pos = 0; do (*stream->read)(stream->physical, buffer+pos, 1); - while (buffer[pos++] != '\0'); + while (buffer[pos++] != '\0') + ; sscanf (buffer, "GNU TypedStream %d", &stream->version); if (stream->version != OBJC_TYPED_STREAM_VERSION) - __objc_fatal ("cannot handle TypedStream version %d", stream->version); + objc_error (nil, OBJC_ERR_STREAM_VERSION, + "cannot handle TypedStream version %d", stream->version); return 1; } @@ -1450,11 +1484,12 @@ static void __objc_finish_write_root_object(struct objc_typed_stream* stream) static void __objc_finish_read_root_object(struct objc_typed_stream* stream) { node_ptr node; - struct objc_list* free_list; SEL awake_sel = sel_get_any_uid ("awake"); + cache_ptr free_list = hash_new (64, + (hash_func_type) hash_ptr, + (compare_func_type) compare_ptrs); /* resolve object forward references */ - free_list = list_cons(NULL, NULL); for (node = hash_next (stream->object_refs, NULL); node; node = hash_next (stream->object_refs, node)) { @@ -1464,13 +1499,19 @@ static void __objc_finish_read_root_object(struct objc_typed_stream* stream) while(reflist) { *((id*)reflist->head) = object; - if (list_find(&free_list, reflist) == NULL) - free_list = list_cons (reflist, free_list); + if (hash_value_for_key (free_list,reflist) == NULL) + hash_add (&free_list,reflist,reflist); + reflist = reflist->tail; } } - list_mapcar (free_list, __objc_free); - list_free (free_list); + + /* apply __objc_free to all objects stored in free_list */ + for (node = hash_next (free_list, NULL); node; + node = hash_next (free_list, node)) + objc_free ((void *) node->key); + + hash_delete (free_list); /* empty object reference table */ hash_delete (stream->object_refs); @@ -1503,7 +1544,7 @@ static void __objc_finish_read_root_object(struct objc_typed_stream* stream) TypedStream* objc_open_typed_stream (FILE* physical, int mode) { - TypedStream* s = (TypedStream*)__objc_xmalloc(sizeof(TypedStream)); + TypedStream* s = (TypedStream*)objc_malloc(sizeof(TypedStream)); s->mode = mode; s->physical = physical; @@ -1590,7 +1631,7 @@ objc_close_typed_stream (TypedStream* stream) if (stream->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM)) fclose ((FILE*)stream->physical); - free (stream); + objc_free(stream); } BOOL diff --git a/gnu/usr.bin/gcc/objc/class.c b/gnu/usr.bin/gcc/objc/class.c index 3617a098803..44aa1b9f98e 100644 --- a/gnu/usr.bin/gcc/objc/class.c +++ b/gnu/usr.bin/gcc/objc/class.c @@ -1,5 +1,5 @@ /* GNU Objective C Runtime class related functions - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup and Dennis Glatting. This file is part of GNU CC. @@ -27,16 +27,16 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sarray.h" /* The table of classname->class. Used for objc_lookup_class and friends */ -static cache_ptr __objc_class_hash = 0; +static cache_ptr __objc_class_hash = 0; /* !T:MUTEX */ /* This is a hook which is called by objc_get_class and objc_lookup_class if the runtime is not able to find the class. This may e.g. try to load in the class using dynamic loading */ -Class (*_objc_lookup_class)(const char* name) = 0; +Class (*_objc_lookup_class)(const char* name) = 0; /* !T:SAFE */ /* True when class links has been resolved */ -BOOL __objc_class_links_resolved = NO; +BOOL __objc_class_links_resolved = NO; /* !T:UNUSED */ /* Initial number of buckets size of class hash table. */ @@ -49,10 +49,14 @@ void __objc_init_class_tables() if(__objc_class_hash) return; + objc_mutex_lock(__objc_runtime_mutex); + __objc_class_hash = hash_new (CLASS_HASH_SIZE, (hash_func_type) hash_string, (compare_func_type) compare_strings); + + objc_mutex_unlock(__objc_runtime_mutex); } /* This function adds a class to the class hash table, and assigns the @@ -62,6 +66,8 @@ __objc_add_class_to_hash(Class class) { Class h_class; + objc_mutex_lock(__objc_runtime_mutex); + /* make sure the table is there */ assert(__objc_class_hash); @@ -82,6 +88,8 @@ __objc_add_class_to_hash(Class class) ++class_number; hash_add (&__objc_class_hash, class->name, class); } + + objc_mutex_unlock(__objc_runtime_mutex); } /* Get the class object for the class named NAME. If NAME does not @@ -91,11 +99,15 @@ Class objc_lookup_class (const char* name) { Class class; + objc_mutex_lock(__objc_runtime_mutex); + /* Make sure the class hash table exists. */ assert (__objc_class_hash); class = hash_value_for_key (__objc_class_hash, name); + objc_mutex_unlock(__objc_runtime_mutex); + if (class) return class; @@ -113,11 +125,15 @@ objc_get_class (const char *name) { Class class; + objc_mutex_lock(__objc_runtime_mutex); + /* Make sure the class hash table exists. */ assert (__objc_class_hash); class = hash_value_for_key (__objc_class_hash, name); + objc_mutex_unlock(__objc_runtime_mutex); + if (class) return class; @@ -127,8 +143,9 @@ objc_get_class (const char *name) if(class) return class; - fprintf(stderr, "objc runtime: cannot find class %s\n", name); - abort(); + objc_error(nil, OBJC_ERR_BAD_CLASS, + "objc runtime: cannot find class %s\n", name); + return 0; } MetaClass @@ -149,11 +166,16 @@ objc_get_meta_class(const char *name) Class objc_next_class(void **enum_state) { + objc_mutex_lock(__objc_runtime_mutex); + /* make sure the table is there */ assert(__objc_class_hash); *(node_ptr*)enum_state = hash_next(__objc_class_hash, *(node_ptr*)enum_state); + + objc_mutex_unlock(__objc_runtime_mutex); + if (*(node_ptr*)enum_state) return (*(node_ptr*)enum_state)->value; return (Class)0; @@ -169,6 +191,8 @@ void __objc_resolve_class_links() assert(object_class); + objc_mutex_lock(__objc_runtime_mutex); + /* Assign subclass links */ for (node = hash_next (__objc_class_hash, NULL); node; node = hash_next (__objc_class_hash, node)) @@ -234,6 +258,8 @@ void __objc_resolve_class_links() sub_class->class_pointer->super_class = class1->class_pointer; } } + + objc_mutex_unlock(__objc_runtime_mutex); } @@ -281,7 +307,8 @@ class_pose_as (Class impostor, Class super_class) if (CLS_ISCLASS (sub)) { /* meta classes */ - CLASSOF (sub)->sibling_class = CLASSOF (impostor)->subclass_list; + CLASSOF (sub)->sibling_class = + CLASSOF (impostor)->subclass_list; CLASSOF (sub)->super_class = CLASSOF (impostor); CLASSOF (impostor)->subclass_list = CLASSOF (sub); } @@ -307,6 +334,8 @@ class_pose_as (Class impostor, Class super_class) what the keys of the hashtable is, change all values that are superclass into impostor. */ + objc_mutex_lock(__objc_runtime_mutex); + for (node = hash_next (__objc_class_hash, NULL); node; node = hash_next (__objc_class_hash, node)) { @@ -317,6 +346,8 @@ class_pose_as (Class impostor, Class super_class) } } + objc_mutex_unlock(__objc_runtime_mutex); + /* next, we update the dispatch tables... */ __objc_update_dispatch_table_for_class (CLASSOF (impostor)); __objc_update_dispatch_table_for_class (impostor); diff --git a/gnu/usr.bin/gcc/objc/config-lang.in b/gnu/usr.bin/gcc/objc/config-lang.in new file mode 100644 index 00000000000..736ba73b4ec --- /dev/null +++ b/gnu/usr.bin/gcc/objc/config-lang.in @@ -0,0 +1,37 @@ +# Top level configure fragment for the GNU Objective-C Runtime Library. +# Copyright (C) 1997 Free Software Foundation, Inc. + +#This file is part of GNU CC. + +#GNU CC is free software; you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation; either version 2, or (at your option) +#any later version. + +#GNU CC is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. + +#You should have received a copy of the GNU General Public License +#along with GNU CC; see the file COPYING. If not, write to +#the Free Software Foundation, 59 Temple Place - Suite 330, +#Boston, MA 02111-1307, USA. + +# Configure looks for the existence of this file to auto-config each language. +# We define several parameters used by configure: +# +# language - name of language as it would appear in $(LANGUAGES) +# compilers - value to add to $(COMPILERS) +# stagestuff - files to add to $(STAGESTUFF) +# diff_excludes - files to ignore when building diffs between two versions. + +language="objc" + +compilers="cc1obj\$(exeext)" + +stagestuff="" + +diff_excludes="" + +echo "Using \`$srcdir/objc/thr-${thread_file}.c' as Objective-C Runtime thread file." diff --git a/gnu/usr.bin/gcc/objc/encoding.c b/gnu/usr.bin/gcc/objc/encoding.c index 9620664746b..acd6b7a7b73 100644 --- a/gnu/usr.bin/gcc/objc/encoding.c +++ b/gnu/usr.bin/gcc/objc/encoding.c @@ -1,5 +1,5 @@ /* Encoding of types for Objective C. - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -153,7 +153,10 @@ objc_sizeof_type(const char* type) } default: - abort(); + { + objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type); + return 0; + } } } @@ -218,6 +221,7 @@ objc_alignof_type(const char* type) return __alignof__(double); break; + case _C_PTR: case _C_ATOM: case _C_CHARPTR: return __alignof__(char*); @@ -250,7 +254,10 @@ objc_alignof_type(const char* type) } default: - abort(); + { + objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type); + return 0; + } } } @@ -341,6 +348,7 @@ objc_skip_typespec (const char* type) case _C_FLT: case _C_DBL: case _C_VOID: + case _C_UNDEF: return ++type; break; @@ -352,7 +360,10 @@ objc_skip_typespec (const char* type) if (*type == _C_ARY_E) return ++type; else - abort(); + { + objc_error(nil, OBJC_ERR_BAD_TYPE, "bad array type %s\n", type); + return 0; + } case _C_STRUCT_B: /* skip name, and elements until closing '}' */ @@ -374,7 +385,10 @@ objc_skip_typespec (const char* type) return objc_skip_typespec (++type); default: - abort(); + { + objc_error(nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type); + return 0; + } } } diff --git a/gnu/usr.bin/gcc/objc/encoding.h b/gnu/usr.bin/gcc/objc/encoding.h index c956034108a..141e9fe9e97 100644 --- a/gnu/usr.bin/gcc/objc/encoding.h +++ b/gnu/usr.bin/gcc/objc/encoding.h @@ -1,5 +1,5 @@ /* Encoding of types for Objective C. - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1993, 1997 Free Software Foundation, Inc. Author: Kresten Krab Thorup @@ -57,7 +57,7 @@ const char* objc_skip_typespec (const char* type); const char* objc_skip_offset (const char* type); const char* objc_skip_argspec (const char* type); int method_get_number_of_arguments (struct objc_method*); -int method_get_size_of_arguments (struct objc_method*); +int method_get_sizeof_arguments (struct objc_method*); char* method_get_first_argument (struct objc_method*, arglist_t argframe, diff --git a/gnu/usr.bin/gcc/objc/hash.c b/gnu/usr.bin/gcc/objc/hash.c index a3077597e42..7534330fa1c 100644 --- a/gnu/usr.bin/gcc/objc/hash.c +++ b/gnu/usr.bin/gcc/objc/hash.c @@ -1,5 +1,5 @@ /* Hash tables for Objective C internal structures - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1993, 1996, 1997 Free Software Foundation, Inc. This file is part of GNU CC. @@ -27,7 +27,6 @@ Boston, MA 02111-1307, USA. */ #include "assert.h" #include "objc/hash.h" -#include "objc/objc.h" #include "runtime.h" /* for DEBUG_PRINTF */ @@ -40,8 +39,6 @@ Boston, MA 02111-1307, USA. */ #define EXPANSION(cache) \ ((cache)->size * 2) -void *__objc_xcalloc (size_t, size_t); - cache_ptr hash_new (unsigned int size, hash_func_type hash_func, compare_func_type compare_func) @@ -54,13 +51,13 @@ hash_new (unsigned int size, hash_func_type hash_func, /* Allocate the cache structure. calloc insures its initialization for default values. */ - cache = (cache_ptr) __objc_xcalloc (1, sizeof (struct cache)); + cache = (cache_ptr) objc_calloc (1, sizeof (struct cache)); assert (cache); /* Allocate the array of buckets for the cache. calloc initializes all of the pointers to NULL. */ cache->node_table - = (node_ptr *) __objc_xcalloc (size, sizeof (node_ptr)); + = (node_ptr *) objc_calloc (size, sizeof (node_ptr)); assert (cache->node_table); cache->size = size; @@ -83,15 +80,28 @@ void hash_delete (cache_ptr cache) { node_ptr node; - + node_ptr next_node; + unsigned int i; /* Purge all key/value pairs from the table. */ - while ((node = hash_next (cache, NULL))) - hash_remove (cache, node->key); + /* Step through the nodes one by one and remove every node WITHOUT + using hash_next. this makes hash_delete much more efficient. */ + for (i = 0;i < cache->size;i++) { + if ((node = cache->node_table[i])) { + /* an entry in the hash table has been found, now step through the + nodes next in the list and free them. */ + while ((next_node = node->next)) { + hash_remove (cache,node->key); + node = next_node; + } + + hash_remove (cache,node->key); + } + } /* Release the array of nodes and the cache itself. */ - free (cache->node_table); - free (cache); + objc_free(cache->node_table); + objc_free(cache); } @@ -99,7 +109,7 @@ void hash_add (cache_ptr *cachep, const void *key, void *value) { size_t indx = (*(*cachep)->hash_func)(*cachep, key); - node_ptr node = (node_ptr) __objc_xcalloc (1, sizeof (struct cache_node)); + node_ptr node = (node_ptr) objc_calloc (1, sizeof (struct cache_node)); assert (node); @@ -172,7 +182,7 @@ hash_remove (cache_ptr cache, const void *key) /* Special case. First element is the key/value pair to be removed. */ if ((*cache->compare_func)(node->key, key)) { cache->node_table[indx] = node->next; - free (node); + objc_free(node); } else { /* Otherwise, find the hash entry. */ @@ -183,7 +193,7 @@ hash_remove (cache_ptr cache, const void *key) if ((*cache->compare_func)(node->key, key)) { prev->next = node->next, removed = YES; - free (node); + objc_free(node); } else prev = node, node = node->next; } while (!removed && node); @@ -252,3 +262,22 @@ hash_value_for_key (cache_ptr cache, const void *key) return retval; } + +/* Given KEY, return YES if it exists in the CACHE. + Return NO if it does not */ + +BOOL +hash_is_key_in_hash (cache_ptr cache, const void *key) +{ + node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)]; + + if (node) + do { + if ((*cache->compare_func)(node->key, key)) + return YES; + else + node = node->next; + } while (node); + + return NO; +} diff --git a/gnu/usr.bin/gcc/objc/hash.h b/gnu/usr.bin/gcc/objc/hash.h index 7a83b0854d1..bddb791c820 100644 --- a/gnu/usr.bin/gcc/objc/hash.h +++ b/gnu/usr.bin/gcc/objc/hash.h @@ -1,5 +1,5 @@ /* Hash tables for Objective C method dispatch. - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc. This file is part of GNU CC. @@ -29,6 +29,7 @@ Boston, MA 02111-1307, USA. */ #define __hash_INCLUDE_GNU #include <stddef.h> +#include <objc/objc.h> /* * This data structure is used to hold items @@ -140,6 +141,9 @@ node_ptr hash_next (cache_ptr cache, node_ptr node); void *hash_value_for_key (cache_ptr cache, const void *key); +/* Used to determine if the given key exists in the hash table */ + +BOOL hash_is_key_in_hash (cache_ptr cache, const void *key); /************************************************ diff --git a/gnu/usr.bin/gcc/objc/init.c b/gnu/usr.bin/gcc/objc/init.c index 9560dc20234..c900cac65e8 100644 --- a/gnu/usr.bin/gcc/objc/init.c +++ b/gnu/usr.bin/gcc/objc/init.c @@ -1,6 +1,7 @@ /* GNU Objective C Runtime initialization - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup + +load support contributed by Ovidiu Predescu <ovidiu@net-community.com> This file is part of GNU CC. @@ -27,17 +28,23 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* The version number of this runtime. This must match the number defined in gcc (objc-act.c) */ -#define OBJC_VERSION 7 +#define OBJC_VERSION 8 #define PROTOCOL_VERSION 2 /* This list contains all modules currently loaded into the runtime */ -static struct objc_list* __objc_module_list = 0; +static struct objc_list* __objc_module_list = 0; /* !T:MUTEX */ /* This list contains all proto_list's not yet assigned class links */ -static struct objc_list* unclaimed_proto_list = 0; +static struct objc_list* unclaimed_proto_list = 0; /* !T:MUTEX */ /* List of unresolved static instances. */ -static struct objc_list *uninitialized_statics; +static struct objc_list *uninitialized_statics = 0; /* !T:MUTEX */ + +/* Global runtime "write" mutex. */ +objc_mutex_t __objc_runtime_mutex = 0; + +/* Number of threads that are alive. */ +int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ /* Check compiler vs runtime version */ static void init_check_module_version (Module_t); @@ -52,23 +59,335 @@ static void __objc_class_add_protocols (Class, struct objc_protocol_list*); or a category is loaded into the runtime. This may e.g. help a dynamic loader determine the classes that have been loaded when an object file is dynamically linked in */ -void (*_objc_load_callback)(Class class, Category* category) = 0; +void (*_objc_load_callback)(Class class, Category* category); /* !T:SAFE */ /* Is all categories/classes resolved? */ -BOOL __objc_dangling_categories = NO; +BOOL __objc_dangling_categories = NO; /* !T:UNUSED */ extern SEL __sel_register_typed_name (const char *name, const char *types, - struct objc_selector *orig); + struct objc_selector *orig, BOOL is_const); + +/* Sends +load to all classes and categories in certain situations. */ +static void objc_send_load (void); + +/* Inserts all the classes defined in module in a tree of classes that + resembles the class hierarchy. This tree is traversed in preorder and the + classes in its nodes receive the +load message if these methods were not + executed before. The algorithm ensures that when the +load method of a class + is executed all the superclasses have been already received the +load + message. */ +static void __objc_create_classes_tree (Module_t module); + +static void __objc_call_callback (Module_t module); + +/* A special version that works only before the classes are completely + installed in the runtime. */ +static BOOL class_is_subclass_of_class (Class class, Class superclass); + +typedef struct objc_class_tree { + Class class; + struct objc_list *subclasses; /* `head' is pointer to an objc_class_tree */ +} objc_class_tree; + +/* This is a linked list of objc_class_tree trees. The head of these trees + are root classes (their super class is Nil). These different trees + represent different class hierarchies. */ +static struct objc_list *__objc_class_tree_list = NULL; + +/* Keeps the +load methods who have been already executed. This hash should + not be destroyed during the execution of the program. */ +static cache_ptr __objc_load_methods = NULL; + +/* Creates a tree of classes whose topmost class is directly inherited from + `upper' and the bottom class in this tree is `bottom_class'. The classes + in this tree are super classes of `bottom_class'. `subclasses' member + of each tree node point to the next subclass tree node. */ +static objc_class_tree * +create_tree_of_subclasses_inherited_from (Class bottom_class, Class upper) +{ + Class superclass = bottom_class->super_class ? + objc_lookup_class ((char*)bottom_class->super_class) + : Nil; + + objc_class_tree *tree, *prev; + + DEBUG_PRINTF ("create_tree_of_subclasses_inherited_from:"); + DEBUG_PRINTF ("bottom_class = %s, upper = %s\n", + (bottom_class ? bottom_class->name : NULL), + (upper ? upper->name : NULL)); + + tree = prev = objc_calloc (1, sizeof (objc_class_tree)); + prev->class = bottom_class; + + while (superclass != upper) + { + tree = objc_calloc (1, sizeof (objc_class_tree)); + tree->class = superclass; + tree->subclasses = list_cons (prev, tree->subclasses); + superclass = (superclass->super_class ? + objc_lookup_class ((char*)superclass->super_class) + : Nil); + prev = tree; + } + + return tree; +} + +/* Insert the `class' into the proper place in the `tree' class hierarchy. This + function returns a new tree if the class has been successfully inserted into + the tree or NULL if the class is not part of the classes hierarchy described + by `tree'. This function is private to objc_tree_insert_class(), you should + not call it directly. */ +static objc_class_tree * +__objc_tree_insert_class (objc_class_tree *tree, Class class) +{ + DEBUG_PRINTF ("__objc_tree_insert_class: tree = %x, class = %s\n", + tree, class->name); + + if (tree == NULL) + return create_tree_of_subclasses_inherited_from (class, NULL); + else if (class == tree->class) + { + /* `class' has been already inserted */ + DEBUG_PRINTF ("1. class %s was previously inserted\n", class->name); + return tree; + } + else if ((class->super_class ? + objc_lookup_class ((char*)class->super_class) + : Nil) + == tree->class) + { + /* If class is a direct subclass of tree->class then add class to the + list of subclasses. First check to see if it wasn't already + inserted. */ + struct objc_list *list = tree->subclasses; + objc_class_tree *node; + + while (list) + { + /* Class has been already inserted; do nothing just return + the tree. */ + if (((objc_class_tree*)list->head)->class == class) + { + DEBUG_PRINTF ("2. class %s was previously inserted\n", + class->name); + return tree; + } + list = list->tail; + } + + /* Create a new node class and insert it into the list of subclasses */ + node = objc_calloc (1, sizeof (objc_class_tree)); + node->class = class; + tree->subclasses = list_cons (node, tree->subclasses); + DEBUG_PRINTF ("3. class %s inserted\n", class->name); + return tree; + } + else + { + /* The class is not a direct subclass of tree->class. Search for class's + superclasses in the list of subclasses. */ + struct objc_list *subclasses = tree->subclasses; + + /* Precondition: the class must be a subclass of tree->class; otherwise + return NULL to indicate our caller that it must take the next tree. */ + if (!class_is_subclass_of_class (class, tree->class)) + return NULL; + + for (; subclasses != NULL; subclasses = subclasses->tail) + { + Class aClass = ((objc_class_tree*)(subclasses->head))->class; + + if (class_is_subclass_of_class (class, aClass)) + { + /* If we found one of class's superclasses we insert the class + into its subtree and return the original tree since nothing + has been changed. */ + subclasses->head + = __objc_tree_insert_class (subclasses->head, class); + DEBUG_PRINTF ("4. class %s inserted\n", class->name); + return tree; + } + } + + /* We haven't found a subclass of `class' in the `subclasses' list. + Create a new tree of classes whose topmost class is a direct subclass + of tree->class. */ + { + objc_class_tree *new_tree + = create_tree_of_subclasses_inherited_from (class, tree->class); + tree->subclasses = list_cons (new_tree, tree->subclasses); + DEBUG_PRINTF ("5. class %s inserted\n", class->name); + return tree; + } + } +} + +/* This function inserts `class' in the right tree hierarchy classes. */ +static void +objc_tree_insert_class (Class class) +{ + struct objc_list *list_node; + objc_class_tree *tree; + + list_node = __objc_class_tree_list; + while (list_node) + { + tree = __objc_tree_insert_class (list_node->head, class); + if (tree) + { + list_node->head = tree; + break; + } + else + list_node = list_node->tail; + } + + /* If the list was finished but the class hasn't been inserted, insert it + here. */ + if (!list_node) + { + __objc_class_tree_list = list_cons (NULL, __objc_class_tree_list); + __objc_class_tree_list->head = __objc_tree_insert_class (NULL, class); + } +} + +/* Traverse tree in preorder. Used to send +load. */ +static void +objc_preorder_traverse (objc_class_tree *tree, + int level, + void (*function)(objc_class_tree*, int)) +{ + struct objc_list *node; + + (*function) (tree, level); + for (node = tree->subclasses; node; node = node->tail) + objc_preorder_traverse (node->head, level + 1, function); +} + +/* Traverse tree in postorder. Used to destroy a tree. */ +static void +objc_postorder_traverse (objc_class_tree *tree, + int level, + void (*function)(objc_class_tree*, int)) +{ + struct objc_list *node; + + for (node = tree->subclasses; node; node = node->tail) + objc_postorder_traverse (node->head, level + 1, function); + (*function) (tree, level); +} + +/* Used to print a tree class hierarchy. */ +static void +__objc_tree_print (objc_class_tree *tree, int level) +{ + int i; + + for (i = 0; i < level; i++) + printf (" "); + printf ("%s\n", tree->class->name); +} + +/* Walks on a linked list of methods in the reverse order and executes all + the methods corresponding to `op' selector. Walking in the reverse order + assures the +load of class is executed first and then +load of categories + because of the way in which categories are added to the class methods. */ +static void +__objc_send_message_in_list (MethodList_t method_list, Class class, SEL op) +{ + int i; + + if (!method_list) + return; + + /* First execute the `op' message in the following method lists */ + __objc_send_message_in_list (method_list->method_next, class, op); + + /* Search the method list. */ + for (i = 0; i < method_list->method_count; i++) + { + Method_t mth = &method_list->method_list[i]; + + if (mth->method_name && sel_eq (mth->method_name, op) + && !hash_is_key_in_hash (__objc_load_methods, mth->method_name)) + { + /* The method was found and wasn't previously executed. */ + (*mth->method_imp) ((id)class, mth->method_name); + + /* Add this method into the +load hash table */ + hash_add (&__objc_load_methods, mth->method_imp, mth->method_imp); + + DEBUG_PRINTF ("sending +load in class: %s\n", class->name); + + break; + } + } +} + +static void +__objc_send_load (objc_class_tree *tree, int level) +{ + static SEL load_sel = 0; + Class class = tree->class; + MethodList_t method_list = class->class_pointer->methods; + + if (!load_sel) + load_sel = sel_register_name ("load"); + + __objc_send_message_in_list (method_list, class, load_sel); +} + +static void +__objc_destroy_class_tree_node (objc_class_tree *tree, int level) +{ + objc_free (tree); +} + +/* This is used to check if the relationship between two classes before the + runtime completely installs the classes. */ +static BOOL +class_is_subclass_of_class (Class class, Class superclass) +{ + for (; class != Nil;) + { + if (class == superclass) + return YES; + class = (class->super_class ? + objc_lookup_class ((char*)class->super_class) + : Nil); + } + + return NO; +} + +/* This list contains all the classes in the runtime system for whom their + superclasses are not yet know to the runtime. */ +static struct objc_list* unresolved_classes = 0; + +/* Static function used to reference the Object and NXConstantString classes. + */ +static void +__objc_force_linking (void) +{ + extern void __objc_linking (void); + __objc_linking (); + + /* Call the function to avoid compiler warning */ + __objc_force_linking (); +} /* Run through the statics list, removing modules as soon as all its statics have been initialized. */ static void -objc_init_statics () +objc_init_statics (void) { struct objc_list **cell = &uninitialized_statics; struct objc_static_instances **statics_in_module; + objc_mutex_lock(__objc_runtime_mutex); + while (*cell) { int module_initialized = 1; @@ -109,11 +428,13 @@ objc_init_statics () /* Remove this module from the uninitialized list. */ struct objc_list *this = *cell; *cell = this->tail; - free (this); + objc_free(this); } else cell = &(*cell)->tail; } + + objc_mutex_unlock(__objc_runtime_mutex); } /* objc_init_statics */ /* This function is called by constructor functions generated for each @@ -133,6 +454,10 @@ __objc_exec_class (Module_t module) /* The symbol table (defined in objc-api.h) generated by gcc */ Symtab_t symtab = module->symtab; + /* The statics in this module */ + struct objc_static_instances **statics + = symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt]; + /* Entry used to traverse hash lists */ struct objc_list** cell; @@ -150,13 +475,22 @@ __objc_exec_class (Module_t module) /* On the first call of this routine, initialize some data structures. */ if (!previous_constructors) { + /* Initialize thread-safe system */ + __objc_init_thread_system(); + __objc_runtime_threads_alive = 1; + __objc_runtime_mutex = objc_mutex_allocate(); + __objc_init_selector_tables(); __objc_init_class_tables(); __objc_init_dispatch_tables(); + __objc_class_tree_list = list_cons (NULL, __objc_class_tree_list); + __objc_load_methods + = hash_new (128, (hash_func_type)hash_ptr, compare_ptrs); previous_constructors = 1; } /* Save the module pointer for later processing. (not currently used) */ + objc_mutex_lock(__objc_runtime_mutex); __objc_module_list = list_cons(module, __objc_module_list); /* Replace referenced selectors from names to SEL's. */ @@ -167,8 +501,11 @@ __objc_exec_class (Module_t module) const char *name, *type; name = (char*)selectors[i].sel_id; type = (char*)selectors[i].sel_types; + /* Constructors are constant static data so we can safely store + pointers to them in the runtime structures. is_const == YES */ __sel_register_typed_name (name, type, - (struct objc_selector*)&(selectors[i])); + (struct objc_selector*)&(selectors[i]), + YES); } } @@ -177,12 +514,17 @@ __objc_exec_class (Module_t module) for (i = 0; i < symtab->cls_def_cnt; ++i) { Class class = (Class) symtab->defs[i]; + const char* superclass = (char*)class->super_class; /* Make sure we have what we think. */ assert (CLS_ISCLASS(class)); assert (CLS_ISMETA(class->class_pointer)); DEBUG_PRINTF ("phase 1, processing class: %s\n", class->name); + /* Initialize the subclass list to be NULL. + In some cases it isn't and this crashes the program. */ + class->subclass_list = NULL; + /* Store the class in the class table and assign class numbers. */ __objc_add_class_to_hash (class); @@ -194,11 +536,17 @@ __objc_exec_class (Module_t module) __objc_install_premature_dtable(class); __objc_install_premature_dtable(class->class_pointer); + /* Register the instance methods as class methods, this is + only done for root classes. */ + __objc_register_instance_methods_to_class(class); + if (class->protocols) __objc_init_protocols (class->protocols); - if (_objc_load_callback) - _objc_load_callback(class, 0); + /* Check to see if the superclass is known in this point. If it's not + add the class to the unresolved_classes list. */ + if (superclass && !objc_lookup_class (superclass)) + unresolved_classes = list_cons (class, unresolved_classes); } /* Process category information from the module. */ @@ -230,8 +578,9 @@ __objc_exec_class (Module_t module) __objc_class_add_protocols (class, category->protocols); } - if (_objc_load_callback) - _objc_load_callback(class, category); + /* Register the instance methods as class methods, this is + only done for root classes. */ + __objc_register_instance_methods_to_class(class); } else { @@ -241,8 +590,8 @@ __objc_exec_class (Module_t module) } } - if (module->statics) - uninitialized_statics = list_cons (module->statics, uninitialized_statics); + if (statics) + uninitialized_statics = list_cons (statics, uninitialized_statics); if (uninitialized_statics) objc_init_statics (); @@ -268,15 +617,16 @@ __objc_exec_class (Module_t module) if (category->class_methods) class_add_method_list ((Class) class->class_pointer, category->class_methods); - + if (category->protocols) { __objc_init_protocols (category->protocols); __objc_class_add_protocols (class, category->protocols); } - - if (_objc_load_callback) - _objc_load_callback(class, category); + + /* Register the instance methods as class methods, this is + only done for root classes. */ + __objc_register_instance_methods_to_class(class); } } @@ -287,6 +637,119 @@ __objc_exec_class (Module_t module) unclaimed_proto_list = 0; } + objc_send_load (); + + objc_mutex_unlock(__objc_runtime_mutex); +} + +static void objc_send_load (void) +{ + if (!__objc_module_list) + return; + + /* Try to find out if all the classes loaded so far also have their + superclasses known to the runtime. We suppose that the objects that are + allocated in the +load method are in general of a class declared in the + same module. */ + if (unresolved_classes) + { + Class class = unresolved_classes->head; + + while (objc_lookup_class ((char*)class->super_class)) + { + list_remove_head (&unresolved_classes); + if (unresolved_classes) + class = unresolved_classes->head; + else + break; + } + + /* + * If we still have classes for whom we don't have yet their super + * classes known to the runtime we don't send the +load messages. + */ + if (unresolved_classes) + return; + } + + /* Special check to allow creating and sending messages to constant strings + in +load methods. If these classes are not yet known, even if all the + other classes are known, delay sending of +load. */ + if (!objc_lookup_class ("NXConstantString") || + !objc_lookup_class ("Object")) + return; + + /* Iterate over all modules in the __objc_module_list and call on them the + __objc_create_classes_tree function. This function creates a tree of + classes that resembles the class hierarchy. */ + list_mapcar (__objc_module_list, (void(*)(void*))__objc_create_classes_tree); + + while (__objc_class_tree_list) + { +#ifdef DEBUG + objc_preorder_traverse (__objc_class_tree_list->head, + 0, __objc_tree_print); +#endif + objc_preorder_traverse (__objc_class_tree_list->head, + 0, __objc_send_load); + objc_postorder_traverse (__objc_class_tree_list->head, + 0, __objc_destroy_class_tree_node); + list_remove_head (&__objc_class_tree_list); + } + + list_mapcar (__objc_module_list, (void(*)(void*))__objc_call_callback); + list_free (__objc_module_list); + __objc_module_list = NULL; +} + +static void +__objc_create_classes_tree (Module_t module) +{ + /* The runtime mutex is locked in this point */ + + Symtab_t symtab = module->symtab; + int i; + + /* Iterate thru classes defined in this module and insert them in the classes + tree hierarchy. */ + for (i = 0; i < symtab->cls_def_cnt; i++) + { + Class class = (Class) symtab->defs[i]; + + objc_tree_insert_class (class); + } +} + +static void +__objc_call_callback (Module_t module) +{ + /* The runtime mutex is locked in this point */ + + Symtab_t symtab = module->symtab; + int i; + + /* Iterate thru classes defined in this module and call the callback for + each one. */ + for (i = 0; i < symtab->cls_def_cnt; i++) + { + Class class = (Class) symtab->defs[i]; + + /* Call the _objc_load_callback for this class. */ + if (_objc_load_callback) + _objc_load_callback(class, 0); + } + + /* Call the _objc_load_callback for categories. Don't register the instance + methods as class methods for categories to root classes since they were + already added in the class. */ + for (i = 0; i < symtab->cat_def_cnt; i++) + { + Category_t category = symtab->defs[i + symtab->cls_def_cnt]; + Class class = objc_lookup_class (category->class_name); + + if (_objc_load_callback) + _objc_load_callback(class, category); + } } /* Sanity check the version of gcc used to compile `module'*/ @@ -294,15 +757,17 @@ static void init_check_module_version(Module_t module) { if ((module->version != OBJC_VERSION) || (module->size != sizeof (Module))) { - fprintf (stderr, "Module %s version %d doesn't match runtime %d\n", - module->name, (int)module->version, OBJC_VERSION); + int code; + if(module->version > OBJC_VERSION) - fprintf (stderr, "Runtime (libobjc.a) is out of date\n"); + code = OBJC_ERR_OBJC_VERSION; else if (module->version < OBJC_VERSION) - fprintf (stderr, "Compiler (gcc) is out of date\n"); + code = OBJC_ERR_GCC_VERSION; else - fprintf (stderr, "Objective C internal error -- bad Module size\n"); - abort (); + code = OBJC_ERR_MODULE_SIZE; + + objc_error(nil, code, "Module %s version %d doesn't match runtime %d\n", + module->name, (int)module->version, OBJC_VERSION); } } @@ -315,12 +780,15 @@ __objc_init_protocols (struct objc_protocol_list* protos) if (! protos) return; + objc_mutex_lock(__objc_runtime_mutex); + if (!proto_class) proto_class = objc_lookup_class("Protocol"); if (!proto_class) { unclaimed_proto_list = list_cons (protos, unclaimed_proto_list); + objc_mutex_unlock(__objc_runtime_mutex); return; } @@ -341,13 +809,14 @@ __objc_init_protocols (struct objc_protocol_list* protos) } else if (protos->list[i]->class_pointer != proto_class) { - fprintf (stderr, - "Version %d doesn't match runtime protocol version %d\n", - (int)((char*)protos->list[i]->class_pointer-(char*)0), - PROTOCOL_VERSION); - abort (); + objc_error(nil, OBJC_ERR_PROTOCOL_VERSION, + "Version %d doesn't match runtime protocol version %d\n", + (int)((char*)protos->list[i]->class_pointer-(char*)0), + PROTOCOL_VERSION); } } + + objc_mutex_unlock(__objc_runtime_mutex); } static void __objc_class_add_protocols (Class class, diff --git a/gnu/usr.bin/gcc/objc/libobjc.def b/gnu/usr.bin/gcc/objc/libobjc.def new file mode 100644 index 00000000000..a4a6049e816 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/libobjc.def @@ -0,0 +1,161 @@ +; GNU Objective C Runtime DLL Export Definitions +; Copyright (C) 1997 Free Software Foundation, Inc. +; Contributed by Scott Christley <scottc@net-community.com> +; +; This file is part of GNU CC. +; +; GNU CC is free software; you can redistribute it and/or modify it under the +; terms of the GNU General Public License as published by the Free Software +; Foundation; either version 2, or (at your option) any later version. +; +; GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +; details. +; +; You should have received a copy of the GNU General Public License along with +; GNU CC; see the file COPYING. If not, write to the Free Software +; Foundation, 59 Temple Place - Suite 330, +; Boston, MA 02111-1307, USA. + +LIBRARY libobjc +EXPORTS +search_for_method_in_list +objc_get_uninstalled_dtable +hash_is_key_in_hash +objc_verror +_objc_load_callback +objc_malloc +objc_atomic_malloc +objc_valloc +objc_realloc +objc_calloc +objc_free +__objc_init_thread_system +objc_mutex_allocate +objc_mutex_deallocate +objc_mutex_lock +objc_mutex_trylock +objc_mutex_unlock +objc_thread_detach +objc_thread_exit +objc_thread_get_data +objc_thread_get_priority +objc_thread_id +objc_thread_set_data +objc_thread_set_priority +objc_thread_yield +__objc_class_name_Object +__objc_class_name_Protocol +__objc_class_name_NXConstantString +objc_error +__objc_object_alloc +__objc_object_copy +__objc_object_dispose +class_create_instance +object_copy +object_dispose +__objc_init_selector_tables +__objc_register_selectors_from_class +__sel_register_typed_name +sel_get_any_typed_uid +sel_get_any_uid +sel_get_name +sel_get_type +sel_get_typed_uid +sel_get_uid +sel_is_mapped +sel_register_name +sel_register_typed_name +sel_types_match +method_get_first_argument +method_get_next_argument +method_get_nth_argument +method_get_number_of_arguments +method_get_sizeof_arguments +objc_aligned_size +objc_alignof_type +objc_get_type_qualifiers +objc_promoted_size +objc_sizeof_type +objc_skip_argspec +objc_skip_offset +objc_skip_type_qualifiers +objc_skip_typespec +__objc_read_nbyte_uint +__objc_read_nbyte_ulong +__objc_write_class +__objc_write_object +__objc_write_selector +objc_close_typed_stream +objc_end_of_typed_stream +objc_flush_typed_stream +objc_get_stream_class_version +objc_open_typed_stream +objc_open_typed_stream_for_file +objc_read_array +objc_read_char +objc_read_int +objc_read_long +objc_read_object +objc_read_selector +objc_read_short +objc_read_string +objc_read_type +objc_read_types +objc_read_unsigned_char +objc_read_unsigned_int +objc_read_unsigned_long +objc_read_unsigned_short +objc_write_array +objc_write_char +objc_write_int +objc_write_long +objc_write_object +objc_write_object_reference +objc_write_root_object +objc_write_selector +objc_write_short +objc_write_string +objc_write_string_atomic +objc_write_type +objc_write_types +objc_write_unsigned_char +objc_write_unsigned_int +objc_write_unsigned_long +objc_write_unsigned_short +__objc_exec_class +__objc_init_dispatch_tables +__objc_install_premature_dtable +__objc_print_dtable_stats +__objc_responds_to +__objc_update_dispatch_table_for_class +class_add_method_list +class_get_class_method +class_get_instance_method +get_imp +nil_method +objc_msg_lookup +objc_msg_lookup_super +objc_msg_sendv +__objc_add_class_to_hash +__objc_init_class_tables +__objc_resolve_class_links +class_pose_as +objc_get_class +objc_get_meta_class +objc_lookup_class +objc_next_class +sarray_at_put +sarray_at_put_safe +sarray_free +sarray_lazy_copy +sarray_new +sarray_realloc +sarray_remove_garbage +hash_add +hash_delete +hash_new +hash_next +hash_remove +hash_value_for_key diff --git a/gnu/usr.bin/gcc/objc/libobjc_entry.c b/gnu/usr.bin/gcc/objc/libobjc_entry.c new file mode 100644 index 00000000000..2d584ab3c1e --- /dev/null +++ b/gnu/usr.bin/gcc/objc/libobjc_entry.c @@ -0,0 +1,55 @@ +/* GNU Objective C Runtime DLL Entry + Copyright (C) 1997 Free Software Foundation, Inc. + Contributed by Scott Christley <scottc@net-community.com> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <windows.h> + +/* + DLL entry function for Objective-C Runtime library + This function gets called everytime a process/thread attaches to DLL + */ +WINBOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, + LPVOID lpReserved) +{ + switch(ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + break; + case DLL_PROCESS_DETACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} + +/* + This section terminates the list of imports under GCC. If you do not + include this then you will have problems when linking with DLLs. + */ +asm (".section .idata$3\n" ".long 0,0,0,0,0,0,0,0"); diff --git a/gnu/usr.bin/gcc/objc/linking.m b/gnu/usr.bin/gcc/objc/linking.m new file mode 100644 index 00000000000..8ecca028110 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/linking.m @@ -0,0 +1,40 @@ +/* Force linking of classes required by Objective C runtime. + Copyright (C) 1997 Free Software Foundation, Inc. + Contributed by Ovidiu Predescu (ovidiu@net-community.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <objc/Object.h> +#include <objc/NXConstStr.h> + +/* Generate references to Object and NXConstanstString classes since they are + needed by the runtime system to run correctly. */ + + +void __objc_linking (void) +{ + [Object name]; + [NXConstantString name]; +} + diff --git a/gnu/usr.bin/gcc/objc/makefile.dos b/gnu/usr.bin/gcc/objc/makefile.dos index 5321733a234..3e1b1871e40 100644 --- a/gnu/usr.bin/gcc/objc/makefile.dos +++ b/gnu/usr.bin/gcc/objc/makefile.dos @@ -1,5 +1,5 @@ # GNU Objective C Runtime Makefile for compiling with djgpp -# Copyright (C) 1993, 1994 Free Software Foundation, Inc. +# Copyright (C) 1993, 1994, 1996 Free Software Foundation, Inc. # # This file is part of GNU CC. # @@ -37,17 +37,17 @@ SUBDIR_INCLUDES = -I. -I.. -I../config -c $(GCC_CFLAGS) $(SUBDIR_INCLUDES) $< OBJC_O = hash.o sarray.o class.o sendmsg.o init.o archive.o \ - selector.o objects.o misc.o object.o protocol.o encoding.o + selector.o objects.o misc.o object.o protocol.o encoding.o thread.o libobjc.a: $(OBJC_O) -rm -f libobjc.a ar rc libobjc.a $(OBJC_O) ranlib libobjc.a -OBJC_H = hash.h list.h sarray.h objc.h \ +OBJC_H = hash.h objc-list.h sarray.h objc.h \ objc-api.h \ object.h protocol.h mutex.h \ - typedstream.h + typedstream.h thread.h mostlyclean: -rm -f *.o libobjc.a xforward fflags diff --git a/gnu/usr.bin/gcc/objc/misc.c b/gnu/usr.bin/gcc/objc/misc.c index 033018ed484..01f9d3bbafa 100644 --- a/gnu/usr.bin/gcc/objc/misc.c +++ b/gnu/usr.bin/gcc/objc/misc.c @@ -1,7 +1,6 @@ /* GNU Objective C Runtime Miscellaneous - Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc. - -Author: Kresten Krab Thorup + Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -26,55 +25,128 @@ Boston, MA 02111-1307, USA. */ however invalidate any other reasons why the executable file might be covered by the GNU General Public License. */ -#ifdef __alpha__ +#define __USE_FIXED_PROTOTYPES__ #include <stdlib.h> -extern int write (int, const char*, int); -extern size_t strlen (const char*); -#endif - #include "runtime.h" -void objc_error(id object, const char* fmt, va_list); +/* +** Error handler function +** NULL so that default is to just print to stderr +*/ +static objc_error_handler _objc_error_handler = NULL; + +/* Trigger an objc error */ +void +objc_error(id object, int code, const char* fmt, ...) +{ + va_list ap; -void (*_objc_error)(id, const char*, va_list) = objc_error; + va_start(ap, fmt); + objc_verror(object, code, fmt, ap); + va_end(ap); +} +/* Trigger an objc error */ void -objc_error(id object, const char* fmt, va_list ap) +objc_verror(id object, int code, const char* fmt, va_list ap) +{ + BOOL result = NO; + + /* Call the error handler if its there + Otherwise print to stderr */ + if (_objc_error_handler) + result = (*_objc_error_handler)(object, code, fmt, ap); + else + vfprintf (stderr, fmt, ap); + + /* Continue if the error handler says its ok + Otherwise abort the program */ + if (result) + return; + else + abort(); +} + +/* Set the error handler */ +objc_error_handler +objc_set_error_handler(objc_error_handler func) +{ + objc_error_handler temp = _objc_error_handler; + _objc_error_handler = func; + return temp; +} + +/* +** Standard functions for memory allocation and disposal. +** Users should use these functions in their ObjC programs so +** that they work properly with garbage collectors as well as +** can take advantage of the exception/error handling available. +*/ + +void * +objc_malloc(size_t size) { - vfprintf (stderr, fmt, ap); - abort (); + void* res = (void*) (*_objc_malloc)(size); + if(!res) + objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); + return res; } -volatile void -objc_fatal(const char* msg) +void * +objc_atomic_malloc(size_t size) { - write(2, msg, (int)strlen((const char*)msg)); - abort(); + void* res = (void*) (*_objc_atomic_malloc)(size); + if(!res) + objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); + return res; } -void* -__objc_xmalloc(size_t size) +void * +objc_valloc(size_t size) { - void* res = (void*) malloc(size); + void* res = (void*) (*_objc_valloc)(size); if(!res) - objc_fatal("Virtual memory exhausted\n"); + objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); return res; } -void* -__objc_xrealloc(void* mem, size_t size) +void * +objc_realloc(void *mem, size_t size) { - void* res = (void*) realloc(mem, size); + void* res = (void*) (*_objc_realloc)(mem, size); if(!res) - objc_fatal("Virtual memory exhausted\n"); + objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); return res; } -void* -__objc_xcalloc(size_t nelem, size_t size) +void * +objc_calloc(size_t nelem, size_t size) { - void* res = (void*)calloc(nelem, size); + void* res = (void*) (*_objc_calloc)(nelem, size); if(!res) - objc_fatal("Virtual memory exhausted\n"); + objc_error(nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); return res; } + +void +objc_free(void *mem) +{ + (*_objc_free)(mem); +} + +/* +** Hook functions for memory allocation and disposal. +** This makes it easy to substitute garbage collection systems +** such as Boehm's GC by assigning these function pointers +** to the GC's allocation routines. By default these point +** to the ANSI standard malloc, realloc, free, etc. +** +** Users should call the normal objc routines above for +** memory allocation and disposal within their programs. +*/ +void *(*_objc_malloc)(size_t) = malloc; +void *(*_objc_atomic_malloc)(size_t) = malloc; +void *(*_objc_valloc)(size_t) = malloc; +void *(*_objc_realloc)(void *, size_t) = realloc; +void *(*_objc_calloc)(size_t, size_t) = calloc; +void (*_objc_free)(void *) = free; diff --git a/gnu/usr.bin/gcc/objc/nil_method.c b/gnu/usr.bin/gcc/objc/nil_method.c new file mode 100644 index 00000000000..1b6212826bd --- /dev/null +++ b/gnu/usr.bin/gcc/objc/nil_method.c @@ -0,0 +1,40 @@ +/* GNU Objective C Runtime nil receiver function + Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +/* This is the nil method, the function that is called when the receiver + of a method is nil */ + +#include "runtime.h" + +id +nil_method(id receiver, SEL op, ...) +{ + return receiver; +} + + + + diff --git a/gnu/usr.bin/gcc/objc/objc-act.c b/gnu/usr.bin/gcc/objc/objc-act.c new file mode 100644 index 00000000000..6b0de8d3556 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/objc-act.c @@ -0,0 +1,8410 @@ +/* Implement classes and message passing for Objective C. + Copyright (C) 1992, 1993, 1994, 1995, 1997 Free Software Foundation, Inc. + Contributed by Steve Naroff. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Purpose: This module implements the Objective-C 4.0 language. + + compatibility issues (with the Stepstone translator): + + - does not recognize the following 3.3 constructs. + @requires, @classes, @messages, = (...) + - methods with variable arguments must conform to ANSI standard. + - tagged structure definitions that appear in BOTH the interface + and implementation are not allowed. + - public/private: all instance variables are public within the + context of the implementation...I consider this to be a bug in + the translator. + - statically allocated objects are not supported. the user will + receive an error if this service is requested. + + code generation `options': + + - OBJC_INT_SELECTORS */ + +#include "config.h" +#include <stdio.h> +#include "tree.h" +#include "c-tree.h" +#include "c-lex.h" +#include "flags.h" +#include "objc-act.h" +#include "input.h" +#include "except.h" +#include "function.h" +#include <string.h> +#include "output.h" + +/* This is the default way of generating a method name. */ +/* I am not sure it is really correct. + Perhaps there's a danger that it will make name conflicts + if method names contain underscores. -- rms. */ +#ifndef OBJC_GEN_METHOD_LABEL +#define OBJC_GEN_METHOD_LABEL(BUF, IS_INST, CLASS_NAME, CAT_NAME, SEL_NAME, NUM) \ + do { \ + char *temp; \ + sprintf ((BUF), "_%s_%s_%s_%s", \ + ((IS_INST) ? "i" : "c"), \ + (CLASS_NAME), \ + ((CAT_NAME)? (CAT_NAME) : ""), \ + (SEL_NAME)); \ + for (temp = (BUF); *temp; temp++) \ + if (*temp == ':') *temp = '_'; \ + } while (0) +#endif + +/* These need specifying. */ +#ifndef OBJC_FORWARDING_STACK_OFFSET +#define OBJC_FORWARDING_STACK_OFFSET 0 +#endif + +#ifndef OBJC_FORWARDING_MIN_OFFSET +#define OBJC_FORWARDING_MIN_OFFSET 0 +#endif + +/* Define the special tree codes that we use. */ + +/* Table indexed by tree code giving a string containing a character + classifying the tree code. Possibilities are + t, d, s, c, r, <, 1 and 2. See objc-tree.def for details. */ + +#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) TYPE, + +char *objc_tree_code_type[] = { + "x", +#include "objc-tree.def" +}; +#undef DEFTREECODE + +/* Table indexed by tree code giving number of expression + operands beyond the fixed part of the node structure. + Not used for types or decls. */ + +#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) LENGTH, + +int objc_tree_code_length[] = { + 0, +#include "objc-tree.def" +}; +#undef DEFTREECODE + +/* Names of tree components. + Used for printing out the tree and error messages. */ +#define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME, + +char *objc_tree_code_name[] = { + "@@dummy", +#include "objc-tree.def" +}; +#undef DEFTREECODE + +/* Set up for use of obstacks. */ + +#include "obstack.h" + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +/* This obstack is used to accumulate the encoding of a data type. */ +static struct obstack util_obstack; +/* This points to the beginning of obstack contents, + so we can free the whole contents. */ +char *util_firstobj; + +/* List of classes with list of their static instances. */ +static tree objc_static_instances = NULL_TREE; + +/* The declaration of the array administrating the static instances. */ +static tree static_instances_decl = NULL_TREE; + +/* for encode_method_def */ +#include "rtl.h" +#include "c-parse.h" + +#define OBJC_VERSION (flag_next_runtime ? 5 : 8) +#define PROTOCOL_VERSION 2 + +#define OBJC_ENCODE_INLINE_DEFS 0 +#define OBJC_ENCODE_DONT_INLINE_DEFS 1 + +/*** Private Interface (procedures) ***/ + +/* Used by compile_file. */ + +static void init_objc PROTO((void)); +static void finish_objc PROTO((void)); + +/* Code generation. */ + +static void synth_module_prologue PROTO((void)); +static tree build_constructor PROTO((tree, tree)); +static char *build_module_descriptor PROTO((void)); +static tree init_module_descriptor PROTO((tree)); +static tree build_objc_method_call PROTO((int, tree, tree, + tree, tree, tree)); +static void generate_strings PROTO((void)); +static tree get_proto_encoding PROTO((tree)); +static void build_selector_translation_table PROTO((void)); +static tree build_ivar_chain PROTO((tree, int)); + +static tree objc_add_static_instance PROTO((tree, tree)); + +static tree build_ivar_template PROTO((void)); +static tree build_method_template PROTO((void)); +static tree build_private_template PROTO((tree)); +static void build_class_template PROTO((void)); +static void build_selector_template PROTO((void)); +static void build_category_template PROTO((void)); +static tree build_super_template PROTO((void)); +static tree build_category_initializer PROTO((tree, tree, tree, + tree, tree, tree)); +static tree build_protocol_initializer PROTO((tree, tree, tree, + tree, tree)); + +static void synth_forward_declarations PROTO((void)); +static void generate_ivar_lists PROTO((void)); +static void generate_dispatch_tables PROTO((void)); +static void generate_shared_structures PROTO((void)); +static tree generate_protocol_list PROTO((tree)); +static void generate_forward_declaration_to_string_table PROTO((void)); +static void build_protocol_reference PROTO((tree)); + +static tree init_selector PROTO((int)); +static tree build_keyword_selector PROTO((tree)); +static tree synth_id_with_class_suffix PROTO((char *, tree)); + +/* From expr.c */ +extern int apply_args_register_offset PROTO((int)); + +static void generate_static_references PROTO((void)); +static int check_methods_accessible PROTO((tree, tree, + int)); +static void encode_aggregate_within PROTO((tree, int, int, + char, char)); + +/* We handle printing method names ourselves for ObjC */ +extern char *(*decl_printable_name) (); + +/* Misc. bookkeeping */ + +typedef struct hashed_entry *hash; +typedef struct hashed_attribute *attr; + +struct hashed_attribute +{ + attr next; + tree value; +}; +struct hashed_entry +{ + attr list; + hash next; + tree key; +}; + +static void hash_init PROTO((void)); +static void hash_enter PROTO((hash *, tree)); +static hash hash_lookup PROTO((hash *, tree)); +static void hash_add_attr PROTO((hash, tree)); +static tree lookup_method PROTO((tree, tree)); +static tree lookup_instance_method_static PROTO((tree, tree)); +static tree lookup_class_method_static PROTO((tree, tree)); +static tree add_class PROTO((tree)); +static void add_category PROTO((tree, tree)); + +enum string_section +{ + class_names, /* class, category, protocol, module names */ + meth_var_names, /* method and variable names */ + meth_var_types /* method and variable type descriptors */ +}; + +static tree add_objc_string PROTO((tree, + enum string_section)); +static tree get_objc_string_decl PROTO((tree, + enum string_section)); +static tree build_objc_string_decl PROTO((tree, + enum string_section)); +static tree build_selector_reference_decl PROTO((tree)); + +/* Protocol additions. */ + +static tree add_protocol PROTO((tree)); +static tree lookup_protocol PROTO((tree)); +static tree lookup_and_install_protocols PROTO((tree)); + +/* Type encoding. */ + +static void encode_type_qualifiers PROTO((tree)); +static void encode_pointer PROTO((tree, int, int)); +static void encode_array PROTO((tree, int, int)); +static void encode_aggregate PROTO((tree, int, int)); +static void encode_bitfield PROTO((int, int)); +static void encode_type PROTO((tree, int, int)); +static void encode_field_decl PROTO((tree, int, int)); + +static void really_start_method PROTO((tree, tree)); +static int comp_method_with_proto PROTO((tree, tree)); +static int comp_proto_with_proto PROTO((tree, tree)); +static tree get_arg_type_list PROTO((tree, int, int)); +static tree expr_last PROTO((tree)); + +/* Utilities for debugging and error diagnostics. */ + +static void warn_with_method PROTO((char *, int, tree)); +static void error_with_ivar PROTO((char *, tree, tree)); +static char *gen_method_decl PROTO((tree, char *)); +static char *gen_declaration PROTO((tree, char *)); +static char *gen_declarator PROTO((tree, char *, char *)); +static int is_complex_decl PROTO((tree)); +static void adorn_decl PROTO((tree, char *)); +static void dump_interface PROTO((FILE *, tree)); + +/* Everything else. */ + +static void objc_fatal PROTO((void)); +static tree define_decl PROTO((tree, tree)); +static tree lookup_method_in_protocol_list PROTO((tree, tree, int)); +static tree lookup_protocol_in_reflist PROTO((tree, tree)); +static tree create_builtin_decl PROTO((enum tree_code, + tree, char *)); +static tree my_build_string PROTO((int, char *)); +static void build_objc_symtab_template PROTO((void)); +static tree init_def_list PROTO((tree)); +static tree init_objc_symtab PROTO((tree)); +static void forward_declare_categories PROTO((void)); +static void generate_objc_symtab_decl PROTO((void)); +static tree build_selector PROTO((tree)); +static tree build_msg_pool_reference PROTO((int)); +static tree build_typed_selector_reference PROTO((tree, tree)); +static tree build_selector_reference PROTO((tree)); +static tree build_class_reference_decl PROTO((tree)); +static void add_class_reference PROTO((tree)); +static tree objc_copy_list PROTO((tree, tree *)); +static tree build_protocol_template PROTO((void)); +static tree build_descriptor_table_initializer PROTO((tree, tree)); +static tree build_method_prototype_list_template PROTO((tree, int)); +static tree build_method_prototype_template PROTO((void)); +static int forwarding_offset PROTO((tree)); +static tree encode_method_prototype PROTO((tree, tree)); +static tree generate_descriptor_table PROTO((tree, char *, int, tree, tree)); +static void generate_method_descriptors PROTO((tree)); +static tree build_tmp_function_decl PROTO((void)); +static void hack_method_prototype PROTO((tree, tree)); +static void generate_protocol_references PROTO((tree)); +static void generate_protocols PROTO((void)); +static void check_ivars PROTO((tree, tree)); +static tree build_ivar_list_template PROTO((tree, int)); +static tree build_method_list_template PROTO((tree, int)); +static tree build_ivar_list_initializer PROTO((tree, tree)); +static tree generate_ivars_list PROTO((tree, char *, + int, tree)); +static tree build_dispatch_table_initializer PROTO((tree, tree)); +static tree generate_dispatch_table PROTO((tree, char *, + int, tree)); +static tree build_shared_structure_initializer PROTO((tree, tree, tree, tree, + tree, int, tree, tree, + tree)); +static void generate_category PROTO((tree)); +static int is_objc_type_qualifier PROTO((tree)); +static tree adjust_type_for_id_default PROTO((tree)); +static tree check_duplicates PROTO((hash)); +static tree receiver_is_class_object PROTO((tree)); +static int check_methods PROTO((tree, tree, int)); +static int conforms_to_protocol PROTO((tree, tree)); +static void check_protocols PROTO((tree, char *, char *)); +static tree encode_method_def PROTO((tree)); +static void gen_declspecs PROTO((tree, char *, int)); +static void generate_classref_translation_entry PROTO((tree)); +static void handle_class_ref PROTO((tree)); + +/*** Private Interface (data) ***/ + +/* Reserved tag definitions. */ + +#define TYPE_ID "id" +#define TAG_OBJECT "objc_object" +#define TAG_CLASS "objc_class" +#define TAG_SUPER "objc_super" +#define TAG_SELECTOR "objc_selector" + +#define UTAG_CLASS "_objc_class" +#define UTAG_IVAR "_objc_ivar" +#define UTAG_IVAR_LIST "_objc_ivar_list" +#define UTAG_METHOD "_objc_method" +#define UTAG_METHOD_LIST "_objc_method_list" +#define UTAG_CATEGORY "_objc_category" +#define UTAG_MODULE "_objc_module" +#define UTAG_STATICS "_objc_statics" +#define UTAG_SYMTAB "_objc_symtab" +#define UTAG_SUPER "_objc_super" +#define UTAG_SELECTOR "_objc_selector" + +#define UTAG_PROTOCOL "_objc_protocol" +#define UTAG_PROTOCOL_LIST "_objc_protocol_list" +#define UTAG_METHOD_PROTOTYPE "_objc_method_prototype" +#define UTAG_METHOD_PROTOTYPE_LIST "_objc__method_prototype_list" + +#define STRING_OBJECT_CLASS_NAME "NXConstantString" +#define PROTOCOL_OBJECT_CLASS_NAME "Protocol" + +static char *TAG_GETCLASS; +static char *TAG_GETMETACLASS; +static char *TAG_MSGSEND; +static char *TAG_MSGSENDSUPER; +static char *TAG_EXECCLASS; + +/* Set by `continue_class' and checked by `is_public'. */ + +#define TREE_STATIC_TEMPLATE(record_type) (TREE_PUBLIC (record_type)) +#define TYPED_OBJECT(type) \ + (TREE_CODE (type) == RECORD_TYPE && TREE_STATIC_TEMPLATE (type)) + +/* Some commonly used instances of "identifier_node". */ + +static tree self_id, ucmd_id; +static tree unused_list; + +static tree self_decl, umsg_decl, umsg_super_decl; +static tree objc_get_class_decl, objc_get_meta_class_decl; + +static tree super_type, selector_type, id_type, objc_class_type; +static tree instance_type, protocol_type; + +/* Type checking macros. */ + +#define IS_ID(TYPE) \ + (TYPE_MAIN_VARIANT (TYPE) == TYPE_MAIN_VARIANT (id_type)) +#define IS_PROTOCOL_QUALIFIED_ID(TYPE) \ + (IS_ID (TYPE) && TYPE_PROTOCOL_LIST (TYPE)) +#define IS_SUPER(TYPE) \ + (super_type && TYPE_MAIN_VARIANT (TYPE) == TYPE_MAIN_VARIANT (super_type)) + +static tree class_chain = NULL_TREE; +static tree alias_chain = NULL_TREE; +static tree interface_chain = NULL_TREE; +static tree protocol_chain = NULL_TREE; + +/* Chains to manage selectors that are referenced and defined in the + module. */ + +static tree cls_ref_chain = NULL_TREE; /* Classes referenced. */ +static tree sel_ref_chain = NULL_TREE; /* Selectors referenced. */ + +/* Chains to manage uniquing of strings. */ + +static tree class_names_chain = NULL_TREE; +static tree meth_var_names_chain = NULL_TREE; +static tree meth_var_types_chain = NULL_TREE; + +/* Hash tables to manage the global pool of method prototypes. */ + +static hash *nst_method_hash_list = 0; +static hash *cls_method_hash_list = 0; + +/* Backend data declarations. */ + +static tree UOBJC_SYMBOLS_decl; +static tree UOBJC_INSTANCE_VARIABLES_decl, UOBJC_CLASS_VARIABLES_decl; +static tree UOBJC_INSTANCE_METHODS_decl, UOBJC_CLASS_METHODS_decl; +static tree UOBJC_CLASS_decl, UOBJC_METACLASS_decl; +static tree UOBJC_SELECTOR_TABLE_decl; +static tree UOBJC_MODULES_decl; +static tree UOBJC_STRINGS_decl; + +/* The following are used when compiling a class implementation. + implementation_template will normally be an interface, however if + none exists this will be equal to implementation_context...it is + set in start_class. */ + +static tree implementation_context = NULL_TREE; +static tree implementation_template = NULL_TREE; + +struct imp_entry +{ + struct imp_entry *next; + tree imp_context; + tree imp_template; + tree class_decl; /* _OBJC_CLASS_<my_name>; */ + tree meta_decl; /* _OBJC_METACLASS_<my_name>; */ +}; + +static void handle_impent PROTO((struct imp_entry *)); + +static struct imp_entry *imp_list = 0; +static int imp_count = 0; /* `@implementation' */ +static int cat_count = 0; /* `@category' */ + +static tree objc_class_template, objc_category_template, uprivate_record; +static tree objc_protocol_template, objc_selector_template; +static tree ucls_super_ref, uucls_super_ref; + +static tree objc_method_template, objc_ivar_template; +static tree objc_symtab_template, objc_module_template; +static tree objc_super_template, objc_object_reference; + +static tree objc_object_id, objc_class_id, objc_id_id; +static tree constant_string_id; +static tree constant_string_type; +static tree UOBJC_SUPER_decl; + +static tree method_context = NULL_TREE; +static int method_slot = 0; /* Used by start_method_def, */ + +#define BUFSIZE 1024 + +static char *errbuf; /* Buffer for error diagnostics */ + +/* Data imported from tree.c. */ + +extern enum debug_info_type write_symbols; + +/* Data imported from toplev.c. */ + +extern char *dump_base_name; + +/* Generate code for GNU or NeXT runtime environment. */ + +#ifdef NEXT_OBJC_RUNTIME +int flag_next_runtime = 1; +#else +int flag_next_runtime = 0; +#endif + +int flag_typed_selectors; + +/* Open and close the file for outputting class declarations, if requested. */ + +int flag_gen_declaration = 0; + +FILE *gen_declaration_file; + +/* Warn if multiple methods are seen for the same selector, but with + different argument types. */ + +int warn_selector = 0; + +/* Warn if methods required by a protocol are not implemented in the + class adopting it. When turned off, methods inherited to that + class are also considered implemented */ + +int flag_warn_protocol = 1; + +/* Tells "encode_pointer/encode_aggregate" whether we are generating + type descriptors for instance variables (as opposed to methods). + Type descriptors for instance variables contain more information + than methods (for static typing and embedded structures). This + was added to support features being planned for dbkit2. */ + +static int generating_instance_variables = 0; + +/* Tells the compiler that this is a special run. Do not perform + any compiling, instead we are to test some platform dependent + features and output a C header file with appropriate definitions. */ + +static int print_struct_values = 0; + +/* Some platforms pass small structures through registers versus through + an invisible pointer. Determine at what size structure is the + transition point between the two possibilities. */ + +void +generate_struct_by_value_array () +{ + tree type; + tree field_decl, field_decl_chain; + int i, j; + int aggregate_in_mem[32]; + int found = 0; + + /* Presumbaly no platform passes 32 byte structures in a register. */ + for (i = 1; i < 32; i++) + { + char buffer[5]; + + /* Create an unnamed struct that has `i' character components */ + type = start_struct (RECORD_TYPE, NULL_TREE); + + strcpy (buffer, "c1"); + field_decl = create_builtin_decl (FIELD_DECL, + char_type_node, + buffer); + field_decl_chain = field_decl; + + for (j = 1; j < i; j++) + { + sprintf (buffer, "c%d", j + 1); + field_decl = create_builtin_decl (FIELD_DECL, + char_type_node, + buffer); + chainon (field_decl_chain, field_decl); + } + finish_struct (type, field_decl_chain, NULL_TREE); + + aggregate_in_mem[i] = aggregate_value_p (type); + if (!aggregate_in_mem[i]) + found = 1; + } + + /* We found some structures that are returned in registers instead of memory + so output the necessary data. */ + if (found) + { + for (i = 31; i >= 0; i--) + if (!aggregate_in_mem[i]) + break; + printf ("#define OBJC_MAX_STRUCT_BY_VALUE %d\n\n", i); + + /* The first member of the structure is always 0 because we don't handle + structures with 0 members */ + printf ("static int struct_forward_array[] = {\n 0"); + + for (j = 1; j <= i; j++) + printf (", %d", aggregate_in_mem[j]); + printf ("\n};\n"); + } + + exit (0); +} + +void +lang_init () +{ +#if !USE_CPPLIB + /* The beginning of the file is a new line; check for #. + With luck, we discover the real source file's name from that + and put it in input_filename. */ + ungetc (check_newline (), finput); +#endif + + /* The line number can be -1 if we had -g3 and the input file + had a directive specifying line 0. But we want predefined + functions to have a line number of 0, not -1. */ + if (lineno == -1) + lineno = 0; + + /* If gen_declaration desired, open the output file. */ + if (flag_gen_declaration) + { + int dump_base_name_length = strlen (dump_base_name); + register char *dumpname = (char *) xmalloc (dump_base_name_length + 7); + strcpy (dumpname, dump_base_name); + strcat (dumpname, ".decl"); + gen_declaration_file = fopen (dumpname, "w"); + if (gen_declaration_file == 0) + pfatal_with_name (dumpname); + } + + if (flag_next_runtime) + { + TAG_GETCLASS = "objc_getClass"; + TAG_GETMETACLASS = "objc_getMetaClass"; + TAG_MSGSEND = "objc_msgSend"; + TAG_MSGSENDSUPER = "objc_msgSendSuper"; + TAG_EXECCLASS = "__objc_execClass"; + } + else + { + TAG_GETCLASS = "objc_get_class"; + TAG_GETMETACLASS = "objc_get_meta_class"; + TAG_MSGSEND = "objc_msg_lookup"; + TAG_MSGSENDSUPER = "objc_msg_lookup_super"; + TAG_EXECCLASS = "__objc_exec_class"; + flag_typed_selectors = 1; + } + + if (doing_objc_thang) + init_objc (); + + if (print_struct_values) + generate_struct_by_value_array (); +} + +static void +objc_fatal () +{ + fatal ("Objective-C text in C source file"); +} + +void +finish_file () +{ + if (doing_objc_thang) + finish_objc (); /* Objective-C finalization */ + + if (gen_declaration_file) + fclose (gen_declaration_file); +} + +void +lang_finish () +{ +} + +char * +lang_identify () +{ + return "objc"; +} + +int +lang_decode_option (p) + char *p; +{ + if (!strcmp (p, "-lang-objc")) + doing_objc_thang = 1; + else if (!strcmp (p, "-gen-decls")) + flag_gen_declaration = 1; + else if (!strcmp (p, "-Wselector")) + warn_selector = 1; + else if (!strcmp (p, "-Wno-selector")) + warn_selector = 0; + else if (!strcmp (p, "-Wprotocol")) + flag_warn_protocol = 1; + else if (!strcmp (p, "-Wno-protocol")) + flag_warn_protocol = 0; + else if (!strcmp (p, "-fgnu-runtime")) + flag_next_runtime = 0; + else if (!strcmp (p, "-fno-next-runtime")) + flag_next_runtime = 0; + else if (!strcmp (p, "-fno-gnu-runtime")) + flag_next_runtime = 1; + else if (!strcmp (p, "-fnext-runtime")) + flag_next_runtime = 1; + else if (!strcmp (p, "-print-objc-runtime-info")) + print_struct_values = 1; + else + return c_decode_option (p); + + return 1; +} + +static tree +define_decl (declarator, declspecs) + tree declarator; + tree declspecs; +{ + tree decl = start_decl (declarator, declspecs, 0, NULL_TREE, NULL_TREE); + finish_decl (decl, NULL_TREE, NULL_TREE); + return decl; +} + +/* Return 1 if LHS and RHS are compatible types for assignment or + various other operations. Return 0 if they are incompatible, and + return -1 if we choose to not decide. When the operation is + REFLEXIVE, check for compatibility in either direction. + + For statically typed objects, an assignment of the form `a' = `b' + is permitted if: + + `a' is of type "id", + `a' and `b' are the same class type, or + `a' and `b' are of class types A and B such that B is a descendant of A. */ + +int +maybe_objc_comptypes (lhs, rhs, reflexive) + tree lhs, rhs; + int reflexive; +{ + if (doing_objc_thang) + return objc_comptypes (lhs, rhs, reflexive); + return -1; +} + +static tree +lookup_method_in_protocol_list (rproto_list, sel_name, class_meth) + tree rproto_list; + tree sel_name; + int class_meth; +{ + tree rproto, p; + tree fnd = 0; + + for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto)) + { + p = TREE_VALUE (rproto); + + if (TREE_CODE (p) == PROTOCOL_INTERFACE_TYPE) + { + if ((fnd = lookup_method (class_meth + ? PROTOCOL_CLS_METHODS (p) + : PROTOCOL_NST_METHODS (p), sel_name))) + ; + else if (PROTOCOL_LIST (p)) + fnd = lookup_method_in_protocol_list (PROTOCOL_LIST (p), + sel_name, class_meth); + } + else + ; /* An identifier...if we could not find a protocol. */ + + if (fnd) + return fnd; + } + + return 0; +} + +static tree +lookup_protocol_in_reflist (rproto_list, lproto) + tree rproto_list; + tree lproto; +{ + tree rproto, p; + + /* Make sure the protocol is support by the object on the rhs. */ + if (TREE_CODE (lproto) == PROTOCOL_INTERFACE_TYPE) + { + tree fnd = 0; + for (rproto = rproto_list; rproto; rproto = TREE_CHAIN (rproto)) + { + p = TREE_VALUE (rproto); + + if (TREE_CODE (p) == PROTOCOL_INTERFACE_TYPE) + { + if (lproto == p) + fnd = lproto; + + else if (PROTOCOL_LIST (p)) + fnd = lookup_protocol_in_reflist (PROTOCOL_LIST (p), lproto); + } + + if (fnd) + return fnd; + } + } + else + ; /* An identifier...if we could not find a protocol. */ + + return 0; +} + +/* Return 1 if LHS and RHS are compatible types for assignment + or various other operations. Return 0 if they are incompatible, + and return -1 if we choose to not decide. When the operation + is REFLEXIVE, check for compatibility in either direction. */ + +int +objc_comptypes (lhs, rhs, reflexive) + tree lhs; + tree rhs; + int reflexive; +{ + /* New clause for protocols. */ + + if (TREE_CODE (lhs) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (lhs)) == RECORD_TYPE + && TREE_CODE (rhs) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (rhs)) == RECORD_TYPE) + { + int lhs_is_proto = IS_PROTOCOL_QUALIFIED_ID (lhs); + int rhs_is_proto = IS_PROTOCOL_QUALIFIED_ID (rhs); + + if (lhs_is_proto) + { + tree lproto, lproto_list = TYPE_PROTOCOL_LIST (lhs); + tree rproto, rproto_list; + tree p; + + if (rhs_is_proto) + { + rproto_list = TYPE_PROTOCOL_LIST (rhs); + + /* Make sure the protocol is supported by the object + on the rhs. */ + for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto)) + { + p = TREE_VALUE (lproto); + rproto = lookup_protocol_in_reflist (rproto_list, p); + + if (!rproto) + warning ("object does not conform to the `%s' protocol", + IDENTIFIER_POINTER (PROTOCOL_NAME (p))); + } + } + else if (TYPED_OBJECT (TREE_TYPE (rhs))) + { + tree rname = TYPE_NAME (TREE_TYPE (rhs)); + tree rinter; + + /* Make sure the protocol is supported by the object + on the rhs. */ + for (lproto = lproto_list; lproto; lproto = TREE_CHAIN (lproto)) + { + p = TREE_VALUE (lproto); + rproto = 0; + rinter = lookup_interface (rname); + + while (rinter && !rproto) + { + tree cat; + + rproto_list = CLASS_PROTOCOL_LIST (rinter); + rproto = lookup_protocol_in_reflist (rproto_list, p); + + /* Check for protocols adopted by categories. */ + cat = CLASS_CATEGORY_LIST (rinter); + while (cat && !rproto) + { + rproto_list = CLASS_PROTOCOL_LIST (cat); + rproto = lookup_protocol_in_reflist (rproto_list, p); + + cat = CLASS_CATEGORY_LIST (cat); + } + + rinter = lookup_interface (CLASS_SUPER_NAME (rinter)); + } + + if (!rproto) + warning ("class `%s' does not implement the `%s' protocol", + IDENTIFIER_POINTER (TYPE_NAME (TREE_TYPE (rhs))), + IDENTIFIER_POINTER (PROTOCOL_NAME (p))); + } + } + + /* May change...based on whether there was any mismatch */ + return 1; + } + else if (rhs_is_proto) + /* Lhs is not a protocol...warn if it is statically typed */ + return (TYPED_OBJECT (TREE_TYPE (lhs)) != 0); + + else + /* Defer to comptypes .*/ + return -1; + } + + else if (TREE_CODE (lhs) == RECORD_TYPE && TREE_CODE (rhs) == RECORD_TYPE) + ; /* Fall thru. This is the case we have been handling all along */ + else + /* Defer to comptypes. */ + return -1; + + /* `id' = `<class> *', `<class> *' = `id' */ + + if ((TYPE_NAME (lhs) == objc_object_id && TYPED_OBJECT (rhs)) + || (TYPE_NAME (rhs) == objc_object_id && TYPED_OBJECT (lhs))) + return 1; + + /* `id' = `Class', `Class' = `id' */ + + else if ((TYPE_NAME (lhs) == objc_object_id + && TYPE_NAME (rhs) == objc_class_id) + || (TYPE_NAME (lhs) == objc_class_id + && TYPE_NAME (rhs) == objc_object_id)) + return 1; + + /* `<class> *' = `<class> *' */ + + else if (TYPED_OBJECT (lhs) && TYPED_OBJECT (rhs)) + { + tree lname = TYPE_NAME (lhs); + tree rname = TYPE_NAME (rhs); + tree inter; + + if (lname == rname) + return 1; + + /* If the left hand side is a super class of the right hand side, + allow it. */ + for (inter = lookup_interface (rname); inter; + inter = lookup_interface (CLASS_SUPER_NAME (inter))) + if (lname == CLASS_SUPER_NAME (inter)) + return 1; + + /* Allow the reverse when reflexive. */ + if (reflexive) + for (inter = lookup_interface (lname); inter; + inter = lookup_interface (CLASS_SUPER_NAME (inter))) + if (rname == CLASS_SUPER_NAME (inter)) + return 1; + + return 0; + } + else + /* Defer to comptypes. */ + return -1; +} + +/* Called from c-decl.c before all calls to rest_of_decl_compilation. */ + +void +objc_check_decl (decl) + tree decl; +{ + tree type = TREE_TYPE (decl); + + if (TREE_CODE (type) == RECORD_TYPE + && TREE_STATIC_TEMPLATE (type) + && type != constant_string_type) + { + error_with_decl (decl, "`%s' cannot be statically allocated"); + fatal ("statically allocated objects not supported"); + } +} + +void +maybe_objc_check_decl (decl) + tree decl; +{ + if (doing_objc_thang) + objc_check_decl (decl); +} + +/* Implement static typing. At this point, we know we have an interface. */ + +tree +get_static_reference (interface, protocols) + tree interface; + tree protocols; +{ + tree type = xref_tag (RECORD_TYPE, interface); + + if (protocols) + { + tree t, m = TYPE_MAIN_VARIANT (type); + + push_obstacks_nochange (); + end_temporary_allocation (); + t = copy_node (type); + TYPE_BINFO (t) = make_tree_vec (2); + pop_obstacks (); + + /* Add this type to the chain of variants of TYPE. */ + TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m); + TYPE_NEXT_VARIANT (m) = t; + + /* Look up protocols and install in lang specific list. */ + TYPE_PROTOCOL_LIST (t) = lookup_and_install_protocols (protocols); + + /* This forces a new pointer type to be created later + (in build_pointer_type)...so that the new template + we just created will actually be used...what a hack! */ + if (TYPE_POINTER_TO (t)) + TYPE_POINTER_TO (t) = 0; + + type = t; + } + + return type; +} + +tree +get_object_reference (protocols) + tree protocols; +{ + tree type_decl = lookup_name (objc_id_id); + tree type; + + if (type_decl && TREE_CODE (type_decl) == TYPE_DECL) + { + type = TREE_TYPE (type_decl); + if (TYPE_MAIN_VARIANT (type) != id_type) + warning ("Unexpected type for `id' (%s)", + gen_declaration (type, errbuf)); + } + else + fatal ("Undefined type `id', please import <objc/objc.h>"); + + /* This clause creates a new pointer type that is qualified with + the protocol specification...this info is used later to do more + elaborate type checking. */ + + if (protocols) + { + tree t, m = TYPE_MAIN_VARIANT (type); + + push_obstacks_nochange (); + end_temporary_allocation (); + t = copy_node (type); + TYPE_BINFO (t) = make_tree_vec (2); + pop_obstacks (); + + /* Add this type to the chain of variants of TYPE. */ + TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m); + TYPE_NEXT_VARIANT (m) = t; + + /* Look up protocols...and install in lang specific list */ + TYPE_PROTOCOL_LIST (t) = lookup_and_install_protocols (protocols); + + /* This forces a new pointer type to be created later + (in build_pointer_type)...so that the new template + we just created will actually be used...what a hack! */ + if (TYPE_POINTER_TO (t)) + TYPE_POINTER_TO (t) = NULL; + + type = t; + } + return type; +} + +static tree +lookup_and_install_protocols (protocols) + tree protocols; +{ + tree proto; + tree prev = NULL; + tree return_value = protocols; + + for (proto = protocols; proto; proto = TREE_CHAIN (proto)) + { + tree ident = TREE_VALUE (proto); + tree p = lookup_protocol (ident); + + if (!p) + { + error ("Cannot find protocol declaration for `%s'", + IDENTIFIER_POINTER (ident)); + if (prev) + TREE_CHAIN (prev) = TREE_CHAIN (proto); + else + return_value = TREE_CHAIN (proto); + } + else + { + /* Replace identifier with actual protocol node. */ + TREE_VALUE (proto) = p; + prev = proto; + } + } + + return return_value; +} + +/* Create and push a decl for a built-in external variable or field NAME. + CODE says which. + TYPE is its data type. */ + +static tree +create_builtin_decl (code, type, name) + enum tree_code code; + tree type; + char *name; +{ + tree decl = build_decl (code, get_identifier (name), type); + + if (code == VAR_DECL) + { + TREE_STATIC (decl) = 1; + make_decl_rtl (decl, 0, 1); + pushdecl (decl); + } + + DECL_ARTIFICIAL (decl) = 1; + return decl; +} + +/* Purpose: "play" parser, creating/installing representations + of the declarations that are required by Objective-C. + + Model: + + type_spec--------->sc_spec + (tree_list) (tree_list) + | | + | | + identifier_node identifier_node */ + +static void +synth_module_prologue () +{ + tree temp_type; + tree super_p; + + /* Defined in `objc.h' */ + objc_object_id = get_identifier (TAG_OBJECT); + + objc_object_reference = xref_tag (RECORD_TYPE, objc_object_id); + + id_type = build_pointer_type (objc_object_reference); + + objc_id_id = get_identifier (TYPE_ID); + objc_class_id = get_identifier (TAG_CLASS); + + objc_class_type = build_pointer_type (xref_tag (RECORD_TYPE, objc_class_id)); + protocol_type = build_pointer_type (xref_tag (RECORD_TYPE, + get_identifier (PROTOCOL_OBJECT_CLASS_NAME))); + + /* Declare type of selector-objects that represent an operation name. */ + +#ifdef OBJC_INT_SELECTORS + /* `unsigned int' */ + selector_type = unsigned_type_node; +#else + /* `struct objc_selector *' */ + selector_type + = build_pointer_type (xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); +#endif /* not OBJC_INT_SELECTORS */ + + /* Forward declare type, or else the prototype for msgSendSuper will + complain. */ + + super_p = build_pointer_type (xref_tag (RECORD_TYPE, + get_identifier (TAG_SUPER))); + + + /* id objc_msgSend (id, SEL, ...); */ + + temp_type + = build_function_type (id_type, + tree_cons (NULL_TREE, id_type, + tree_cons (NULL_TREE, selector_type, + NULL_TREE))); + + if (! flag_next_runtime) + { + umsg_decl = build_decl (FUNCTION_DECL, + get_identifier (TAG_MSGSEND), temp_type); + DECL_EXTERNAL (umsg_decl) = 1; + TREE_PUBLIC (umsg_decl) = 1; + DECL_INLINE (umsg_decl) = 1; + DECL_ARTIFICIAL (umsg_decl) = 1; + + if (flag_traditional && TAG_MSGSEND[0] != '_') + DECL_BUILT_IN_NONANSI (umsg_decl) = 1; + + make_decl_rtl (umsg_decl, NULL_PTR, 1); + pushdecl (umsg_decl); + } + else + umsg_decl = builtin_function (TAG_MSGSEND, temp_type, NOT_BUILT_IN, 0); + + /* id objc_msgSendSuper (struct objc_super *, SEL, ...); */ + + temp_type + = build_function_type (id_type, + tree_cons (NULL_TREE, super_p, + tree_cons (NULL_TREE, selector_type, + NULL_TREE))); + + umsg_super_decl = builtin_function (TAG_MSGSENDSUPER, + temp_type, NOT_BUILT_IN, 0); + + /* id objc_getClass (const char *); */ + + temp_type = build_function_type (id_type, + tree_cons (NULL_TREE, + const_string_type_node, + tree_cons (NULL_TREE, void_type_node, + NULL_TREE))); + + objc_get_class_decl + = builtin_function (TAG_GETCLASS, temp_type, NOT_BUILT_IN, 0); + + /* id objc_getMetaClass (const char *); */ + + objc_get_meta_class_decl + = builtin_function (TAG_GETMETACLASS, temp_type, NOT_BUILT_IN, 0); + + /* static SEL _OBJC_SELECTOR_TABLE[]; */ + + if (! flag_next_runtime) + { + if (flag_typed_selectors) + { + /* Suppress outputting debug symbols, because + dbxout_init hasn'r been called yet. */ + enum debug_info_type save_write_symbols = write_symbols; + write_symbols = NO_DEBUG; + + build_selector_template (); + temp_type = build_array_type (objc_selector_template, NULL_TREE); + + write_symbols = save_write_symbols; + } + else + temp_type = build_array_type (selector_type, NULL_TREE); + + layout_type (temp_type); + UOBJC_SELECTOR_TABLE_decl + = create_builtin_decl (VAR_DECL, temp_type, + "_OBJC_SELECTOR_TABLE"); + + /* Avoid warning when not sending messages. */ + TREE_USED (UOBJC_SELECTOR_TABLE_decl) = 1; + } + + generate_forward_declaration_to_string_table (); + + /* Forward declare constant_string_id and constant_string_type. */ + constant_string_id = get_identifier (STRING_OBJECT_CLASS_NAME); + constant_string_type = xref_tag (RECORD_TYPE, constant_string_id); +} + +/* Custom build_string which sets TREE_TYPE! */ + +static tree +my_build_string (len, str) + int len; + char *str; +{ + int wide_flag = 0; + tree a_string = build_string (len, str); + + /* Some code from combine_strings, which is local to c-parse.y. */ + if (TREE_TYPE (a_string) == int_array_type_node) + wide_flag = 1; + + TREE_TYPE (a_string) + = build_array_type (wide_flag ? integer_type_node : char_type_node, + build_index_type (build_int_2 (len - 1, 0))); + + TREE_CONSTANT (a_string) = 1; /* Puts string in the readonly segment */ + TREE_STATIC (a_string) = 1; + + return a_string; +} + +/* Return a newly constructed OBJC_STRING_CST node whose value is + the LEN characters at STR. + The TREE_TYPE is not initialized. */ + +tree +build_objc_string (len, str) + int len; + char *str; +{ + tree s = build_string (len, str); + + TREE_SET_CODE (s, OBJC_STRING_CST); + return s; +} + +/* Given a chain of OBJC_STRING_CST's, build a static instance of + NXConstanString which points at the concatenation of those strings. + We place the string object in the __string_objects section of the + __OBJC segment. The Objective-C runtime will initialize the isa + pointers of the string objects to point at the NXConstandString class + object. */ + +tree +build_objc_string_object (strings) + tree strings; +{ + tree string, initlist, constructor; + int length; + + if (!doing_objc_thang) + objc_fatal (); + + if (lookup_interface (constant_string_id) == NULL_TREE) + { + error ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (constant_string_id)); + return error_mark_node; + } + + add_class_reference (constant_string_id); + + /* Combine_strings will work for OBJC_STRING_CST's too. */ + string = combine_strings (strings); + TREE_SET_CODE (string, STRING_CST); + length = TREE_STRING_LENGTH (string) - 1; + + if (! flag_next_runtime) + { + push_obstacks_nochange (); + end_temporary_allocation (); + if (! TREE_PERMANENT (strings)) + string = my_build_string (length + 1, + TREE_STRING_POINTER (string)); + } + + /* & ((NXConstantString) {0, string, length}) */ + + initlist = build_tree_list (NULL_TREE, build_int_2 (0, 0)); + initlist + = tree_cons (NULL_TREE, copy_node (build_unary_op (ADDR_EXPR, string, 1)), + initlist); + initlist = tree_cons (NULL_TREE, build_int_2 (length, 0), initlist); + constructor = build_constructor (constant_string_type, nreverse (initlist)); + + if (!flag_next_runtime) + { + constructor + = objc_add_static_instance (constructor, constant_string_type); + pop_obstacks (); + } + + return (build_unary_op (ADDR_EXPR, constructor, 1)); +} + +/* Declare a static instance of CLASS_DECL initialized by CONSTRUCTOR. */ + +static tree +objc_add_static_instance (constructor, class_decl) + tree constructor, class_decl; +{ + static int num_static_inst; + tree *chain, decl, decl_spec, decl_expr; + char buf[256]; + + push_obstacks_nochange (); + end_temporary_allocation (); + + /* Find the list of static instances for the CLASS_DECL. Create one if + not found. */ + for (chain = &objc_static_instances; + *chain && TREE_VALUE (*chain) != class_decl; + chain = &TREE_CHAIN (*chain)); + if (!*chain) + { + *chain = tree_cons (NULL_TREE, class_decl, NULL_TREE); + add_objc_string (TYPE_NAME (class_decl), class_names); + } + + sprintf (buf, "_OBJC_INSTANCE_%d", num_static_inst++); + decl = build_decl (VAR_DECL, get_identifier (buf), class_decl); + DECL_COMMON (decl) = 1; + TREE_STATIC (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + pushdecl_top_level (decl); + rest_of_decl_compilation (decl, 0, 1, 0); + + /* Do this here so it gets output later instead of possibly + inside something else we are writing. */ + DECL_INITIAL (decl) = constructor; + + /* Add the DECL to the head of this CLASS' list. */ + TREE_PURPOSE (*chain) = tree_cons (NULL_TREE, decl, TREE_PURPOSE (*chain)); + + pop_obstacks (); + return decl; +} + +/* Build a static constant CONSTRUCTOR + with type TYPE and elements ELTS. */ + +static tree +build_constructor (type, elts) + tree type, elts; +{ + tree constructor = build (CONSTRUCTOR, type, NULL_TREE, elts); + + TREE_CONSTANT (constructor) = 1; + TREE_STATIC (constructor) = 1; + TREE_READONLY (constructor) = 1; + + return constructor; +} + +/* Take care of defining and initializing _OBJC_SYMBOLS. */ + +/* Predefine the following data type: + + struct _objc_symtab + { + long sel_ref_cnt; + SEL *refs; + short cls_def_cnt; + short cat_def_cnt; + void *defs[cls_def_cnt + cat_def_cnt]; + }; */ + +static void +build_objc_symtab_template () +{ + tree field_decl, field_decl_chain, index; + + objc_symtab_template + = start_struct (RECORD_TYPE, get_identifier (UTAG_SYMTAB)); + + /* long sel_ref_cnt; */ + + field_decl = create_builtin_decl (FIELD_DECL, + long_integer_type_node, + "sel_ref_cnt"); + field_decl_chain = field_decl; + + /* SEL *refs; */ + + field_decl = create_builtin_decl (FIELD_DECL, + build_pointer_type (selector_type), + "refs"); + chainon (field_decl_chain, field_decl); + + /* short cls_def_cnt; */ + + field_decl = create_builtin_decl (FIELD_DECL, + short_integer_type_node, + "cls_def_cnt"); + chainon (field_decl_chain, field_decl); + + /* short cat_def_cnt; */ + + field_decl = create_builtin_decl (FIELD_DECL, + short_integer_type_node, + "cat_def_cnt"); + chainon (field_decl_chain, field_decl); + + /* void *defs[cls_def_cnt + cat_def_cnt]; */ + + if (!flag_next_runtime) + index = build_index_type (build_int_2 (imp_count + cat_count, 0)); + else + index = build_index_type (build_int_2 (imp_count + cat_count - 1, + imp_count == 0 && cat_count == 0 + ? -1 : 0)); + field_decl = create_builtin_decl (FIELD_DECL, + build_array_type (ptr_type_node, index), + "defs"); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_symtab_template, field_decl_chain, NULL_TREE); +} + +/* Create the initial value for the `defs' field of _objc_symtab. + This is a CONSTRUCTOR. */ + +static tree +init_def_list (type) + tree type; +{ + tree expr, initlist = NULL_TREE; + struct imp_entry *impent; + + if (imp_count) + for (impent = imp_list; impent; impent = impent->next) + { + if (TREE_CODE (impent->imp_context) == CLASS_IMPLEMENTATION_TYPE) + { + expr = build_unary_op (ADDR_EXPR, impent->class_decl, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + } + + if (cat_count) + for (impent = imp_list; impent; impent = impent->next) + { + if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE) + { + expr = build_unary_op (ADDR_EXPR, impent->class_decl, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + } + + if (!flag_next_runtime) + { + /* statics = { ..., _OBJC_STATIC_INSTANCES, ... } */ + tree expr; + + if (static_instances_decl) + expr = build_unary_op (ADDR_EXPR, static_instances_decl, 0); + else + expr = build_int_2 (0, 0); + + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + return build_constructor (type, nreverse (initlist)); +} + +/* Construct the initial value for all of _objc_symtab. */ + +static tree +init_objc_symtab (type) + tree type; +{ + tree initlist; + + /* sel_ref_cnt = { ..., 5, ... } */ + + initlist = build_tree_list (NULL_TREE, build_int_2 (0, 0)); + + /* refs = { ..., _OBJC_SELECTOR_TABLE, ... } */ + + if (flag_next_runtime || ! sel_ref_chain) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + initlist = tree_cons (NULL_TREE, + build_unary_op (ADDR_EXPR, + UOBJC_SELECTOR_TABLE_decl, 1), + initlist); + + /* cls_def_cnt = { ..., 5, ... } */ + + initlist = tree_cons (NULL_TREE, build_int_2 (imp_count, 0), initlist); + + /* cat_def_cnt = { ..., 5, ... } */ + + initlist = tree_cons (NULL_TREE, build_int_2 (cat_count, 0), initlist); + + /* cls_def = { ..., { &Foo, &Bar, ...}, ... } */ + + if (imp_count || cat_count || static_instances_decl) + { + + tree field = TYPE_FIELDS (type); + field = TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (field)))); + + initlist = tree_cons (NULL_TREE, init_def_list (TREE_TYPE (field)), + initlist); + } + + return build_constructor (type, nreverse (initlist)); +} + +/* Push forward-declarations of all the categories + so that init_def_list can use them in a CONSTRUCTOR. */ + +static void +forward_declare_categories () +{ + struct imp_entry *impent; + tree sav = implementation_context; + + for (impent = imp_list; impent; impent = impent->next) + { + if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE) + { + /* Set an invisible arg to synth_id_with_class_suffix. */ + implementation_context = impent->imp_context; + impent->class_decl + = create_builtin_decl (VAR_DECL, objc_category_template, + IDENTIFIER_POINTER (synth_id_with_class_suffix ("_OBJC_CATEGORY", implementation_context))); + } + } + implementation_context = sav; +} + +/* Create the declaration of _OBJC_SYMBOLS, with type `strict _objc_symtab' + and initialized appropriately. */ + +static void +generate_objc_symtab_decl () +{ + tree sc_spec; + + if (!objc_category_template) + build_category_template (); + + /* forward declare categories */ + if (cat_count) + forward_declare_categories (); + + if (!objc_symtab_template) + build_objc_symtab_template (); + + sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]); + + UOBJC_SYMBOLS_decl = start_decl (get_identifier ("_OBJC_SYMBOLS"), + tree_cons (NULL_TREE, + objc_symtab_template, sc_spec), + 1, + NULL_TREE, NULL_TREE); + + TREE_USED (UOBJC_SYMBOLS_decl) = 1; + DECL_IGNORED_P (UOBJC_SYMBOLS_decl) = 1; + DECL_ARTIFICIAL (UOBJC_SYMBOLS_decl) = 1; + finish_decl (UOBJC_SYMBOLS_decl, + init_objc_symtab (TREE_TYPE (UOBJC_SYMBOLS_decl)), + NULL_TREE); +} + +static tree +init_module_descriptor (type) + tree type; +{ + tree initlist, expr; + + /* version = { 1, ... } */ + + expr = build_int_2 (OBJC_VERSION, 0); + initlist = build_tree_list (NULL_TREE, expr); + + /* size = { ..., sizeof (struct objc_module), ... } */ + + expr = size_in_bytes (objc_module_template); + initlist = tree_cons (NULL_TREE, expr, initlist); + + /* name = { ..., "foo.m", ... } */ + + expr = add_objc_string (get_identifier (input_filename), class_names); + initlist = tree_cons (NULL_TREE, expr, initlist); + + /* symtab = { ..., _OBJC_SYMBOLS, ... } */ + + if (UOBJC_SYMBOLS_decl) + expr = build_unary_op (ADDR_EXPR, UOBJC_SYMBOLS_decl, 0); + else + expr = build_int_2 (0, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + + return build_constructor (type, nreverse (initlist)); +} + +/* Write out the data structures to describe Objective C classes defined. + If appropriate, compile and output a setup function to initialize them. + Return a string which is the name of a function to call to initialize + the Objective C data structures for this file (and perhaps for other files + also). + + struct objc_module { ... } _OBJC_MODULE = { ... }; */ + +static char * +build_module_descriptor () +{ + tree decl_specs, field_decl, field_decl_chain; + + objc_module_template + = start_struct (RECORD_TYPE, get_identifier (UTAG_MODULE)); + + /* Long version; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("version"); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* long size; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("size"); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* char *name; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("name")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_symtab *symtab; */ + + decl_specs = get_identifier (UTAG_SYMTAB); + decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, decl_specs)); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("symtab")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_module_template, field_decl_chain, NULL_TREE); + + /* Create an instance of "objc_module". */ + + decl_specs = tree_cons (NULL_TREE, objc_module_template, + build_tree_list (NULL_TREE, + ridpointers[(int) RID_STATIC])); + + UOBJC_MODULES_decl = start_decl (get_identifier ("_OBJC_MODULES"), + decl_specs, 1, NULL_TREE, NULL_TREE); + + DECL_ARTIFICIAL (UOBJC_MODULES_decl) = 1; + DECL_IGNORED_P (UOBJC_MODULES_decl) = 1; + finish_decl (UOBJC_MODULES_decl, + init_module_descriptor (TREE_TYPE (UOBJC_MODULES_decl)), + NULL_TREE); + + /* Mark the decl to avoid "defined but not used" warning. */ + DECL_IN_SYSTEM_HEADER (UOBJC_MODULES_decl) = 1; + + /* Generate a constructor call for the module descriptor. + This code was generated by reading the grammar rules + of c-parse.in; Therefore, it may not be the most efficient + way of generating the requisite code. */ + + if (flag_next_runtime) + return 0; + + { + tree parms, function_decl, decelerator, void_list_node; + tree function_type; + extern tree get_file_function_name (); + tree init_function_name = get_file_function_name ('I'); + + /* Declare void __objc_execClass (void *); */ + + void_list_node = build_tree_list (NULL_TREE, void_type_node); + function_type + = build_function_type (void_type_node, + tree_cons (NULL_TREE, ptr_type_node, + void_list_node)); + function_decl = build_decl (FUNCTION_DECL, + get_identifier (TAG_EXECCLASS), + function_type); + DECL_EXTERNAL (function_decl) = 1; + DECL_ARTIFICIAL (function_decl) = 1; + TREE_PUBLIC (function_decl) = 1; + + pushdecl (function_decl); + rest_of_decl_compilation (function_decl, 0, 0, 0); + + parms + = build_tree_list (NULL_TREE, + build_unary_op (ADDR_EXPR, UOBJC_MODULES_decl, 0)); + decelerator = build_function_call (function_decl, parms); + + /* void _GLOBAL_$I$<gnyf> () {objc_execClass (&L_OBJC_MODULES);} */ + + start_function (void_list_node, + build_parse_node (CALL_EXPR, init_function_name, + /* This has the format of the output + of get_parm_info. */ + tree_cons (NULL_TREE, NULL_TREE, + void_list_node), + NULL_TREE), + NULL_TREE, NULL_TREE, 0); +#if 0 /* This should be turned back on later + for the systems where collect is not needed. */ + /* Make these functions nonglobal + so each file can use the same name. */ + TREE_PUBLIC (current_function_decl) = 0; +#endif + TREE_USED (current_function_decl) = 1; + store_parm_decls (); + + assemble_external (function_decl); + c_expand_expr_stmt (decelerator); + + TREE_PUBLIC (current_function_decl) = 1; + + function_decl = current_function_decl; + finish_function (0); + + /* Return the name of the constructor function. */ + return XSTR (XEXP (DECL_RTL (function_decl), 0), 0); + } +} + +/* extern const char _OBJC_STRINGS[]; */ + +static void +generate_forward_declaration_to_string_table () +{ + tree sc_spec, decl_specs, expr_decl; + + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_EXTERN], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); + + expr_decl + = build_nt (ARRAY_REF, get_identifier ("_OBJC_STRINGS"), NULL_TREE); + + UOBJC_STRINGS_decl = define_decl (expr_decl, decl_specs); +} + +/* Return the DECL of the string IDENT in the SECTION. */ + +static tree +get_objc_string_decl (ident, section) + tree ident; + enum string_section section; +{ + tree chain, decl; + + if (section == class_names) + chain = class_names_chain; + else if (section == meth_var_names) + chain = meth_var_names_chain; + else if (section == meth_var_types) + chain = meth_var_types_chain; + + for (; chain != 0; chain = TREE_VALUE (chain)) + if (TREE_VALUE (chain) == ident) + return (TREE_PURPOSE (chain)); + + abort (); + return NULL_TREE; +} + +/* Output references to all statically allocated objects. Return the DECL + for the array built. */ + +static void +generate_static_references () +{ + tree decls = NULL_TREE, ident, decl_spec, expr_decl, expr = NULL_TREE; + tree class_name, class, decl, instance, idecl, initlist; + tree cl_chain, in_chain, type; + int num_inst, num_class; + char buf[256]; + + if (flag_next_runtime) + abort (); + + for (cl_chain = objc_static_instances, num_class = 0; + cl_chain; cl_chain = TREE_CHAIN (cl_chain), num_class++) + { + for (num_inst = 0, in_chain = TREE_PURPOSE (cl_chain); + in_chain; num_inst++, in_chain = TREE_CHAIN (in_chain)); + + sprintf (buf, "_OBJC_STATIC_INSTANCES_%d", num_class); + ident = get_identifier (buf); + + expr_decl = build_nt (ARRAY_REF, ident, NULL_TREE); + decl_spec = tree_cons (NULL_TREE, build_pointer_type (void_type_node), + build_tree_list (NULL_TREE, + ridpointers[(int) RID_STATIC])); + decl = start_decl (expr_decl, decl_spec, 1, NULL_TREE, NULL_TREE); + DECL_CONTEXT (decl) = 0; + DECL_ARTIFICIAL (decl) = 1; + + /* Output {class_name, ...}. */ + class = TREE_VALUE (cl_chain); + class_name = get_objc_string_decl (TYPE_NAME (class), class_names); + initlist = build_tree_list (NULL_TREE, + build_unary_op (ADDR_EXPR, class_name, 1)); + + /* Output {..., instance, ...}. */ + for (in_chain = TREE_PURPOSE (cl_chain); + in_chain; in_chain = TREE_CHAIN (in_chain)) + { + expr = build_unary_op (ADDR_EXPR, TREE_VALUE (in_chain), 1); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + /* Output {..., NULL}. */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + + expr = build_constructor (TREE_TYPE (decl), nreverse (initlist)); + finish_decl (decl, expr, NULL_TREE); + TREE_USED (decl) = 1; + + type = build_array_type (build_pointer_type (void_type_node), 0); + decl = build_decl (VAR_DECL, ident, type); + make_decl_rtl (decl, 0, 1); + TREE_USED (decl) = 1; + decls + = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, decl, 1), decls); + } + + decls = tree_cons (NULL_TREE, build_int_2 (0, 0), decls); + ident = get_identifier ("_OBJC_STATIC_INSTANCES"); + expr_decl = build_nt (ARRAY_REF, ident, NULL_TREE); + decl_spec = tree_cons (NULL_TREE, build_pointer_type (void_type_node), + build_tree_list (NULL_TREE, + ridpointers[(int) RID_STATIC])); + static_instances_decl + = start_decl (expr_decl, decl_spec, 1, NULL_TREE, NULL_TREE); + TREE_USED (static_instances_decl) = 1; + DECL_CONTEXT (static_instances_decl) = 0; + DECL_ARTIFICIAL (static_instances_decl) = 1; + end_temporary_allocation (); + expr = build_constructor (TREE_TYPE (static_instances_decl), + nreverse (decls)); + finish_decl (static_instances_decl, expr, NULL_TREE); +} + +/* Output all strings. */ + +static void +generate_strings () +{ + tree sc_spec, decl_specs, expr_decl; + tree chain, string_expr; + tree string, decl; + + for (chain = class_names_chain; chain; chain = TREE_CHAIN (chain)) + { + string = TREE_VALUE (chain); + decl = TREE_PURPOSE (chain); + sc_spec + = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); + expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE); + decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE, NULL_TREE); + end_temporary_allocation (); + string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, + IDENTIFIER_POINTER (string)); + finish_decl (decl, string_expr, NULL_TREE); + } + + for (chain = meth_var_names_chain; chain; chain = TREE_CHAIN (chain)) + { + string = TREE_VALUE (chain); + decl = TREE_PURPOSE (chain); + sc_spec + = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); + expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE); + decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE, NULL_TREE); + string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, + IDENTIFIER_POINTER (string)); + finish_decl (decl, string_expr, NULL_TREE); + } + + for (chain = meth_var_types_chain; chain; chain = TREE_CHAIN (chain)) + { + string = TREE_VALUE (chain); + decl = TREE_PURPOSE (chain); + sc_spec + = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], sc_spec); + expr_decl = build_nt (ARRAY_REF, DECL_NAME (decl), NULL_TREE); + decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE, NULL_TREE); + string_expr = my_build_string (IDENTIFIER_LENGTH (string) + 1, + IDENTIFIER_POINTER (string)); + finish_decl (decl, string_expr, NULL_TREE); + } +} + +static tree +build_selector_reference_decl (name) + tree name; +{ + tree decl, ident; + char buf[256]; + static int idx = 0; + + sprintf (buf, "_OBJC_SELECTOR_REFERENCES_%d", idx++); + + push_obstacks_nochange (); + end_temporary_allocation (); + + ident = get_identifier (buf); + + decl = build_decl (VAR_DECL, ident, selector_type); + DECL_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + TREE_USED (decl) = 1; + TREE_READONLY (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_CONTEXT (decl) = 0; + + make_decl_rtl (decl, 0, 1); + pushdecl_top_level (decl); + + pop_obstacks (); + + return decl; +} + +/* Just a handy wrapper for add_objc_string. */ + +static tree +build_selector (ident) + tree ident; +{ + tree expr = add_objc_string (ident, meth_var_names); + if (flag_typed_selectors) + return expr; + else + return build_c_cast (selector_type, expr); /* cast! */ +} + +/* Synthesize the following expr: (char *)&_OBJC_STRINGS[<offset>] + The cast stops the compiler from issuing the following message: + grok.m: warning: initialization of non-const * pointer from const * + grok.m: warning: initialization between incompatible pointer types. */ + +static tree +build_msg_pool_reference (offset) + int offset; +{ + tree expr = build_int_2 (offset, 0); + tree cast; + + expr = build_array_ref (UOBJC_STRINGS_decl, expr); + expr = build_unary_op (ADDR_EXPR, expr, 0); + + cast = build_tree_list (build_tree_list (NULL_TREE, + ridpointers[(int) RID_CHAR]), + build1 (INDIRECT_REF, NULL_TREE, NULL_TREE)); + TREE_TYPE (expr) = groktypename (cast); + return expr; +} + +static tree +init_selector (offset) + int offset; +{ + tree expr = build_msg_pool_reference (offset); + TREE_TYPE (expr) = selector_type; + return expr; +} + +static void +build_selector_translation_table () +{ + tree sc_spec, decl_specs; + tree chain, initlist = NULL_TREE; + int offset = 0; + tree decl, var_decl, name; + + /* The corresponding pop_obstacks is in finish_decl, + called at the end of this function. */ + if (! flag_next_runtime) + push_obstacks_nochange (); + + for (chain = sel_ref_chain; chain; chain = TREE_CHAIN (chain)) + { + tree expr; + + expr = build_selector (TREE_VALUE (chain)); + + if (flag_next_runtime) + { + name = DECL_NAME (TREE_PURPOSE (chain)); + + sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]); + + /* static SEL _OBJC_SELECTOR_REFERENCES_n = ...; */ + decl_specs = tree_cons (NULL_TREE, selector_type, sc_spec); + + var_decl = name; + + /* The `decl' that is returned from start_decl is the one that we + forward declared in `build_selector_reference' */ + decl = start_decl (var_decl, decl_specs, 1, NULL_TREE, NULL_TREE); + } + + /* add one for the '\0' character */ + offset += IDENTIFIER_LENGTH (TREE_VALUE (chain)) + 1; + + if (flag_next_runtime) + finish_decl (decl, expr, NULL_TREE); + else + { + if (flag_typed_selectors) + { + tree eltlist = NULL_TREE; + tree encoding = get_proto_encoding (TREE_PURPOSE (chain)); + eltlist = tree_cons (NULL_TREE, expr, NULL_TREE); + eltlist = tree_cons (NULL_TREE, encoding, eltlist); + expr = build_constructor (objc_selector_template, + nreverse (eltlist)); + } + initlist = tree_cons (NULL_TREE, expr, initlist); + + } + } + + if (! flag_next_runtime) + { + /* Cause the variable and its initial value to be actually output. */ + DECL_EXTERNAL (UOBJC_SELECTOR_TABLE_decl) = 0; + TREE_STATIC (UOBJC_SELECTOR_TABLE_decl) = 1; + /* NULL terminate the list and fix the decl for output. */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + DECL_INITIAL (UOBJC_SELECTOR_TABLE_decl) = (tree) 1; + initlist = build_constructor (TREE_TYPE (UOBJC_SELECTOR_TABLE_decl), + nreverse (initlist)); + finish_decl (UOBJC_SELECTOR_TABLE_decl, initlist, NULL_TREE); + current_function_decl = NULL_TREE; + } +} + +static tree +get_proto_encoding (proto) + tree proto; +{ + tree encoding; + if (proto) + { + tree tmp_decl; + + if (! METHOD_ENCODING (proto)) + { + tmp_decl = build_tmp_function_decl (); + hack_method_prototype (proto, tmp_decl); + encoding = encode_method_prototype (proto, tmp_decl); + METHOD_ENCODING (proto) = encoding; + } + else + encoding = METHOD_ENCODING (proto); + + return add_objc_string (encoding, meth_var_types); + } + else + return build_int_2 (0, 0); +} + +/* sel_ref_chain is a list whose "value" fields will be instances of + identifier_node that represent the selector. */ + +static tree +build_typed_selector_reference (ident, proto) + tree ident, proto; +{ + tree *chain = &sel_ref_chain; + tree expr; + int index = 0; + + while (*chain) + { + if (TREE_PURPOSE (*chain) == ident && TREE_VALUE (*chain) == proto) + goto return_at_index; + + index++; + chain = &TREE_CHAIN (*chain); + } + + *chain = perm_tree_cons (proto, ident, NULL_TREE); + + return_at_index: + expr = build_unary_op (ADDR_EXPR, + build_array_ref (UOBJC_SELECTOR_TABLE_decl, + build_int_2 (index, 0)), + 1); + return build_c_cast (selector_type, expr); +} + +static tree +build_selector_reference (ident) + tree ident; +{ + tree *chain = &sel_ref_chain; + tree expr; + int index = 0; + + while (*chain) + { + if (TREE_VALUE (*chain) == ident) + return (flag_next_runtime + ? TREE_PURPOSE (*chain) + : build_array_ref (UOBJC_SELECTOR_TABLE_decl, + build_int_2 (index, 0))); + + index++; + chain = &TREE_CHAIN (*chain); + } + + expr = build_selector_reference_decl (ident); + + *chain = perm_tree_cons (expr, ident, NULL_TREE); + + return (flag_next_runtime + ? expr + : build_array_ref (UOBJC_SELECTOR_TABLE_decl, + build_int_2 (index, 0))); +} + +static tree +build_class_reference_decl (name) + tree name; +{ + tree decl, ident; + char buf[256]; + static int idx = 0; + + sprintf (buf, "_OBJC_CLASS_REFERENCES_%d", idx++); + + push_obstacks_nochange (); + end_temporary_allocation (); + + ident = get_identifier (buf); + + decl = build_decl (VAR_DECL, ident, objc_class_type); + DECL_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + TREE_USED (decl) = 1; + TREE_READONLY (decl) = 1; + DECL_CONTEXT (decl) = 0; + DECL_ARTIFICIAL (decl) = 1; + + make_decl_rtl (decl, 0, 1); + pushdecl_top_level (decl); + + pop_obstacks (); + + return decl; +} + +/* Create a class reference, but don't create a variable to reference + it. */ + +static void +add_class_reference (ident) + tree ident; +{ + tree chain; + + if ((chain = cls_ref_chain)) + { + tree tail; + do + { + if (ident == TREE_VALUE (chain)) + return; + + tail = chain; + chain = TREE_CHAIN (chain); + } + while (chain); + + /* Append to the end of the list */ + TREE_CHAIN (tail) = perm_tree_cons (NULL_TREE, ident, NULL_TREE); + } + else + cls_ref_chain = perm_tree_cons (NULL_TREE, ident, NULL_TREE); +} + +/* Get a class reference, creating it if necessary. Also create the + reference variable. */ + +tree +get_class_reference (ident) + tree ident; +{ + if (flag_next_runtime) + { + tree *chain; + tree decl; + + for (chain = &cls_ref_chain; *chain; chain = &TREE_CHAIN (*chain)) + if (TREE_VALUE (*chain) == ident) + { + if (! TREE_PURPOSE (*chain)) + TREE_PURPOSE (*chain) = build_class_reference_decl (ident); + + return TREE_PURPOSE (*chain); + } + + decl = build_class_reference_decl (ident); + *chain = perm_tree_cons (decl, ident, NULL_TREE); + return decl; + } + else + { + tree params; + + add_class_reference (ident); + + params = build_tree_list (NULL_TREE, + my_build_string (IDENTIFIER_LENGTH (ident) + 1, + IDENTIFIER_POINTER (ident))); + + assemble_external (objc_get_class_decl); + return build_function_call (objc_get_class_decl, params); + } +} + +/* SEL_REFDEF_CHAIN is a list whose "value" fields will be instances + of identifier_node that represent the selector. It returns the + offset of the selector from the beginning of the _OBJC_STRINGS + pool. This offset is typically used by init_selector during code + generation. + + For each string section we have a chain which maps identifier nodes + to decls for the strings. */ + +static tree +add_objc_string (ident, section) + tree ident; + enum string_section section; +{ + tree *chain, decl; + + if (section == class_names) + chain = &class_names_chain; + else if (section == meth_var_names) + chain = &meth_var_names_chain; + else if (section == meth_var_types) + chain = &meth_var_types_chain; + + while (*chain) + { + if (TREE_VALUE (*chain) == ident) + return build_unary_op (ADDR_EXPR, TREE_PURPOSE (*chain), 1); + + chain = &TREE_CHAIN (*chain); + } + + decl = build_objc_string_decl (ident, section); + + *chain = perm_tree_cons (decl, ident, NULL_TREE); + + return build_unary_op (ADDR_EXPR, decl, 1); +} + +static tree +build_objc_string_decl (name, section) + tree name; + enum string_section section; +{ + tree decl, ident; + char buf[256]; + static int class_names_idx = 0; + static int meth_var_names_idx = 0; + static int meth_var_types_idx = 0; + + if (section == class_names) + sprintf (buf, "_OBJC_CLASS_NAME_%d", class_names_idx++); + else if (section == meth_var_names) + sprintf (buf, "_OBJC_METH_VAR_NAME_%d", meth_var_names_idx++); + else if (section == meth_var_types) + sprintf (buf, "_OBJC_METH_VAR_TYPE_%d", meth_var_types_idx++); + + push_obstacks_nochange (); + end_temporary_allocation (); + ident = get_identifier (buf); + + decl = build_decl (VAR_DECL, ident, build_array_type (char_type_node, 0)); + DECL_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + TREE_USED (decl) = 1; + TREE_READONLY (decl) = 1; + TREE_CONSTANT (decl) = 1; + DECL_CONTEXT (decl) = 0; + DECL_ARTIFICIAL (decl) = 1; + + make_decl_rtl (decl, 0, 1); + pushdecl_top_level (decl); + + pop_obstacks (); + + return decl; +} + + +void +objc_declare_alias (alias_ident, class_ident) + tree alias_ident; + tree class_ident; +{ + if (!doing_objc_thang) + objc_fatal (); + + if (is_class_name (class_ident) != class_ident) + warning ("Cannot find class `%s'", IDENTIFIER_POINTER (class_ident)); + else if (is_class_name (alias_ident)) + warning ("Class `%s' already exists", IDENTIFIER_POINTER (alias_ident)); + else + alias_chain = tree_cons (class_ident, alias_ident, alias_chain); +} + +void +objc_declare_class (ident_list) + tree ident_list; +{ + tree list; + + if (!doing_objc_thang) + objc_fatal (); + + for (list = ident_list; list; list = TREE_CHAIN (list)) + { + tree ident = TREE_VALUE (list); + tree decl; + + if ((decl = lookup_name (ident))) + { + error ("`%s' redeclared as different kind of symbol", + IDENTIFIER_POINTER (ident)); + error_with_decl (decl, "previous declaration of `%s'"); + } + + if (! is_class_name (ident)) + { + tree record = xref_tag (RECORD_TYPE, ident); + TREE_STATIC_TEMPLATE (record) = 1; + class_chain = tree_cons (NULL_TREE, ident, class_chain); + } + } +} + +tree +is_class_name (ident) + tree ident; +{ + tree chain; + + if (lookup_interface (ident)) + return ident; + + for (chain = class_chain; chain; chain = TREE_CHAIN (chain)) + { + if (ident == TREE_VALUE (chain)) + return ident; + } + + for (chain = alias_chain; chain; chain = TREE_CHAIN (chain)) + { + if (ident == TREE_VALUE (chain)) + return TREE_PURPOSE (chain); + } + + return 0; +} + +tree +lookup_interface (ident) + tree ident; +{ + tree chain; + + for (chain = interface_chain; chain; chain = TREE_CHAIN (chain)) + { + if (ident == CLASS_NAME (chain)) + return chain; + } + return NULL_TREE; +} + +static tree +objc_copy_list (list, head) + tree list; + tree *head; +{ + tree newlist = NULL_TREE, tail = NULL_TREE; + + while (list) + { + tail = copy_node (list); + + /* The following statement fixes a bug when inheriting instance + variables that are declared to be bitfields. finish_struct + expects to find the width of the bitfield in DECL_INITIAL, + which it nulls out after processing the decl of the super + class...rather than change the way finish_struct works (which + is risky), I create the situation it expects...s.naroff + (7/23/89). */ + + if (DECL_BIT_FIELD (tail) && DECL_INITIAL (tail) == 0) + DECL_INITIAL (tail) = build_int_2 (DECL_FIELD_SIZE (tail), 0); + + newlist = chainon (newlist, tail); + list = TREE_CHAIN (list); + } + + *head = newlist; + return tail; +} + +/* Used by: build_private_template, get_class_ivars, and + continue_class. COPY is 1 when called from @defs. In this case + copy all fields. Otherwise don't copy leaf ivars since we rely on + them being side-effected exactly once by finish_struct. */ + +static tree +build_ivar_chain (interface, copy) + tree interface; + int copy; +{ + tree my_name, super_name, ivar_chain; + + my_name = CLASS_NAME (interface); + super_name = CLASS_SUPER_NAME (interface); + + /* Possibly copy leaf ivars. */ + if (copy) + objc_copy_list (CLASS_IVARS (interface), &ivar_chain); + else + ivar_chain = CLASS_IVARS (interface); + + while (super_name) + { + tree op1; + tree super_interface = lookup_interface (super_name); + + if (!super_interface) + { + /* fatal did not work with 2 args...should fix */ + error ("Cannot find interface declaration for `%s', superclass of `%s'", + IDENTIFIER_POINTER (super_name), + IDENTIFIER_POINTER (my_name)); + exit (FATAL_EXIT_CODE); + } + + if (super_interface == interface) + { + fatal ("Circular inheritance in interface declaration for `%s'", + IDENTIFIER_POINTER (super_name)); + } + + interface = super_interface; + my_name = CLASS_NAME (interface); + super_name = CLASS_SUPER_NAME (interface); + + op1 = CLASS_IVARS (interface); + if (op1) + { + tree head, tail = objc_copy_list (op1, &head); + + /* Prepend super class ivars...make a copy of the list, we + do not want to alter the original. */ + TREE_CHAIN (tail) = ivar_chain; + ivar_chain = head; + } + } + return ivar_chain; +} + +/* struct <classname> { + struct objc_class *isa; + ... + }; */ + +static tree +build_private_template (class) + tree class; +{ + tree ivar_context; + + if (CLASS_STATIC_TEMPLATE (class)) + { + uprivate_record = CLASS_STATIC_TEMPLATE (class); + ivar_context = TYPE_FIELDS (CLASS_STATIC_TEMPLATE (class)); + } + else + { + uprivate_record = start_struct (RECORD_TYPE, CLASS_NAME (class)); + + ivar_context = build_ivar_chain (class, 0); + + finish_struct (uprivate_record, ivar_context, NULL_TREE); + + CLASS_STATIC_TEMPLATE (class) = uprivate_record; + + /* mark this record as class template - for class type checking */ + TREE_STATIC_TEMPLATE (uprivate_record) = 1; + } + + instance_type + = groktypename (build_tree_list (build_tree_list (NULL_TREE, + uprivate_record), + build1 (INDIRECT_REF, NULL_TREE, + NULL_TREE))); + + return ivar_context; +} + +/* Begin code generation for protocols... */ + +/* struct objc_protocol { + char *protocol_name; + struct objc_protocol **protocol_list; + struct objc_method_desc *instance_methods; + struct objc_method_desc *class_methods; + }; */ + +static tree +build_protocol_template () +{ + tree decl_specs, field_decl, field_decl_chain; + tree template; + + template = start_struct (RECORD_TYPE, get_identifier (UTAG_PROTOCOL)); + + /* struct objc_class *isa; */ + + decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, + get_identifier (UTAG_CLASS))); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("isa")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* char *protocol_name; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("protocol_name")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_protocol **protocol_list; */ + + decl_specs = build_tree_list (NULL_TREE, template); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("protocol_list")); + field_decl = build1 (INDIRECT_REF, NULL_TREE, field_decl); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *instance_methods; */ + + decl_specs + = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_PROTOTYPE_LIST))); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("instance_methods")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *class_methods; */ + + decl_specs + = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_PROTOTYPE_LIST))); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("class_methods")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + return finish_struct (template, field_decl_chain, NULL_TREE); +} + +static tree +build_descriptor_table_initializer (type, entries) + tree type; + tree entries; +{ + tree initlist = NULL_TREE; + + do + { + tree eltlist = NULL_TREE; + + eltlist + = tree_cons (NULL_TREE, + build_selector (METHOD_SEL_NAME (entries)), NULL_TREE); + eltlist + = tree_cons (NULL_TREE, + add_objc_string (METHOD_ENCODING (entries), + meth_var_types), + eltlist); + + initlist + = tree_cons (NULL_TREE, + build_constructor (type, nreverse (eltlist)), initlist); + + entries = TREE_CHAIN (entries); + } + while (entries); + + return build_constructor (build_array_type (type, 0), nreverse (initlist)); +} + +/* struct objc_method_prototype_list { + int count; + struct objc_method_prototype { + SEL name; + char *types; + } list[1]; + }; */ + +static tree +build_method_prototype_list_template (list_type, size) + tree list_type; + int size; +{ + tree objc_ivar_list_record; + tree decl_specs, field_decl, field_decl_chain; + + /* Generate an unnamed struct definition. */ + + objc_ivar_list_record = start_struct (RECORD_TYPE, NULL_TREE); + + /* int method_count; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("method_count"); + + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* struct objc_method method_list[]; */ + + decl_specs = build_tree_list (NULL_TREE, list_type); + field_decl = build_nt (ARRAY_REF, get_identifier ("method_list"), + build_int_2 (size, 0)); + + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_ivar_list_record, field_decl_chain, NULL_TREE); + + return objc_ivar_list_record; +} + +static tree +build_method_prototype_template () +{ + tree proto_record; + tree decl_specs, field_decl, field_decl_chain; + + proto_record + = start_struct (RECORD_TYPE, get_identifier (UTAG_METHOD_PROTOTYPE)); + +#ifdef OBJC_INT_SELECTORS + /* unsigned int _cmd; */ + decl_specs + = tree_cons (NULL_TREE, ridpointers[(int) RID_UNSIGNED], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_INT], decl_specs); + field_decl = get_identifier ("_cmd"); +#else /* OBJC_INT_SELECTORS */ + /* struct objc_selector *_cmd; */ + decl_specs = tree_cons (NULL_TREE, xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR)), NULL_TREE); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("_cmd")); +#endif /* OBJC_INT_SELECTORS */ + + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], NULL_TREE); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("method_types")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (proto_record, field_decl_chain, NULL_TREE); + + return proto_record; +} + +/* True if last call to forwarding_offset yielded a register offset. */ +static int offset_is_register; + +static int +forwarding_offset (parm) + tree parm; +{ + int offset_in_bytes; + + if (GET_CODE (DECL_INCOMING_RTL (parm)) == MEM) + { + rtx addr = XEXP (DECL_INCOMING_RTL (parm), 0); + + /* ??? Here we assume that the parm address is indexed + off the frame pointer or arg pointer. + If that is not true, we produce meaningless results, + but do not crash. */ + if (GET_CODE (addr) == PLUS + && GET_CODE (XEXP (addr, 1)) == CONST_INT) + offset_in_bytes = INTVAL (XEXP (addr, 1)); + else + offset_in_bytes = 0; + + offset_in_bytes += OBJC_FORWARDING_STACK_OFFSET; + offset_is_register = 0; + } + else if (GET_CODE (DECL_INCOMING_RTL (parm)) == REG) + { + int regno = REGNO (DECL_INCOMING_RTL (parm)); + offset_in_bytes = apply_args_register_offset (regno); + offset_is_register = 1; + } + else + return 0; + + /* This is the case where the parm is passed as an int or double + and it is converted to a char, short or float and stored back + in the parmlist. In this case, describe the parm + with the variable's declared type, and adjust the address + if the least significant bytes (which we are using) are not + the first ones. */ + if (BYTES_BIG_ENDIAN && TREE_TYPE (parm) != DECL_ARG_TYPE (parm)) + offset_in_bytes += (GET_MODE_SIZE (TYPE_MODE (DECL_ARG_TYPE (parm))) + - GET_MODE_SIZE (GET_MODE (DECL_RTL (parm)))); + + return offset_in_bytes; +} + +static tree +encode_method_prototype (method_decl, func_decl) + tree method_decl; + tree func_decl; +{ + tree parms; + int stack_size, i; + tree user_args; + int max_parm_end = 0; + char buf[40]; + tree result; + + /* ONEWAY and BYCOPY, for remote object are the only method qualifiers. */ + encode_type_qualifiers (TREE_PURPOSE (TREE_TYPE (method_decl))); + + /* C type. */ + encode_type (TREE_TYPE (TREE_TYPE (func_decl)), + obstack_object_size (&util_obstack), + OBJC_ENCODE_INLINE_DEFS); + + /* Stack size. */ + for (parms = DECL_ARGUMENTS (func_decl); parms; + parms = TREE_CHAIN (parms)) + { + int parm_end = (forwarding_offset (parms) + + (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (parms))) + / BITS_PER_UNIT)); + + if (!offset_is_register && max_parm_end < parm_end) + max_parm_end = parm_end; + } + + stack_size = max_parm_end - OBJC_FORWARDING_MIN_OFFSET; + + sprintf (buf, "%d", stack_size); + obstack_grow (&util_obstack, buf, strlen (buf)); + + user_args = METHOD_SEL_ARGS (method_decl); + + /* Argument types. */ + for (parms = DECL_ARGUMENTS (func_decl), i = 0; parms; + parms = TREE_CHAIN (parms), i++) + { + /* Process argument qualifiers for user supplied arguments. */ + if (i > 1) + { + encode_type_qualifiers (TREE_PURPOSE (TREE_TYPE (user_args))); + user_args = TREE_CHAIN (user_args); + } + + /* Type. */ + encode_type (TREE_TYPE (parms), + obstack_object_size (&util_obstack), + OBJC_ENCODE_INLINE_DEFS); + + /* Compute offset. */ + sprintf (buf, "%d", forwarding_offset (parms)); + + /* Indicate register. */ + if (offset_is_register) + obstack_1grow (&util_obstack, '+'); + + obstack_grow (&util_obstack, buf, strlen (buf)); + } + + obstack_1grow (&util_obstack, '\0'); + result = get_identifier (obstack_finish (&util_obstack)); + obstack_free (&util_obstack, util_firstobj); + return result; +} + +static tree +generate_descriptor_table (type, name, size, list, proto) + tree type; + char *name; + int size; + tree list; + tree proto; +{ + tree sc_spec, decl_specs, decl, initlist; + + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, type, sc_spec); + + decl = start_decl (synth_id_with_class_suffix (name, proto), + decl_specs, 1, NULL_TREE, NULL_TREE); + + initlist = build_tree_list (NULL_TREE, build_int_2 (size, 0)); + initlist = tree_cons (NULL_TREE, list, initlist); + + finish_decl (decl, build_constructor (type, nreverse (initlist)), + NULL_TREE); + + return decl; +} + +static void +generate_method_descriptors (protocol) /* generate_dispatch_tables */ + tree protocol; +{ + static tree objc_method_prototype_template; + tree initlist, chain, method_list_template; + tree cast, variable_length_type; + int size; + + if (!objc_method_prototype_template) + objc_method_prototype_template = build_method_prototype_template (); + + cast = build_tree_list (build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_PROTOTYPE_LIST))), + NULL_TREE); + variable_length_type = groktypename (cast); + + chain = PROTOCOL_CLS_METHODS (protocol); + if (chain) + { + size = list_length (chain); + + method_list_template + = build_method_prototype_list_template (objc_method_prototype_template, + size); + + initlist + = build_descriptor_table_initializer (objc_method_prototype_template, + chain); + + UOBJC_CLASS_METHODS_decl + = generate_descriptor_table (method_list_template, + "_OBJC_PROTOCOL_CLASS_METHODS", + size, initlist, protocol); + TREE_TYPE (UOBJC_CLASS_METHODS_decl) = variable_length_type; + } + else + UOBJC_CLASS_METHODS_decl = 0; + + chain = PROTOCOL_NST_METHODS (protocol); + if (chain) + { + size = list_length (chain); + + method_list_template + = build_method_prototype_list_template (objc_method_prototype_template, + size); + initlist + = build_descriptor_table_initializer (objc_method_prototype_template, + chain); + + UOBJC_INSTANCE_METHODS_decl + = generate_descriptor_table (method_list_template, + "_OBJC_PROTOCOL_INSTANCE_METHODS", + size, initlist, protocol); + TREE_TYPE (UOBJC_INSTANCE_METHODS_decl) = variable_length_type; + } + else + UOBJC_INSTANCE_METHODS_decl = 0; +} + +static tree +build_tmp_function_decl () +{ + tree decl_specs, expr_decl, parms; + static int xxx = 0; + char buffer[80]; + + /* struct objc_object *objc_xxx (id, SEL, ...); */ + pushlevel (0); + decl_specs = build_tree_list (NULL_TREE, objc_object_reference); + push_parm_decl (build_tree_list + (build_tree_list (decl_specs, + build1 (INDIRECT_REF, NULL_TREE, + NULL_TREE)), + build_tree_list (NULL_TREE, NULL_TREE))); + + decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + expr_decl = build1 (INDIRECT_REF, NULL_TREE, NULL_TREE); + + push_parm_decl (build_tree_list (build_tree_list (decl_specs, expr_decl), + build_tree_list (NULL_TREE, NULL_TREE))); + parms = get_parm_info (0); + poplevel (0, 0, 0); + + decl_specs = build_tree_list (NULL_TREE, objc_object_reference); + sprintf (buffer, "__objc_tmp_%x", xxx++); + expr_decl = build_nt (CALL_EXPR, get_identifier (buffer), parms, NULL_TREE); + expr_decl = build1 (INDIRECT_REF, NULL_TREE, expr_decl); + + return define_decl (expr_decl, decl_specs); +} + +static void +hack_method_prototype (nst_methods, tmp_decl) + tree nst_methods; + tree tmp_decl; +{ + tree parms; + tree parm; + + /* Hack to avoid problem with static typing of self arg. */ + TREE_SET_CODE (nst_methods, CLASS_METHOD_DECL); + start_method_def (nst_methods); + TREE_SET_CODE (nst_methods, INSTANCE_METHOD_DECL); + + if (METHOD_ADD_ARGS (nst_methods) == (tree) 1) + parms = get_parm_info (0); /* we have a `, ...' */ + else + parms = get_parm_info (1); /* place a `void_at_end' */ + + poplevel (0, 0, 0); /* Must be called BEFORE start_function. */ + + /* Usually called from store_parm_decls -> init_function_start. */ + + DECL_ARGUMENTS (tmp_decl) = TREE_PURPOSE (parms); + current_function_decl = tmp_decl; + + { + /* Code taken from start_function. */ + tree restype = TREE_TYPE (TREE_TYPE (tmp_decl)); + /* Promote the value to int before returning it. */ + if (TREE_CODE (restype) == INTEGER_TYPE + && TYPE_PRECISION (restype) < TYPE_PRECISION (integer_type_node)) + restype = integer_type_node; + DECL_RESULT (tmp_decl) = build_decl (RESULT_DECL, 0, restype); + } + + for (parm = DECL_ARGUMENTS (tmp_decl); parm; parm = TREE_CHAIN (parm)) + DECL_CONTEXT (parm) = tmp_decl; + + init_function_start (tmp_decl, "objc-act", 0); + + /* Typically called from expand_function_start for function definitions. */ + assign_parms (tmp_decl, 0); + + /* install return type */ + TREE_TYPE (TREE_TYPE (tmp_decl)) = groktypename (TREE_TYPE (nst_methods)); + +} + +static void +generate_protocol_references (plist) + tree plist; +{ + tree lproto; + + /* Forward declare protocols referenced. */ + for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto)) + { + tree proto = TREE_VALUE (lproto); + + if (TREE_CODE (proto) == PROTOCOL_INTERFACE_TYPE + && PROTOCOL_NAME (proto)) + { + if (! PROTOCOL_FORWARD_DECL (proto)) + build_protocol_reference (proto); + + if (PROTOCOL_LIST (proto)) + generate_protocol_references (PROTOCOL_LIST (proto)); + } + } +} + +static void +generate_protocols () +{ + tree p, tmp_decl, encoding; + tree sc_spec, decl_specs, decl; + tree initlist, protocol_name_expr, refs_decl, refs_expr; + tree cast_type2 = 0; + + tmp_decl = build_tmp_function_decl (); + + if (! objc_protocol_template) + objc_protocol_template = build_protocol_template (); + + /* If a protocol was directly referenced, pull in indirect references. */ + for (p = protocol_chain; p; p = TREE_CHAIN (p)) + if (PROTOCOL_FORWARD_DECL (p) && PROTOCOL_LIST (p)) + generate_protocol_references (PROTOCOL_LIST (p)); + + for (p = protocol_chain; p; p = TREE_CHAIN (p)) + { + tree nst_methods = PROTOCOL_NST_METHODS (p); + tree cls_methods = PROTOCOL_CLS_METHODS (p); + + /* If protocol wasn't referenced, don't generate any code. */ + if (! PROTOCOL_FORWARD_DECL (p)) + continue; + + /* Make sure we link in the Protocol class. */ + add_class_reference (get_identifier (PROTOCOL_OBJECT_CLASS_NAME)); + + while (nst_methods) + { + if (! METHOD_ENCODING (nst_methods)) + { + hack_method_prototype (nst_methods, tmp_decl); + encoding = encode_method_prototype (nst_methods, tmp_decl); + METHOD_ENCODING (nst_methods) = encoding; + } + nst_methods = TREE_CHAIN (nst_methods); + } + + while (cls_methods) + { + if (! METHOD_ENCODING (cls_methods)) + { + hack_method_prototype (cls_methods, tmp_decl); + encoding = encode_method_prototype (cls_methods, tmp_decl); + METHOD_ENCODING (cls_methods) = encoding; + } + + cls_methods = TREE_CHAIN (cls_methods); + } + generate_method_descriptors (p); + + if (PROTOCOL_LIST (p)) + refs_decl = generate_protocol_list (p); + else + refs_decl = 0; + + /* static struct objc_protocol _OBJC_PROTOCOL_<mumble>; */ + + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], + NULL_TREE); + decl_specs = tree_cons (NULL_TREE, objc_protocol_template, sc_spec); + + decl = start_decl (synth_id_with_class_suffix ("_OBJC_PROTOCOL", p), + decl_specs, 1, NULL_TREE, NULL_TREE); + + protocol_name_expr = add_objc_string (PROTOCOL_NAME (p), class_names); + + if (refs_decl) + { + if (!cast_type2) + cast_type2 + = groktypename + (build_tree_list (build_tree_list (NULL_TREE, + objc_protocol_template), + build1 (INDIRECT_REF, NULL_TREE, + build1 (INDIRECT_REF, NULL_TREE, + NULL_TREE)))); + + refs_expr = build_unary_op (ADDR_EXPR, refs_decl, 0); + TREE_TYPE (refs_expr) = cast_type2; + } + else + refs_expr = build_int_2 (0, 0); + + /* UOBJC_INSTANCE_METHODS_decl/UOBJC_CLASS_METHODS_decl are set + by generate_method_descriptors, which is called above. */ + initlist = build_protocol_initializer (TREE_TYPE (decl), + protocol_name_expr, refs_expr, + UOBJC_INSTANCE_METHODS_decl, + UOBJC_CLASS_METHODS_decl); + finish_decl (decl, initlist, NULL_TREE); + + /* Mark the decl as used to avoid "defined but not used" warning. */ + TREE_USED (decl) = 1; + } +} + +static tree +build_protocol_initializer (type, protocol_name, protocol_list, + instance_methods, class_methods) + tree type; + tree protocol_name; + tree protocol_list; + tree instance_methods; + tree class_methods; +{ + tree initlist = NULL_TREE, expr; + static tree cast_type = 0; + + if (!cast_type) + cast_type + = groktypename + (build_tree_list + (build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_CLASS))), + build1 (INDIRECT_REF, NULL_TREE, NULL_TREE))); + + /* Filling the "isa" in with one allows the runtime system to + detect that the version change...should remove before final release. */ + + expr = build_int_2 (PROTOCOL_VERSION, 0); + TREE_TYPE (expr) = cast_type; + initlist = tree_cons (NULL_TREE, expr, initlist); + initlist = tree_cons (NULL_TREE, protocol_name, initlist); + initlist = tree_cons (NULL_TREE, protocol_list, initlist); + + if (!instance_methods) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, instance_methods, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + if (!class_methods) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, class_methods, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + return build_constructor (type, nreverse (initlist)); +} + +/* struct objc_category { + char *category_name; + char *class_name; + struct objc_method_list *instance_methods; + struct objc_method_list *class_methods; + struct objc_protocol_list *protocols; + }; */ + +static void +build_category_template () +{ + tree decl_specs, field_decl, field_decl_chain; + + objc_category_template = start_struct (RECORD_TYPE, + get_identifier (UTAG_CATEGORY)); + /* char *category_name; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("category_name")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* char *class_name; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("class_name")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *instance_methods; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_LIST))); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("instance_methods")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *class_methods; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_LIST))); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("class_methods")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_protocol **protocol_list; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_PROTOCOL))); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("protocol_list")); + field_decl = build1 (INDIRECT_REF, NULL_TREE, field_decl); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_category_template, field_decl_chain, NULL_TREE); +} + +/* struct objc_selector { + void *sel_id; + char *sel_type; + }; */ + +static void +build_selector_template () +{ + + tree decl_specs, field_decl, field_decl_chain; + + objc_selector_template + = start_struct (RECORD_TYPE, get_identifier (UTAG_SELECTOR)); + + /* void *sel_id; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_VOID]); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("sel_id")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* char *sel_type; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("sel_type")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_selector_template, field_decl_chain, NULL_TREE); +} + +/* struct objc_class { + struct objc_class *isa; + struct objc_class *super_class; + char *name; + long version; + long info; + long instance_size; + struct objc_ivar_list *ivars; + struct objc_method_list *methods; + if (flag_next_runtime) + struct objc_cache *cache; + else { + struct sarray *dtable; + struct objc_class *subclass_list; + struct objc_class *sibling_class; + } + struct objc_protocol_list *protocols; + }; */ + +static void +build_class_template () +{ + tree decl_specs, field_decl, field_decl_chain; + + objc_class_template + = start_struct (RECORD_TYPE, get_identifier (UTAG_CLASS)); + + /* struct objc_class *isa; */ + + decl_specs = build_tree_list (NULL_TREE, objc_class_template); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("isa")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* struct objc_class *super_class; */ + + decl_specs = build_tree_list (NULL_TREE, objc_class_template); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("super_class")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* char *name; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("name")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* long version; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("version"); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* long info; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("info"); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* long instance_size; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_LONG]); + field_decl = get_identifier ("instance_size"); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_ivar_list *ivars; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_IVAR_LIST))); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("ivars")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_method_list *methods; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_LIST))); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("methods")); + field_decl + = grokfield (input_filename, lineno, field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + if (flag_next_runtime) + { + /* struct objc_cache *cache; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier ("objc_cache"))); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("cache")); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + } + else + { + /* struct sarray *dtable; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier ("sarray"))); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("dtable")); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_class *subclass_list; */ + + decl_specs = build_tree_list (NULL_TREE, objc_class_template); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("subclass_list")); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_class *sibling_class; */ + + decl_specs = build_tree_list (NULL_TREE, objc_class_template); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("sibling_class")); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + } + + /* struct objc_protocol **protocol_list; */ + + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_PROTOCOL))); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("protocol_list")); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, field_decl); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + + finish_struct (objc_class_template, field_decl_chain, NULL_TREE); +} + +/* Generate appropriate forward declarations for an implementation. */ + +static void +synth_forward_declarations () +{ + tree sc_spec, decl_specs, an_id; + + /* extern struct objc_class _OBJC_CLASS_<my_name>; */ + + an_id = synth_id_with_class_suffix ("_OBJC_CLASS", implementation_context); + + sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_EXTERN]); + decl_specs = tree_cons (NULL_TREE, objc_class_template, sc_spec); + UOBJC_CLASS_decl = define_decl (an_id, decl_specs); + TREE_USED (UOBJC_CLASS_decl) = 1; + DECL_ARTIFICIAL (UOBJC_CLASS_decl) = 1; + + /* extern struct objc_class _OBJC_METACLASS_<my_name>; */ + + an_id = synth_id_with_class_suffix ("_OBJC_METACLASS", + implementation_context); + + UOBJC_METACLASS_decl = define_decl (an_id, decl_specs); + TREE_USED (UOBJC_METACLASS_decl) = 1; + DECL_ARTIFICIAL(UOBJC_METACLASS_decl) = 1; + + /* Pre-build the following entities - for speed/convenience. */ + + an_id = get_identifier ("super_class"); + ucls_super_ref = build_component_ref (UOBJC_CLASS_decl, an_id); + uucls_super_ref = build_component_ref (UOBJC_METACLASS_decl, an_id); +} + +static void +error_with_ivar (message, decl, rawdecl) + char *message; + tree decl; + tree rawdecl; +{ + count_error (0); + + report_error_function (DECL_SOURCE_FILE (decl)); + + fprintf (stderr, "%s:%d: ", + DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl)); + bzero (errbuf, BUFSIZE); + fprintf (stderr, "%s `%s'\n", message, gen_declaration (rawdecl, errbuf)); +} + +#define USERTYPE(t) \ + (TREE_CODE (t) == RECORD_TYPE || TREE_CODE (t) == UNION_TYPE \ + || TREE_CODE (t) == ENUMERAL_TYPE) + +static void +check_ivars (inter, imp) + tree inter; + tree imp; +{ + tree intdecls = CLASS_IVARS (inter); + tree impdecls = CLASS_IVARS (imp); + tree rawintdecls = CLASS_RAW_IVARS (inter); + tree rawimpdecls = CLASS_RAW_IVARS (imp); + + while (1) + { + tree t1, t2; + + if (intdecls == 0 && impdecls == 0) + break; + if (intdecls == 0 || impdecls == 0) + { + error ("inconsistent instance variable specification"); + break; + } + + t1 = TREE_TYPE (intdecls); t2 = TREE_TYPE (impdecls); + + if (!comptypes (t1, t2)) + { + if (DECL_NAME (intdecls) == DECL_NAME (impdecls)) + { + error_with_ivar ("conflicting instance variable type", + impdecls, rawimpdecls); + error_with_ivar ("previous declaration of", + intdecls, rawintdecls); + } + else /* both the type and the name don't match */ + { + error ("inconsistent instance variable specification"); + break; + } + } + + else if (DECL_NAME (intdecls) != DECL_NAME (impdecls)) + { + error_with_ivar ("conflicting instance variable name", + impdecls, rawimpdecls); + error_with_ivar ("previous declaration of", + intdecls, rawintdecls); + } + + intdecls = TREE_CHAIN (intdecls); + impdecls = TREE_CHAIN (impdecls); + rawintdecls = TREE_CHAIN (rawintdecls); + rawimpdecls = TREE_CHAIN (rawimpdecls); + } +} + +/* Set super_type to the data type node for struct objc_super *, + first defining struct objc_super itself. + This needs to be done just once per compilation. */ + +static tree +build_super_template () +{ + tree record, decl_specs, field_decl, field_decl_chain; + + record = start_struct (RECORD_TYPE, get_identifier (UTAG_SUPER)); + + /* struct objc_object *self; */ + + decl_specs = build_tree_list (NULL_TREE, objc_object_reference); + field_decl = get_identifier ("self"); + field_decl = build1 (INDIRECT_REF, NULL_TREE, field_decl); + field_decl = grokfield (input_filename, lineno, + field_decl, decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* struct objc_class *class; */ + + decl_specs = get_identifier (UTAG_CLASS); + decl_specs = build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, decl_specs)); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("class")); + + field_decl = grokfield (input_filename, lineno, + field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (record, field_decl_chain, NULL_TREE); + + /* `struct objc_super *' */ + super_type = groktypename (build_tree_list (build_tree_list (NULL_TREE, + record), + build1 (INDIRECT_REF, + NULL_TREE, NULL_TREE))); + return record; +} + +/* struct objc_ivar { + char *ivar_name; + char *ivar_type; + int ivar_offset; + }; */ + +static tree +build_ivar_template () +{ + tree objc_ivar_id, objc_ivar_record; + tree decl_specs, field_decl, field_decl_chain; + + objc_ivar_id = get_identifier (UTAG_IVAR); + objc_ivar_record = start_struct (RECORD_TYPE, objc_ivar_id); + + /* char *ivar_name; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("ivar_name")); + + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* char *ivar_type; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_CHAR]); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("ivar_type")); + + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* int ivar_offset; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("ivar_offset"); + + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_ivar_record, field_decl_chain, NULL_TREE); + + return objc_ivar_record; +} + +/* struct { + int ivar_count; + struct objc_ivar ivar_list[ivar_count]; + }; */ + +static tree +build_ivar_list_template (list_type, size) + tree list_type; + int size; +{ + tree objc_ivar_list_record; + tree decl_specs, field_decl, field_decl_chain; + + objc_ivar_list_record = start_struct (RECORD_TYPE, NULL_TREE); + + /* int ivar_count; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("ivar_count"); + + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* struct objc_ivar ivar_list[]; */ + + decl_specs = build_tree_list (NULL_TREE, list_type); + field_decl = build_nt (ARRAY_REF, get_identifier ("ivar_list"), + build_int_2 (size, 0)); + + field_decl = grokfield (input_filename, lineno, + field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_ivar_list_record, field_decl_chain, NULL_TREE); + + return objc_ivar_list_record; +} + +/* struct { + int method_next; + int method_count; + struct objc_method method_list[method_count]; + }; */ + +static tree +build_method_list_template (list_type, size) + tree list_type; + int size; +{ + tree objc_ivar_list_record; + tree decl_specs, field_decl, field_decl_chain; + + objc_ivar_list_record = start_struct (RECORD_TYPE, NULL_TREE); + + /* int method_next; */ + + decl_specs + = build_tree_list + (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_PROTOTYPE_LIST))); + field_decl + = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("method_next")); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + /* int method_count; */ + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_INT]); + field_decl = get_identifier ("method_count"); + + field_decl = grokfield (input_filename, lineno, + field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* struct objc_method method_list[]; */ + + decl_specs = build_tree_list (NULL_TREE, list_type); + field_decl = build_nt (ARRAY_REF, get_identifier ("method_list"), + build_int_2 (size, 0)); + + field_decl = grokfield (input_filename, lineno, + field_decl, decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (objc_ivar_list_record, field_decl_chain, NULL_TREE); + + return objc_ivar_list_record; +} + +static tree +build_ivar_list_initializer (type, field_decl) + tree type; + tree field_decl; +{ + tree initlist = NULL_TREE; + + do + { + tree ivar = NULL_TREE; + + /* Set name. */ + if (DECL_NAME (field_decl)) + ivar = tree_cons (NULL_TREE, + add_objc_string (DECL_NAME (field_decl), + meth_var_names), + ivar); + else + /* Unnamed bit-field ivar (yuck). */ + ivar = tree_cons (NULL_TREE, build_int_2 (0, 0), ivar); + + /* Set type. */ + encode_field_decl (field_decl, + obstack_object_size (&util_obstack), + OBJC_ENCODE_DONT_INLINE_DEFS); + + /* Null terminate string. */ + obstack_1grow (&util_obstack, 0); + ivar + = tree_cons + (NULL_TREE, + add_objc_string (get_identifier (obstack_finish (&util_obstack)), + meth_var_types), + ivar); + obstack_free (&util_obstack, util_firstobj); + + /* set offset */ + ivar + = tree_cons + (NULL_TREE, + build_int_2 ((TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field_decl)) + / BITS_PER_UNIT), + 0), + ivar); + + initlist = tree_cons (NULL_TREE, + build_constructor (type, nreverse (ivar)), + initlist); + + field_decl = TREE_CHAIN (field_decl); + } + while (field_decl); + + return build_constructor (build_array_type (type, 0), nreverse (initlist)); +} + +static tree +generate_ivars_list (type, name, size, list) + tree type; + char *name; + int size; + tree list; +{ + tree sc_spec, decl_specs, decl, initlist; + + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, type, sc_spec); + + decl = start_decl (synth_id_with_class_suffix (name, implementation_context), + decl_specs, 1, NULL_TREE, NULL_TREE); + + initlist = build_tree_list (NULL_TREE, build_int_2 (size, 0)); + initlist = tree_cons (NULL_TREE, list, initlist); + + finish_decl (decl, + build_constructor (TREE_TYPE (decl), nreverse (initlist)), + NULL_TREE); + + return decl; +} + +static void +generate_ivar_lists () +{ + tree initlist, ivar_list_template, chain; + tree cast, variable_length_type; + int size; + + generating_instance_variables = 1; + + if (!objc_ivar_template) + objc_ivar_template = build_ivar_template (); + + cast + = build_tree_list + (build_tree_list (NULL_TREE, xref_tag (RECORD_TYPE, + get_identifier (UTAG_IVAR_LIST))), + NULL_TREE); + variable_length_type = groktypename (cast); + + /* Only generate class variables for the root of the inheritance + hierarchy since these will be the same for every class. */ + + if (CLASS_SUPER_NAME (implementation_template) == NULL_TREE + && (chain = TYPE_FIELDS (objc_class_template))) + { + size = list_length (chain); + + ivar_list_template = build_ivar_list_template (objc_ivar_template, size); + initlist = build_ivar_list_initializer (objc_ivar_template, chain); + + UOBJC_CLASS_VARIABLES_decl + = generate_ivars_list (ivar_list_template, "_OBJC_CLASS_VARIABLES", + size, initlist); + TREE_TYPE (UOBJC_CLASS_VARIABLES_decl) = variable_length_type; + } + else + UOBJC_CLASS_VARIABLES_decl = 0; + + chain = CLASS_IVARS (implementation_template); + if (chain) + { + size = list_length (chain); + ivar_list_template = build_ivar_list_template (objc_ivar_template, size); + initlist = build_ivar_list_initializer (objc_ivar_template, chain); + + UOBJC_INSTANCE_VARIABLES_decl + = generate_ivars_list (ivar_list_template, "_OBJC_INSTANCE_VARIABLES", + size, initlist); + TREE_TYPE (UOBJC_INSTANCE_VARIABLES_decl) = variable_length_type; + } + else + UOBJC_INSTANCE_VARIABLES_decl = 0; + + generating_instance_variables = 0; +} + +static tree +build_dispatch_table_initializer (type, entries) + tree type; + tree entries; +{ + tree initlist = NULL_TREE; + + do + { + tree elemlist = NULL_TREE; + + elemlist = tree_cons (NULL_TREE, + build_selector (METHOD_SEL_NAME (entries)), + NULL_TREE); + + elemlist = tree_cons (NULL_TREE, + add_objc_string (METHOD_ENCODING (entries), + meth_var_types), + elemlist); + + elemlist = tree_cons (NULL_TREE, + build_unary_op (ADDR_EXPR, + METHOD_DEFINITION (entries), 1), + elemlist); + + initlist = tree_cons (NULL_TREE, + build_constructor (type, nreverse (elemlist)), + initlist); + + entries = TREE_CHAIN (entries); + } + while (entries); + + return build_constructor (build_array_type (type, 0), nreverse (initlist)); +} + +/* To accomplish method prototyping without generating all kinds of + inane warnings, the definition of the dispatch table entries were + changed from: + + struct objc_method { SEL _cmd; ...; id (*_imp)(); }; + to: + struct objc_method { SEL _cmd; ...; void *_imp; }; */ + +static tree +build_method_template () +{ + tree _SLT_record; + tree decl_specs, field_decl, field_decl_chain; + + _SLT_record = start_struct (RECORD_TYPE, get_identifier (UTAG_METHOD)); + +#ifdef OBJC_INT_SELECTORS + /* unsigned int _cmd; */ + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_UNSIGNED], + NULL_TREE); + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_INT], decl_specs); + field_decl = get_identifier ("_cmd"); +#else /* not OBJC_INT_SELECTORS */ + /* struct objc_selector *_cmd; */ + decl_specs = tree_cons (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR)), + NULL_TREE); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("_cmd")); +#endif /* not OBJC_INT_SELECTORS */ + + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + field_decl_chain = field_decl; + + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_CHAR], NULL_TREE); + field_decl = build1 (INDIRECT_REF, NULL_TREE, + get_identifier ("method_types")); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + /* void *_imp; */ + + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_VOID], NULL_TREE); + field_decl = build1 (INDIRECT_REF, NULL_TREE, get_identifier ("_imp")); + field_decl = grokfield (input_filename, lineno, field_decl, + decl_specs, NULL_TREE); + chainon (field_decl_chain, field_decl); + + finish_struct (_SLT_record, field_decl_chain, NULL_TREE); + + return _SLT_record; +} + + +static tree +generate_dispatch_table (type, name, size, list) + tree type; + char *name; + int size; + tree list; +{ + tree sc_spec, decl_specs, decl, initlist; + + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, type, sc_spec); + + decl = start_decl (synth_id_with_class_suffix (name, implementation_context), + decl_specs, 1, NULL_TREE, NULL_TREE); + + initlist = build_tree_list (NULL_TREE, build_int_2 (0, 0)); + initlist = tree_cons (NULL_TREE, build_int_2 (size, 0), initlist); + initlist = tree_cons (NULL_TREE, list, initlist); + + finish_decl (decl, + build_constructor (TREE_TYPE (decl), nreverse (initlist)), + NULL_TREE); + + return decl; +} + +static void +generate_dispatch_tables () +{ + tree initlist, chain, method_list_template; + tree cast, variable_length_type; + int size; + + if (!objc_method_template) + objc_method_template = build_method_template (); + + cast + = build_tree_list + (build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_METHOD_LIST))), + NULL_TREE); + + variable_length_type = groktypename (cast); + + chain = CLASS_CLS_METHODS (implementation_context); + if (chain) + { + size = list_length (chain); + + method_list_template + = build_method_list_template (objc_method_template, size); + initlist + = build_dispatch_table_initializer (objc_method_template, chain); + + UOBJC_CLASS_METHODS_decl + = generate_dispatch_table (method_list_template, + ((TREE_CODE (implementation_context) + == CLASS_IMPLEMENTATION_TYPE) + ? "_OBJC_CLASS_METHODS" + : "_OBJC_CATEGORY_CLASS_METHODS"), + size, initlist); + TREE_TYPE (UOBJC_CLASS_METHODS_decl) = variable_length_type; + } + else + UOBJC_CLASS_METHODS_decl = 0; + + chain = CLASS_NST_METHODS (implementation_context); + if (chain) + { + size = list_length (chain); + + method_list_template + = build_method_list_template (objc_method_template, size); + initlist + = build_dispatch_table_initializer (objc_method_template, chain); + + if (TREE_CODE (implementation_context) == CLASS_IMPLEMENTATION_TYPE) + UOBJC_INSTANCE_METHODS_decl + = generate_dispatch_table (method_list_template, + "_OBJC_INSTANCE_METHODS", + size, initlist); + else + /* We have a category. */ + UOBJC_INSTANCE_METHODS_decl + = generate_dispatch_table (method_list_template, + "_OBJC_CATEGORY_INSTANCE_METHODS", + size, initlist); + TREE_TYPE (UOBJC_INSTANCE_METHODS_decl) = variable_length_type; + } + else + UOBJC_INSTANCE_METHODS_decl = 0; +} + +static tree +generate_protocol_list (i_or_p) + tree i_or_p; +{ + static tree cast_type = 0; + tree initlist, decl_specs, sc_spec; + tree refs_decl, expr_decl, lproto, e, plist; + int size = 0; + + if (TREE_CODE (i_or_p) == CLASS_INTERFACE_TYPE + || TREE_CODE (i_or_p) == CATEGORY_INTERFACE_TYPE) + plist = CLASS_PROTOCOL_LIST (i_or_p); + else if (TREE_CODE (i_or_p) == PROTOCOL_INTERFACE_TYPE) + plist = PROTOCOL_LIST (i_or_p); + else + abort (); + + if (!cast_type) + cast_type + = groktypename + (build_tree_list + (build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_PROTOCOL))), + build1 (INDIRECT_REF, NULL_TREE, NULL_TREE))); + + /* Compute size. */ + for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto)) + if (TREE_CODE (TREE_VALUE (lproto)) == PROTOCOL_INTERFACE_TYPE + && PROTOCOL_FORWARD_DECL (TREE_VALUE (lproto))) + size++; + + /* Build initializer. */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), NULL_TREE); + + e = build_int_2 (size, 0); + TREE_TYPE (e) = cast_type; + initlist = tree_cons (NULL_TREE, e, initlist); + + for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto)) + { + tree pval = TREE_VALUE (lproto); + + if (TREE_CODE (pval) == PROTOCOL_INTERFACE_TYPE + && PROTOCOL_FORWARD_DECL (pval)) + { + e = build_unary_op (ADDR_EXPR, PROTOCOL_FORWARD_DECL (pval), 0); + initlist = tree_cons (NULL_TREE, e, initlist); + } + } + + /* static struct objc_protocol *refs[n]; */ + + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, xref_tag (RECORD_TYPE, + get_identifier (UTAG_PROTOCOL)), + sc_spec); + + if (TREE_CODE (i_or_p) == PROTOCOL_INTERFACE_TYPE) + expr_decl = build_nt (ARRAY_REF, + synth_id_with_class_suffix ("_OBJC_PROTOCOL_REFS", + i_or_p), + build_int_2 (size + 2, 0)); + else if (TREE_CODE (i_or_p) == CLASS_INTERFACE_TYPE) + expr_decl = build_nt (ARRAY_REF, + synth_id_with_class_suffix ("_OBJC_CLASS_PROTOCOLS", + i_or_p), + build_int_2 (size + 2, 0)); + else if (TREE_CODE (i_or_p) == CATEGORY_INTERFACE_TYPE) + expr_decl + = build_nt (ARRAY_REF, + synth_id_with_class_suffix ("_OBJC_CATEGORY_PROTOCOLS", + i_or_p), + build_int_2 (size + 2, 0)); + + expr_decl = build1 (INDIRECT_REF, NULL_TREE, expr_decl); + + refs_decl = start_decl (expr_decl, decl_specs, 1, NULL_TREE, NULL_TREE); + + finish_decl (refs_decl, build_constructor (TREE_TYPE (refs_decl), + nreverse (initlist)), + NULL_TREE); + + return refs_decl; +} + +static tree +build_category_initializer (type, cat_name, class_name, + instance_methods, class_methods, protocol_list) + tree type; + tree cat_name; + tree class_name; + tree instance_methods; + tree class_methods; + tree protocol_list; +{ + tree initlist = NULL_TREE, expr; + + initlist = tree_cons (NULL_TREE, cat_name, initlist); + initlist = tree_cons (NULL_TREE, class_name, initlist); + + if (!instance_methods) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, instance_methods, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + if (!class_methods) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, class_methods, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + /* protocol_list = */ + if (!protocol_list) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + static tree cast_type2; + + if (!cast_type2) + cast_type2 + = groktypename + (build_tree_list + (build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_PROTOCOL))), + build1 (INDIRECT_REF, NULL_TREE, + build1 (INDIRECT_REF, NULL_TREE, NULL_TREE)))); + + expr = build_unary_op (ADDR_EXPR, protocol_list, 0); + TREE_TYPE (expr) = cast_type2; + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + return build_constructor (type, nreverse (initlist)); +} + +/* struct objc_class { + struct objc_class *isa; + struct objc_class *super_class; + char *name; + long version; + long info; + long instance_size; + struct objc_ivar_list *ivars; + struct objc_method_list *methods; + if (flag_next_runtime) + struct objc_cache *cache; + else { + struct sarray *dtable; + struct objc_class *subclass_list; + struct objc_class *sibling_class; + } + struct objc_protocol_list *protocols; + }; */ + +static tree +build_shared_structure_initializer (type, isa, super, name, size, status, + dispatch_table, ivar_list, protocol_list) + tree type; + tree isa; + tree super; + tree name; + tree size; + int status; + tree dispatch_table; + tree ivar_list; + tree protocol_list; +{ + tree initlist = NULL_TREE, expr; + + /* isa = */ + initlist = tree_cons (NULL_TREE, isa, initlist); + + /* super_class = */ + initlist = tree_cons (NULL_TREE, super, initlist); + + /* name = */ + initlist = tree_cons (NULL_TREE, default_conversion (name), initlist); + + /* version = */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + + /* info = */ + initlist = tree_cons (NULL_TREE, build_int_2 (status, 0), initlist); + + /* instance_size = */ + initlist = tree_cons (NULL_TREE, size, initlist); + + /* objc_ivar_list = */ + if (!ivar_list) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, ivar_list, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + /* objc_method_list = */ + if (!dispatch_table) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + expr = build_unary_op (ADDR_EXPR, dispatch_table, 0); + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + if (flag_next_runtime) + /* method_cache = */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + /* dtable = */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + + /* subclass_list = */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + + /* sibling_class = */ + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + } + + /* protocol_list = */ + if (! protocol_list) + initlist = tree_cons (NULL_TREE, build_int_2 (0, 0), initlist); + else + { + static tree cast_type2; + + if (!cast_type2) + cast_type2 + = groktypename + (build_tree_list + (build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (UTAG_PROTOCOL))), + build1 (INDIRECT_REF, NULL_TREE, + build1 (INDIRECT_REF, NULL_TREE, NULL_TREE)))); + + expr = build_unary_op (ADDR_EXPR, protocol_list, 0); + TREE_TYPE (expr) = cast_type2; + initlist = tree_cons (NULL_TREE, expr, initlist); + } + + return build_constructor (type, nreverse (initlist)); +} + +/* static struct objc_category _OBJC_CATEGORY_<name> = { ... }; */ + +static void +generate_category (cat) + tree cat; +{ + tree sc_spec, decl_specs, decl; + tree initlist, cat_name_expr, class_name_expr; + tree protocol_decl, category; + + add_class_reference (CLASS_NAME (cat)); + cat_name_expr = add_objc_string (CLASS_SUPER_NAME (cat), class_names); + + class_name_expr = add_objc_string (CLASS_NAME (cat), class_names); + + category = CLASS_CATEGORY_LIST (implementation_template); + + /* find the category interface from the class it is associated with */ + while (category) + { + if (CLASS_SUPER_NAME (cat) == CLASS_SUPER_NAME (category)) + break; + category = CLASS_CATEGORY_LIST (category); + } + + if (category && CLASS_PROTOCOL_LIST (category)) + { + generate_protocol_references (CLASS_PROTOCOL_LIST (category)); + protocol_decl = generate_protocol_list (category); + } + else + protocol_decl = 0; + + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + decl_specs = tree_cons (NULL_TREE, objc_category_template, sc_spec); + + decl = start_decl (synth_id_with_class_suffix ("_OBJC_CATEGORY", + implementation_context), + decl_specs, 1, NULL_TREE, NULL_TREE); + + initlist = build_category_initializer (TREE_TYPE (decl), + cat_name_expr, class_name_expr, + UOBJC_INSTANCE_METHODS_decl, + UOBJC_CLASS_METHODS_decl, + protocol_decl); + + TREE_USED (decl) = 1; + finish_decl (decl, initlist, NULL_TREE); +} + +/* static struct objc_class _OBJC_METACLASS_Foo={ ... }; + static struct objc_class _OBJC_CLASS_Foo={ ... }; */ + +static void +generate_shared_structures () +{ + tree sc_spec, decl_specs, decl; + tree name_expr, super_expr, root_expr; + tree my_root_id = NULL_TREE, my_super_id = NULL_TREE; + tree cast_type, initlist, protocol_decl; + + my_super_id = CLASS_SUPER_NAME (implementation_template); + if (my_super_id) + { + add_class_reference (my_super_id); + + /* Compute "my_root_id" - this is required for code generation. + the "isa" for all meta class structures points to the root of + the inheritance hierarchy (e.g. "__Object")... */ + my_root_id = my_super_id; + do + { + tree my_root_int = lookup_interface (my_root_id); + + if (my_root_int && CLASS_SUPER_NAME (my_root_int)) + my_root_id = CLASS_SUPER_NAME (my_root_int); + else + break; + } + while (1); + } + else + /* No super class. */ + my_root_id = CLASS_NAME (implementation_template); + + cast_type + = groktypename (build_tree_list (build_tree_list (NULL_TREE, + objc_class_template), + build1 (INDIRECT_REF, + NULL_TREE, NULL_TREE))); + + name_expr = add_objc_string (CLASS_NAME (implementation_template), + class_names); + + /* Install class `isa' and `super' pointers at runtime. */ + if (my_super_id) + { + super_expr = add_objc_string (my_super_id, class_names); + super_expr = build_c_cast (cast_type, super_expr); /* cast! */ + } + else + super_expr = build_int_2 (0, 0); + + root_expr = add_objc_string (my_root_id, class_names); + root_expr = build_c_cast (cast_type, root_expr); /* cast! */ + + if (CLASS_PROTOCOL_LIST (implementation_template)) + { + generate_protocol_references + (CLASS_PROTOCOL_LIST (implementation_template)); + protocol_decl = generate_protocol_list (implementation_template); + } + else + protocol_decl = 0; + + /* static struct objc_class _OBJC_METACLASS_Foo = { ... }; */ + + sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]); + decl_specs = tree_cons (NULL_TREE, objc_class_template, sc_spec); + + decl = start_decl (DECL_NAME (UOBJC_METACLASS_decl), decl_specs, 1, + NULL_TREE, NULL_TREE); + + initlist + = build_shared_structure_initializer + (TREE_TYPE (decl), + root_expr, super_expr, name_expr, + build_int_2 ((TREE_INT_CST_LOW (TYPE_SIZE (objc_class_template)) + / BITS_PER_UNIT), + 0), + 2 /*CLS_META*/, + UOBJC_CLASS_METHODS_decl, + UOBJC_CLASS_VARIABLES_decl, + protocol_decl); + + finish_decl (decl, initlist, NULL_TREE); + + /* static struct objc_class _OBJC_CLASS_Foo={ ... }; */ + + decl = start_decl (DECL_NAME (UOBJC_CLASS_decl), decl_specs, 1, + NULL_TREE, NULL_TREE); + + initlist + = build_shared_structure_initializer + (TREE_TYPE (decl), + build_unary_op (ADDR_EXPR, UOBJC_METACLASS_decl, 0), + super_expr, name_expr, + build_int_2 + ((TREE_INT_CST_LOW + (TYPE_SIZE (CLASS_STATIC_TEMPLATE (implementation_template))) + / BITS_PER_UNIT), + 0), + 1 /*CLS_FACTORY*/, + UOBJC_INSTANCE_METHODS_decl, + UOBJC_INSTANCE_VARIABLES_decl, + protocol_decl); + + finish_decl (decl, initlist, NULL_TREE); +} + +static tree +synth_id_with_class_suffix (preamble, ctxt) + char *preamble; + tree ctxt; +{ + char *string; + if (TREE_CODE (ctxt) == CLASS_IMPLEMENTATION_TYPE + || TREE_CODE (ctxt) == CLASS_INTERFACE_TYPE) + { + char *class_name + = IDENTIFIER_POINTER (CLASS_NAME (implementation_context)); + string = (char *) alloca (strlen (preamble) + strlen (class_name) + 3); + sprintf (string, "%s_%s", preamble, + IDENTIFIER_POINTER (CLASS_NAME (ctxt))); + } + else if (TREE_CODE (ctxt) == CATEGORY_IMPLEMENTATION_TYPE + || TREE_CODE (ctxt) == CATEGORY_INTERFACE_TYPE) + { + /* We have a category. */ + char *class_name + = IDENTIFIER_POINTER (CLASS_NAME (implementation_context)); + char *class_super_name + = IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context)); + string = (char *) alloca (strlen (preamble) + + strlen (class_name) + + strlen (class_super_name) + + 3); + sprintf (string, "%s_%s_%s", preamble, class_name, class_super_name); + } + else if (TREE_CODE (ctxt) == PROTOCOL_INTERFACE_TYPE) + { + char *protocol_name = IDENTIFIER_POINTER (PROTOCOL_NAME (ctxt)); + string + = (char *) alloca (strlen (preamble) + strlen (protocol_name) + 3); + sprintf (string, "%s_%s", preamble, protocol_name); + } + return get_identifier (string); +} + +static int +is_objc_type_qualifier (node) + tree node; +{ + return (TREE_CODE (node) == IDENTIFIER_NODE + && (node == ridpointers [(int) RID_CONST] + || node == ridpointers [(int) RID_VOLATILE] + || node == ridpointers [(int) RID_IN] + || node == ridpointers [(int) RID_OUT] + || node == ridpointers [(int) RID_INOUT] + || node == ridpointers [(int) RID_BYCOPY] + || node == ridpointers [(int) RID_ONEWAY])); +} + +/* If type is empty or only type qualifiers are present, add default + type of id (otherwise grokdeclarator will default to int). */ + +static tree +adjust_type_for_id_default (type) + tree type; +{ + tree declspecs, chain; + + if (!type) + return build_tree_list (build_tree_list (NULL_TREE, objc_object_reference), + build1 (INDIRECT_REF, NULL_TREE, NULL_TREE)); + + declspecs = TREE_PURPOSE (type); + + /* Determine if a typespec is present. */ + for (chain = declspecs; + chain; + chain = TREE_CHAIN (chain)) + { + if (!is_objc_type_qualifier (TREE_VALUE (chain))) + return type; + } + + return build_tree_list (tree_cons (NULL_TREE, objc_object_reference, + declspecs), + build1 (INDIRECT_REF, NULL_TREE, NULL_TREE)); +} + +/* Usage: + keyworddecl: + selector ':' '(' typename ')' identifier + + Purpose: + Transform an Objective-C keyword argument into + the C equivalent parameter declarator. + + In: key_name, an "identifier_node" (optional). + arg_type, a "tree_list" (optional). + arg_name, an "identifier_node". + + Note: It would be really nice to strongly type the preceding + arguments in the function prototype; however, then I + could not use the "accessor" macros defined in "tree.h". + + Out: an instance of "keyword_decl". */ + +tree +build_keyword_decl (key_name, arg_type, arg_name) + tree key_name; + tree arg_type; + tree arg_name; +{ + tree keyword_decl; + + /* If no type is specified, default to "id". */ + arg_type = adjust_type_for_id_default (arg_type); + + keyword_decl = make_node (KEYWORD_DECL); + + TREE_TYPE (keyword_decl) = arg_type; + KEYWORD_ARG_NAME (keyword_decl) = arg_name; + KEYWORD_KEY_NAME (keyword_decl) = key_name; + + return keyword_decl; +} + +/* Given a chain of keyword_decl's, synthesize the full keyword selector. */ + +static tree +build_keyword_selector (selector) + tree selector; +{ + int len = 0; + tree key_chain, key_name; + char *buf; + + for (key_chain = selector; key_chain; key_chain = TREE_CHAIN (key_chain)) + { + if (TREE_CODE (selector) == KEYWORD_DECL) + key_name = KEYWORD_KEY_NAME (key_chain); + else if (TREE_CODE (selector) == TREE_LIST) + key_name = TREE_PURPOSE (key_chain); + + if (key_name) + len += IDENTIFIER_LENGTH (key_name) + 1; + else + /* Just a ':' arg. */ + len++; + } + + buf = (char *)alloca (len + 1); + bzero (buf, len + 1); + + for (key_chain = selector; key_chain; key_chain = TREE_CHAIN (key_chain)) + { + if (TREE_CODE (selector) == KEYWORD_DECL) + key_name = KEYWORD_KEY_NAME (key_chain); + else if (TREE_CODE (selector) == TREE_LIST) + key_name = TREE_PURPOSE (key_chain); + + if (key_name) + strcat (buf, IDENTIFIER_POINTER (key_name)); + strcat (buf, ":"); + } + + return get_identifier (buf); +} + +/* Used for declarations and definitions. */ + +tree +build_method_decl (code, ret_type, selector, add_args) + enum tree_code code; + tree ret_type; + tree selector; + tree add_args; +{ + tree method_decl; + + /* If no type is specified, default to "id". */ + ret_type = adjust_type_for_id_default (ret_type); + + method_decl = make_node (code); + TREE_TYPE (method_decl) = ret_type; + + /* If we have a keyword selector, create an identifier_node that + represents the full selector name (`:' included)... */ + if (TREE_CODE (selector) == KEYWORD_DECL) + { + METHOD_SEL_NAME (method_decl) = build_keyword_selector (selector); + METHOD_SEL_ARGS (method_decl) = selector; + METHOD_ADD_ARGS (method_decl) = add_args; + } + else + { + METHOD_SEL_NAME (method_decl) = selector; + METHOD_SEL_ARGS (method_decl) = NULL_TREE; + METHOD_ADD_ARGS (method_decl) = NULL_TREE; + } + + return method_decl; +} + +#define METHOD_DEF 0 +#define METHOD_REF 1 + +/* Used by `build_message_expr' and `comp_method_types'. Return an + argument list for method METH. CONTEXT is either METHOD_DEF or + METHOD_REF, saying whether we are trying to define a method or call + one. SUPERFLAG says this is for a send to super; this makes a + difference for the NeXT calling sequence in which the lookup and + the method call are done together. */ + +static tree +get_arg_type_list (meth, context, superflag) + tree meth; + int context; + int superflag; +{ + tree arglist, akey; + + /* Receiver type. */ + if (flag_next_runtime && superflag) + arglist = build_tree_list (NULL_TREE, super_type); + else if (context == METHOD_DEF) + arglist = build_tree_list (NULL_TREE, TREE_TYPE (self_decl)); + else + arglist = build_tree_list (NULL_TREE, id_type); + + /* Selector type - will eventually change to `int'. */ + chainon (arglist, build_tree_list (NULL_TREE, selector_type)); + + /* Build a list of argument types. */ + for (akey = METHOD_SEL_ARGS (meth); akey; akey = TREE_CHAIN (akey)) + { + tree arg_decl = groktypename_in_parm_context (TREE_TYPE (akey)); + chainon (arglist, build_tree_list (NULL_TREE, TREE_TYPE (arg_decl))); + } + + if (METHOD_ADD_ARGS (meth) == (tree)1) + /* We have a `, ...' immediately following the selector, + finalize the arglist...simulate get_parm_info (0). */ + ; + else if (METHOD_ADD_ARGS (meth)) + { + /* we have a variable length selector */ + tree add_arg_list = TREE_CHAIN (METHOD_ADD_ARGS (meth)); + chainon (arglist, add_arg_list); + } + else + /* finalize the arglist...simulate get_parm_info (1) */ + chainon (arglist, build_tree_list (NULL_TREE, void_type_node)); + + return arglist; +} + +static tree +check_duplicates (hsh) + hash hsh; +{ + tree meth = NULL_TREE; + + if (hsh) + { + meth = hsh->key; + + if (hsh->list) + { + /* We have two methods with the same name and different types. */ + attr loop; + char type = (TREE_CODE (meth) == INSTANCE_METHOD_DECL) ? '-' : '+'; + + warning ("multiple declarations for method `%s'", + IDENTIFIER_POINTER (METHOD_SEL_NAME (meth))); + + warn_with_method ("using", type, meth); + for (loop = hsh->list; loop; loop = loop->next) + warn_with_method ("also found", type, loop->value); + } + } + return meth; +} + +/* If RECEIVER is a class reference, return the identifier node for the + referenced class. RECEIVER is created by get_class_reference, so we + check the exact form created depending on which runtimes are used. */ + +static tree +receiver_is_class_object (receiver) + tree receiver; +{ + tree chain, exp, arg; + if (flag_next_runtime) + { + /* The receiver is a variable created by build_class_reference_decl. */ + if (TREE_CODE (receiver) == VAR_DECL + && TREE_TYPE (receiver) == objc_class_type) + /* Look up the identifier. */ + for (chain = cls_ref_chain; chain; chain = TREE_CHAIN (chain)) + if (TREE_PURPOSE (chain) == receiver) + return TREE_VALUE (chain); + } + else + { + /* The receiver is a function call that returns an id. Check if + it is a call to objc_getClass, if so, pick up the class name. */ + if ((exp = TREE_OPERAND (receiver, 0)) + && TREE_CODE (exp) == ADDR_EXPR + && (exp = TREE_OPERAND (exp, 0)) + && TREE_CODE (exp) == FUNCTION_DECL + && exp == objc_get_class_decl + /* we have a call to objc_getClass! */ + && (arg = TREE_OPERAND (receiver, 1)) + && TREE_CODE (arg) == TREE_LIST + && (arg = TREE_VALUE (arg))) + { + STRIP_NOPS (arg); + if (TREE_CODE (arg) == ADDR_EXPR + && (arg = TREE_OPERAND (arg, 0)) + && TREE_CODE (arg) == STRING_CST) + /* Finally, we have the class name. */ + return get_identifier (TREE_STRING_POINTER (arg)); + } + } + return 0; +} + +/* If we are currently building a message expr, this holds + the identifier of the selector of the message. This is + used when printing warnings about argument mismatches. */ + +static tree building_objc_message_expr = 0; + +tree +maybe_building_objc_message_expr () +{ + return building_objc_message_expr; +} + +/* Construct an expression for sending a message. + MESS has the object to send to in TREE_PURPOSE + and the argument list (including selector) in TREE_VALUE. + + (*(<abstract_decl>(*)())_msg)(receiver, selTransTbl[n], ...); + (*(<abstract_decl>(*)())_msgSuper)(receiver, selTransTbl[n], ...); */ + +tree +build_message_expr (mess) + tree mess; +{ + tree receiver = TREE_PURPOSE (mess); + tree selector, self_object; + tree rtype, sel_name; + tree args = TREE_VALUE (mess); + tree method_params = NULL_TREE; + tree method_prototype = NULL_TREE; + tree retval; + int statically_typed = 0, statically_allocated = 0; + tree class_ident = 0; + + /* 1 if this is sending to the superclass. */ + int super; + + if (!doing_objc_thang) + objc_fatal (); + + if (TREE_CODE (receiver) == ERROR_MARK) + return error_mark_node; + + /* Determine receiver type. */ + rtype = TREE_TYPE (receiver); + super = IS_SUPER (rtype); + + if (! super) + { + if (TREE_STATIC_TEMPLATE (rtype)) + statically_allocated = 1; + else if (TREE_CODE (rtype) == POINTER_TYPE + && TREE_STATIC_TEMPLATE (TREE_TYPE (rtype))) + statically_typed = 1; + else if ((flag_next_runtime + || (TREE_CODE (receiver) == CALL_EXPR && IS_ID (rtype))) + && (class_ident = receiver_is_class_object (receiver))) + ; + else if (! IS_ID (rtype) + /* Allow any type that matches objc_class_type. */ + && ! comptypes (rtype, objc_class_type)) + { + bzero (errbuf, BUFSIZE); + warning ("invalid receiver type `%s'", + gen_declaration (rtype, errbuf)); + } + + if (statically_allocated) + receiver = build_unary_op (ADDR_EXPR, receiver, 0); + + /* Don't evaluate the receiver twice. */ + receiver = save_expr (receiver); + self_object = receiver; + } + else + /* If sending to `super', use current self as the object. */ + self_object = self_decl; + + /* Obtain the full selector name. */ + + if (TREE_CODE (args) == IDENTIFIER_NODE) + /* A unary selector. */ + sel_name = args; + else if (TREE_CODE (args) == TREE_LIST) + sel_name = build_keyword_selector (args); + + /* Build the parameter list to give to the method. */ + + method_params = NULL_TREE; + if (TREE_CODE (args) == TREE_LIST) + { + tree chain = args, prev = NULL_TREE; + + /* We have a keyword selector--check for comma expressions. */ + while (chain) + { + tree element = TREE_VALUE (chain); + + /* We have a comma expression, must collapse... */ + if (TREE_CODE (element) == TREE_LIST) + { + if (prev) + TREE_CHAIN (prev) = element; + else + args = element; + } + prev = chain; + chain = TREE_CHAIN (chain); + } + method_params = args; + } + + /* Determine operation return type. */ + + if (IS_SUPER (rtype)) + { + tree iface; + + if (CLASS_SUPER_NAME (implementation_template)) + { + iface + = lookup_interface (CLASS_SUPER_NAME (implementation_template)); + + if (TREE_CODE (method_context) == INSTANCE_METHOD_DECL) + method_prototype = lookup_instance_method_static (iface, sel_name); + else + method_prototype = lookup_class_method_static (iface, sel_name); + + if (iface && !method_prototype) + warning ("`%s' does not respond to `%s'", + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_template)), + IDENTIFIER_POINTER (sel_name)); + } + else + { + error ("no super class declared in interface for `%s'", + IDENTIFIER_POINTER (CLASS_NAME (implementation_template))); + return error_mark_node; + } + + } + else if (statically_allocated) + { + tree ctype = TREE_TYPE (rtype); + tree iface = lookup_interface (TYPE_NAME (rtype)); + + if (iface) + method_prototype = lookup_instance_method_static (iface, sel_name); + + if (! method_prototype && TYPE_PROTOCOL_LIST (ctype)) + method_prototype + = lookup_method_in_protocol_list (TYPE_PROTOCOL_LIST (ctype), + sel_name, 0); + + if (!method_prototype) + warning ("`%s' does not respond to `%s'", + IDENTIFIER_POINTER (TYPE_NAME (rtype)), + IDENTIFIER_POINTER (sel_name)); + } + else if (statically_typed) + { + tree ctype = TREE_TYPE (rtype); + + /* `self' is now statically_typed. All methods should be visible + within the context of the implementation. */ + if (implementation_context + && CLASS_NAME (implementation_context) == TYPE_NAME (ctype)) + { + method_prototype + = lookup_instance_method_static (implementation_template, + sel_name); + + if (! method_prototype && TYPE_PROTOCOL_LIST (ctype)) + method_prototype + = lookup_method_in_protocol_list (TYPE_PROTOCOL_LIST (ctype), + sel_name, 0); + + if (! method_prototype + && implementation_template != implementation_context) + /* The method is not published in the interface. Check + locally. */ + method_prototype + = lookup_method (CLASS_NST_METHODS (implementation_context), + sel_name); + } + else + { + tree iface; + + if ((iface = lookup_interface (TYPE_NAME (ctype)))) + method_prototype = lookup_instance_method_static (iface, sel_name); + + if (! method_prototype) + { + tree protocol_list = TYPE_PROTOCOL_LIST (ctype); + if (protocol_list) + method_prototype + = lookup_method_in_protocol_list (protocol_list, + sel_name, 0); + } + } + + if (!method_prototype) + warning ("`%s' does not respond to `%s'", + IDENTIFIER_POINTER (TYPE_NAME (ctype)), + IDENTIFIER_POINTER (sel_name)); + } + else if (class_ident) + { + if (implementation_context + && CLASS_NAME (implementation_context) == class_ident) + { + method_prototype + = lookup_class_method_static (implementation_template, sel_name); + + if (!method_prototype + && implementation_template != implementation_context) + /* The method is not published in the interface. Check + locally. */ + method_prototype + = lookup_method (CLASS_CLS_METHODS (implementation_context), + sel_name); + } + else + { + tree iface; + + if ((iface = lookup_interface (class_ident))) + method_prototype = lookup_class_method_static (iface, sel_name); + } + + if (!method_prototype) + { + warning ("cannot find class (factory) method."); + warning ("return type for `%s' defaults to id", + IDENTIFIER_POINTER (sel_name)); + } + } + else if (IS_PROTOCOL_QUALIFIED_ID (rtype)) + { + /* An anonymous object that has been qualified with a protocol. */ + + tree protocol_list = TYPE_PROTOCOL_LIST (rtype); + + method_prototype = lookup_method_in_protocol_list (protocol_list, + sel_name, 0); + + if (!method_prototype) + { + hash hsh; + + warning ("method `%s' not implemented by protocol.", + IDENTIFIER_POINTER (sel_name)); + + /* Try and find the method signature in the global pools. */ + + if (!(hsh = hash_lookup (nst_method_hash_list, sel_name))) + hsh = hash_lookup (cls_method_hash_list, sel_name); + + if (!(method_prototype = check_duplicates (hsh))) + warning ("return type defaults to id"); + } + } + else + { + hash hsh; + + /* We think we have an instance...loophole: extern id Object; */ + hsh = hash_lookup (nst_method_hash_list, sel_name); + if (!hsh) + /* For various loopholes, like sending messages to self in a + factory context. */ + hsh = hash_lookup (cls_method_hash_list, sel_name); + + method_prototype = check_duplicates (hsh); + if (!method_prototype) + { + warning ("cannot find method."); + warning ("return type for `%s' defaults to id", + IDENTIFIER_POINTER (sel_name)); + } + } + + /* Save the selector name for printing error messages. */ + building_objc_message_expr = sel_name; + + /* Build the parameters list for looking up the method. + These are the object itself and the selector. */ + + if (flag_typed_selectors) + selector = build_typed_selector_reference (sel_name, method_prototype); + else + selector = build_selector_reference (sel_name); + + retval = build_objc_method_call (super, method_prototype, + receiver, self_object, + selector, method_params); + + building_objc_message_expr = 0; + + return retval; +} + +/* Build a tree expression to send OBJECT the operation SELECTOR, + looking up the method on object LOOKUP_OBJECT (often same as OBJECT), + assuming the method has prototype METHOD_PROTOTYPE. + (That is an INSTANCE_METHOD_DECL or CLASS_METHOD_DECL.) + Use METHOD_PARAMS as list of args to pass to the method. + If SUPER_FLAG is nonzero, we look up the superclass's method. */ + +static tree +build_objc_method_call (super_flag, method_prototype, lookup_object, object, + selector, method_params) + int super_flag; + tree method_prototype, lookup_object, object, selector, method_params; +{ + tree sender = (super_flag ? umsg_super_decl : umsg_decl); + tree rcv_p = (super_flag + ? build_pointer_type (xref_tag (RECORD_TYPE, + get_identifier (TAG_SUPER))) + : id_type); + + if (flag_next_runtime) + { + if (! method_prototype) + { + method_params = tree_cons (NULL_TREE, lookup_object, + tree_cons (NULL_TREE, selector, + method_params)); + assemble_external (sender); + return build_function_call (sender, method_params); + } + else + { + /* This is a real kludge, but it is used only for the Next. + Clobber the data type of SENDER temporarily to accept + all the arguments for this operation, and to return + whatever this operation returns. */ + tree arglist = NULL_TREE; + tree retval; + + /* Save the proper contents of SENDER's data type. */ + tree savarg = TYPE_ARG_TYPES (TREE_TYPE (sender)); + tree savret = TREE_TYPE (TREE_TYPE (sender)); + + /* Install this method's argument types. */ + arglist = get_arg_type_list (method_prototype, METHOD_REF, + super_flag); + TYPE_ARG_TYPES (TREE_TYPE (sender)) = arglist; + + /* Install this method's return type. */ + TREE_TYPE (TREE_TYPE (sender)) + = groktypename (TREE_TYPE (method_prototype)); + + /* Call SENDER with all the parameters. This will do type + checking using the arg types for this method. */ + method_params = tree_cons (NULL_TREE, lookup_object, + tree_cons (NULL_TREE, selector, + method_params)); + assemble_external (sender); + retval = build_function_call (sender, method_params); + + /* Restore SENDER's return/argument types. */ + TYPE_ARG_TYPES (TREE_TYPE (sender)) = savarg; + TREE_TYPE (TREE_TYPE (sender)) = savret; + return retval; + } + } + else + { + /* This is the portable way. + First call the lookup function to get a pointer to the method, + then cast the pointer, then call it with the method arguments. */ + tree method; + + /* Avoid trouble since we may evaluate each of these twice. */ + object = save_expr (object); + selector = save_expr (selector); + + lookup_object = build_c_cast (rcv_p, lookup_object); + + assemble_external (sender); + method + = build_function_call (sender, + tree_cons (NULL_TREE, lookup_object, + tree_cons (NULL_TREE, selector, + NULL_TREE))); + + /* If we have a method prototype, construct the data type this + method needs, and cast what we got from SENDER into a pointer + to that type. */ + if (method_prototype) + { + tree arglist = get_arg_type_list (method_prototype, METHOD_REF, + super_flag); + tree valtype = groktypename (TREE_TYPE (method_prototype)); + tree fake_function_type = build_function_type (valtype, arglist); + TREE_TYPE (method) = build_pointer_type (fake_function_type); + } + else + TREE_TYPE (method) + = build_pointer_type (build_function_type (ptr_type_node, NULL_TREE)); + + /* Pass the object to the method. */ + assemble_external (method); + return build_function_call (method, + tree_cons (NULL_TREE, object, + tree_cons (NULL_TREE, selector, + method_params))); + } +} + +static void +build_protocol_reference (p) + tree p; +{ + tree decl, ident, ptype; + + push_obstacks_nochange (); + end_temporary_allocation (); + + /* extern struct objc_protocol _OBJC_PROTOCOL_<mumble>; */ + + ident = synth_id_with_class_suffix ("_OBJC_PROTOCOL", p); + ptype + = groktypename (build_tree_list (build_tree_list (NULL_TREE, + objc_protocol_template), + NULL_TREE)); + + if (IDENTIFIER_GLOBAL_VALUE (ident)) + decl = IDENTIFIER_GLOBAL_VALUE (ident); /* Set by pushdecl. */ + else + { + decl = build_decl (VAR_DECL, ident, ptype); + DECL_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + TREE_USED (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + + make_decl_rtl (decl, 0, 1); + pushdecl_top_level (decl); + } + + PROTOCOL_FORWARD_DECL (p) = decl; + pop_obstacks (); +} + +tree +build_protocol_expr (protoname) + tree protoname; +{ + tree expr; + tree p; + + if (!doing_objc_thang) + objc_fatal (); + + p = lookup_protocol (protoname); + + if (!p) + { + error ("Cannot find protocol declaration for `%s'", + IDENTIFIER_POINTER (protoname)); + return error_mark_node; + } + + if (!PROTOCOL_FORWARD_DECL (p)) + build_protocol_reference (p); + + expr = build_unary_op (ADDR_EXPR, PROTOCOL_FORWARD_DECL (p), 0); + + TREE_TYPE (expr) = protocol_type; + + return expr; +} + +tree +build_selector_expr (selnamelist) + tree selnamelist; +{ + tree selname; + + if (!doing_objc_thang) + objc_fatal (); + + /* Obtain the full selector name. */ + if (TREE_CODE (selnamelist) == IDENTIFIER_NODE) + /* A unary selector. */ + selname = selnamelist; + else if (TREE_CODE (selnamelist) == TREE_LIST) + selname = build_keyword_selector (selnamelist); + + if (flag_typed_selectors) + return build_typed_selector_reference (selname, 0); + else + return build_selector_reference (selname); +} + +tree +build_encode_expr (type) + tree type; +{ + tree result; + char *string; + + if (!doing_objc_thang) + objc_fatal (); + + encode_type (type, obstack_object_size (&util_obstack), + OBJC_ENCODE_INLINE_DEFS); + obstack_1grow (&util_obstack, 0); /* null terminate string */ + string = obstack_finish (&util_obstack); + + /* Synthesize a string that represents the encoded struct/union. */ + result = my_build_string (strlen (string) + 1, string); + obstack_free (&util_obstack, util_firstobj); + return result; +} + +tree +build_ivar_reference (id) + tree id; +{ + if (TREE_CODE (method_context) == CLASS_METHOD_DECL) + { + /* Historically, a class method that produced objects (factory + method) would assign `self' to the instance that it + allocated. This would effectively turn the class method into + an instance method. Following this assignment, the instance + variables could be accessed. That practice, while safe, + violates the simple rule that a class method should not refer + to an instance variable. It's better to catch the cases + where this is done unknowingly than to support the above + paradigm. */ + warning ("instance variable `%s' accessed in class method", + IDENTIFIER_POINTER (id)); + TREE_TYPE (self_decl) = instance_type; /* cast */ + } + + return build_component_ref (build_indirect_ref (self_decl, "->"), id); +} + +#define HASH_ALLOC_LIST_SIZE 170 +#define ATTR_ALLOC_LIST_SIZE 170 +#define SIZEHASHTABLE 257 + +/* make positive */ +#define HASHFUNCTION(key) ((HOST_WIDE_INT) key & 0x7fffffff) + +static void +hash_init () +{ + nst_method_hash_list = (hash *)xmalloc (SIZEHASHTABLE * sizeof (hash)); + cls_method_hash_list = (hash *)xmalloc (SIZEHASHTABLE * sizeof (hash)); + + if (!nst_method_hash_list || !cls_method_hash_list) + perror ("unable to allocate space in objc-tree.c"); + else + { + int i; + + for (i = 0; i < SIZEHASHTABLE; i++) + { + nst_method_hash_list[i] = 0; + cls_method_hash_list[i] = 0; + } + } +} + +static void +hash_enter (hashlist, method) + hash *hashlist; + tree method; +{ + static hash hash_alloc_list = 0; + static int hash_alloc_index = 0; + hash obj; + int slot = HASHFUNCTION (METHOD_SEL_NAME (method)) % SIZEHASHTABLE; + + if (! hash_alloc_list || hash_alloc_index >= HASH_ALLOC_LIST_SIZE) + { + hash_alloc_index = 0; + hash_alloc_list = (hash) xmalloc (sizeof (struct hashed_entry) + * HASH_ALLOC_LIST_SIZE); + if (! hash_alloc_list) + perror ("unable to allocate in objc-tree.c"); + } + obj = &hash_alloc_list[hash_alloc_index++]; + obj->list = 0; + obj->next = hashlist[slot]; + obj->key = method; + + hashlist[slot] = obj; /* append to front */ +} + +static hash +hash_lookup (hashlist, sel_name) + hash *hashlist; + tree sel_name; +{ + hash target; + + target = hashlist[HASHFUNCTION (sel_name) % SIZEHASHTABLE]; + + while (target) + { + if (sel_name == METHOD_SEL_NAME (target->key)) + return target; + + target = target->next; + } + return 0; +} + +static void +hash_add_attr (entry, value) + hash entry; + tree value; +{ + static attr attr_alloc_list = 0; + static int attr_alloc_index = 0; + attr obj; + + if (! attr_alloc_list || attr_alloc_index >= ATTR_ALLOC_LIST_SIZE) + { + attr_alloc_index = 0; + attr_alloc_list = (attr) xmalloc (sizeof (struct hashed_attribute) + * ATTR_ALLOC_LIST_SIZE); + if (! attr_alloc_list) + perror ("unable to allocate in objc-tree.c"); + } + obj = &attr_alloc_list[attr_alloc_index++]; + obj->next = entry->list; + obj->value = value; + + entry->list = obj; /* append to front */ +} + +static tree +lookup_method (mchain, method) + tree mchain; + tree method; +{ + tree key; + + if (TREE_CODE (method) == IDENTIFIER_NODE) + key = method; + else + key = METHOD_SEL_NAME (method); + + while (mchain) + { + if (METHOD_SEL_NAME (mchain) == key) + return mchain; + mchain = TREE_CHAIN (mchain); + } + return NULL_TREE; +} + +static tree +lookup_instance_method_static (interface, ident) + tree interface; + tree ident; +{ + tree inter = interface; + tree chain = CLASS_NST_METHODS (inter); + tree meth = NULL_TREE; + + do + { + if ((meth = lookup_method (chain, ident))) + return meth; + + if (CLASS_CATEGORY_LIST (inter)) + { + tree category = CLASS_CATEGORY_LIST (inter); + chain = CLASS_NST_METHODS (category); + + do + { + if ((meth = lookup_method (chain, ident))) + return meth; + + /* Check for instance methods in protocols in categories. */ + if (CLASS_PROTOCOL_LIST (category)) + { + if ((meth = (lookup_method_in_protocol_list + (CLASS_PROTOCOL_LIST (category), ident, 0)))) + return meth; + } + + if ((category = CLASS_CATEGORY_LIST (category))) + chain = CLASS_NST_METHODS (category); + } + while (category); + } + + if (CLASS_PROTOCOL_LIST (inter)) + { + if ((meth = (lookup_method_in_protocol_list + (CLASS_PROTOCOL_LIST (inter), ident, 0)))) + return meth; + } + + if ((inter = lookup_interface (CLASS_SUPER_NAME (inter)))) + chain = CLASS_NST_METHODS (inter); + } + while (inter); + + return meth; +} + +static tree +lookup_class_method_static (interface, ident) + tree interface; + tree ident; +{ + tree inter = interface; + tree chain = CLASS_CLS_METHODS (inter); + tree meth = NULL_TREE; + tree root_inter = NULL_TREE; + + do + { + if ((meth = lookup_method (chain, ident))) + return meth; + + if (CLASS_CATEGORY_LIST (inter)) + { + tree category = CLASS_CATEGORY_LIST (inter); + chain = CLASS_CLS_METHODS (category); + + do + { + if ((meth = lookup_method (chain, ident))) + return meth; + + /* Check for class methods in protocols in categories. */ + if (CLASS_PROTOCOL_LIST (category)) + { + if ((meth = (lookup_method_in_protocol_list + (CLASS_PROTOCOL_LIST (category), ident, 1)))) + return meth; + } + + if ((category = CLASS_CATEGORY_LIST (category))) + chain = CLASS_CLS_METHODS (category); + } + while (category); + } + + /* Check for class methods in protocols. */ + if (CLASS_PROTOCOL_LIST (inter)) + { + if ((meth = (lookup_method_in_protocol_list + (CLASS_PROTOCOL_LIST (inter), ident, 1)))) + return meth; + } + + root_inter = inter; + if ((inter = lookup_interface (CLASS_SUPER_NAME (inter)))) + chain = CLASS_CLS_METHODS (inter); + } + while (inter); + + /* Simulate wrap around. */ + return lookup_instance_method_static (root_inter, ident); +} + +tree +add_class_method (class, method) + tree class; + tree method; +{ + tree mth; + hash hsh; + + /* We will have allocated the method parameter declarations on the + maybepermanent_obstack. Need to make sure they stick around! */ + preserve_data (); + + if (!(mth = lookup_method (CLASS_CLS_METHODS (class), method))) + { + /* put method on list in reverse order */ + TREE_CHAIN (method) = CLASS_CLS_METHODS (class); + CLASS_CLS_METHODS (class) = method; + } + else + { + if (TREE_CODE (class) == CLASS_IMPLEMENTATION_TYPE) + error ("duplicate definition of class method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + else + { + /* Check types; if different, complain. */ + if (!comp_proto_with_proto (method, mth)) + error ("duplicate declaration of class method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + } + } + + if (!(hsh = hash_lookup (cls_method_hash_list, METHOD_SEL_NAME (method)))) + { + /* Install on a global chain. */ + hash_enter (cls_method_hash_list, method); + } + else + { + /* Check types; if different, add to a list. */ + if (!comp_proto_with_proto (method, hsh->key)) + hash_add_attr (hsh, method); + } + return method; +} + +tree +add_instance_method (class, method) + tree class; + tree method; +{ + tree mth; + hash hsh; + + /* We will have allocated the method parameter declarations on the + maybepermanent_obstack. Need to make sure they stick around! */ + preserve_data (); + + if (!(mth = lookup_method (CLASS_NST_METHODS (class), method))) + { + /* Put method on list in reverse order. */ + TREE_CHAIN (method) = CLASS_NST_METHODS (class); + CLASS_NST_METHODS (class) = method; + } + else + { + if (TREE_CODE (class) == CLASS_IMPLEMENTATION_TYPE) + error ("duplicate definition of instance method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + else + { + /* Check types; if different, complain. */ + if (!comp_proto_with_proto (method, mth)) + error ("duplicate declaration of instance method `%s'.", + IDENTIFIER_POINTER (METHOD_SEL_NAME (mth))); + } + } + + if (!(hsh = hash_lookup (nst_method_hash_list, METHOD_SEL_NAME (method)))) + { + /* Install on a global chain. */ + hash_enter (nst_method_hash_list, method); + } + else + { + /* Check types; if different, add to a list. */ + if (!comp_proto_with_proto (method, hsh->key)) + hash_add_attr (hsh, method); + } + return method; +} + +static tree +add_class (class) + tree class; +{ + /* Put interfaces on list in reverse order. */ + TREE_CHAIN (class) = interface_chain; + interface_chain = class; + return interface_chain; +} + +static void +add_category (class, category) + tree class; + tree category; +{ + /* Put categories on list in reverse order. */ + tree cat = CLASS_CATEGORY_LIST (class); + + while (cat) + { + if (CLASS_SUPER_NAME (cat) == CLASS_SUPER_NAME (category)) + warning ("duplicate interface declaration for category `%s(%s)'", + IDENTIFIER_POINTER (CLASS_NAME (class)), + IDENTIFIER_POINTER (CLASS_SUPER_NAME (category))); + cat = CLASS_CATEGORY_LIST (cat); + } + + CLASS_CATEGORY_LIST (category) = CLASS_CATEGORY_LIST (class); + CLASS_CATEGORY_LIST (class) = category; +} + +/* Called after parsing each instance variable declaration. Necessary to + preserve typedefs and implement public/private... + + PUBLIC is 1 for public, 0 for protected, and 2 for private. */ + +tree +add_instance_variable (class, public, declarator, declspecs, width) + tree class; + int public; + tree declarator; + tree declspecs; + tree width; +{ + tree field_decl, raw_decl; + + raw_decl = build_tree_list (declspecs, declarator); + + if (CLASS_RAW_IVARS (class)) + chainon (CLASS_RAW_IVARS (class), raw_decl); + else + CLASS_RAW_IVARS (class) = raw_decl; + + field_decl = grokfield (input_filename, lineno, + declarator, declspecs, width); + + /* Overload the public attribute, it is not used for FIELD_DECLs. */ + switch (public) + { + case 0: + TREE_PUBLIC (field_decl) = 0; + TREE_PRIVATE (field_decl) = 0; + TREE_PROTECTED (field_decl) = 1; + break; + + case 1: + TREE_PUBLIC (field_decl) = 1; + TREE_PRIVATE (field_decl) = 0; + TREE_PROTECTED (field_decl) = 0; + break; + + case 2: + TREE_PUBLIC (field_decl) = 0; + TREE_PRIVATE (field_decl) = 1; + TREE_PROTECTED (field_decl) = 0; + break; + + } + + if (CLASS_IVARS (class)) + chainon (CLASS_IVARS (class), field_decl); + else + CLASS_IVARS (class) = field_decl; + + return class; +} + +tree +is_ivar (decl_chain, ident) + tree decl_chain; + tree ident; +{ + for ( ; decl_chain; decl_chain = TREE_CHAIN (decl_chain)) + if (DECL_NAME (decl_chain) == ident) + return decl_chain; + return NULL_TREE; +} + +/* True if the ivar is private and we are not in its implementation. */ + +int +is_private (decl) + tree decl; +{ + if (TREE_PRIVATE (decl) + && ! is_ivar (CLASS_IVARS (implementation_template), DECL_NAME (decl))) + { + error ("instance variable `%s' is declared private", + IDENTIFIER_POINTER (DECL_NAME (decl))); + return 1; + } + else + return 0; +} + +/* We have an instance variable reference;, check to see if it is public. */ + +int +is_public (expr, identifier) + tree expr; + tree identifier; +{ + tree basetype = TREE_TYPE (expr); + enum tree_code code = TREE_CODE (basetype); + tree decl; + + if (code == RECORD_TYPE) + { + if (TREE_STATIC_TEMPLATE (basetype)) + { + if (!lookup_interface (TYPE_NAME (basetype))) + { + error ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (TYPE_NAME (basetype))); + return 0; + } + + if ((decl = is_ivar (TYPE_FIELDS (basetype), identifier))) + { + if (TREE_PUBLIC (decl)) + return 1; + + /* Important difference between the Stepstone translator: + all instance variables should be public within the context + of the implementation. */ + if (implementation_context + && (((TREE_CODE (implementation_context) + == CLASS_IMPLEMENTATION_TYPE) + || (TREE_CODE (implementation_context) + == CATEGORY_IMPLEMENTATION_TYPE)) + && (CLASS_NAME (implementation_context) + == TYPE_NAME (basetype)))) + return ! is_private (decl); + + error ("instance variable `%s' is declared %s", + IDENTIFIER_POINTER (identifier), + TREE_PRIVATE (decl) ? "private" : "protected"); + return 0; + } + } + + else if (implementation_context && (basetype == objc_object_reference)) + { + TREE_TYPE (expr) = uprivate_record; + warning ("static access to object of type `id'"); + } + } + + return 1; +} + +/* Implement @defs (<classname>) within struct bodies. */ + +tree +get_class_ivars (interface) + tree interface; +{ + if (!doing_objc_thang) + objc_fatal (); + + return build_ivar_chain (interface, 1); +} + +/* Make sure all entries in CHAIN are also in LIST. */ + +static int +check_methods (chain, list, mtype) + tree chain; + tree list; + int mtype; +{ + int first = 1; + + while (chain) + { + if (!lookup_method (list, chain)) + { + if (first) + { + if (TREE_CODE (implementation_context) + == CLASS_IMPLEMENTATION_TYPE) + warning ("incomplete implementation of class `%s'", + IDENTIFIER_POINTER (CLASS_NAME (implementation_context))); + else if (TREE_CODE (implementation_context) + == CATEGORY_IMPLEMENTATION_TYPE) + warning ("incomplete implementation of category `%s'", + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context))); + first = 0; + } + + warning ("method definition for `%c%s' not found", + mtype, IDENTIFIER_POINTER (METHOD_SEL_NAME (chain))); + } + + chain = TREE_CHAIN (chain); + } + + return first; +} + +static int +conforms_to_protocol (class, protocol) + tree class; + tree protocol; +{ + while (protocol) + { + tree p = CLASS_PROTOCOL_LIST (class); + + while (p && TREE_VALUE (p) != TREE_VALUE (protocol)) + p = TREE_CHAIN (p); + + if (!p) + { + tree super = (CLASS_SUPER_NAME (class) + ? lookup_interface (CLASS_SUPER_NAME (class)) + : NULL_TREE); + int tmp = super ? conforms_to_protocol (super, protocol) : 0; + if (!tmp) + return 0; + } + + protocol = TREE_CHAIN (protocol); + } + + return 1; +} + +/* Make sure all methods in CHAIN are accessible as MTYPE methods in + CONTEXT. This is one of two mechanisms to check protocol integrity. */ + +static int +check_methods_accessible (chain, context, mtype) + tree chain; + tree context; + int mtype; +{ + int first = 1; + tree list; + tree base_context = context; + + while (chain) + { + context = base_context; + while (context) + { + if (mtype == '+') + list = CLASS_CLS_METHODS (context); + else + list = CLASS_NST_METHODS (context); + + if (lookup_method (list, chain)) + break; + + else if (TREE_CODE (context) == CLASS_IMPLEMENTATION_TYPE + || TREE_CODE (context) == CLASS_INTERFACE_TYPE) + context = (CLASS_SUPER_NAME (context) + ? lookup_interface (CLASS_SUPER_NAME (context)) + : NULL_TREE); + + else if (TREE_CODE (context) == CATEGORY_IMPLEMENTATION_TYPE + || TREE_CODE (context) == CATEGORY_INTERFACE_TYPE) + context = (CLASS_NAME (context) + ? lookup_interface (CLASS_NAME (context)) + : NULL_TREE); + else + abort (); + } + + if (context == NULL_TREE) + { + if (first) + { + if (TREE_CODE (implementation_context) + == CLASS_IMPLEMENTATION_TYPE) + warning ("incomplete implementation of class `%s'", + IDENTIFIER_POINTER + (CLASS_NAME (implementation_context))); + else if (TREE_CODE (implementation_context) + == CATEGORY_IMPLEMENTATION_TYPE) + warning ("incomplete implementation of category `%s'", + IDENTIFIER_POINTER + (CLASS_SUPER_NAME (implementation_context))); + first = 0; + } + warning ("method definition for `%c%s' not found", + mtype, IDENTIFIER_POINTER (METHOD_SEL_NAME (chain))); + } + + chain = TREE_CHAIN (chain); /* next method... */ + } + return first; +} + +static void +check_protocols (proto_list, type, name) + tree proto_list; + char *type; + char *name; +{ + for ( ; proto_list; proto_list = TREE_CHAIN (proto_list)) + { + tree p = TREE_VALUE (proto_list); + + if (TREE_CODE (p) == PROTOCOL_INTERFACE_TYPE) + { + int f1, f2; + + /* Ensure that all protocols have bodies. */ + if (flag_warn_protocol) { + f1 = check_methods (PROTOCOL_CLS_METHODS (p), + CLASS_CLS_METHODS (implementation_context), + '+'); + f2 = check_methods (PROTOCOL_NST_METHODS (p), + CLASS_NST_METHODS (implementation_context), + '-'); + } else { + f1 = check_methods_accessible (PROTOCOL_CLS_METHODS (p), + implementation_context, + '+'); + f2 = check_methods_accessible (PROTOCOL_NST_METHODS (p), + implementation_context, + '-'); + } + + if (!f1 || !f2) + warning ("%s `%s' does not fully implement the `%s' protocol", + type, name, IDENTIFIER_POINTER (PROTOCOL_NAME (p))); + + } + else + ; /* An identifier if we could not find a protocol. */ + + /* Check protocols recursively. */ + if (PROTOCOL_LIST (p)) + { + tree super_class + = lookup_interface (CLASS_SUPER_NAME (implementation_template)); + if (! conforms_to_protocol (super_class, PROTOCOL_LIST (p))) + check_protocols (PROTOCOL_LIST (p), type, name); + } + } +} + +/* Make sure that the class CLASS_NAME is defined + CODE says which kind of thing CLASS_NAME ought to be. + It can be CLASS_INTERFACE_TYPE, CLASS_IMPLEMENTATION_TYPE, + CATEGORY_INTERFACE_TYPE, or CATEGORY_IMPLEMENTATION_TYPE. + + If CODE is CLASS_INTERFACE_TYPE, we also do a push_obstacks_nochange + whose matching pop is in continue_class. */ + +tree +start_class (code, class_name, super_name, protocol_list) + enum tree_code code; + tree class_name; + tree super_name; + tree protocol_list; +{ + tree class, decl; + + if (code == CLASS_INTERFACE_TYPE) + { + push_obstacks_nochange (); + end_temporary_allocation (); + } + + if (!doing_objc_thang) + objc_fatal (); + + class = make_node (code); + TYPE_BINFO (class) = make_tree_vec (5); + + CLASS_NAME (class) = class_name; + CLASS_SUPER_NAME (class) = super_name; + CLASS_CLS_METHODS (class) = NULL_TREE; + + if (! is_class_name (class_name) && (decl = lookup_name (class_name))) + { + error ("`%s' redeclared as different kind of symbol", + IDENTIFIER_POINTER (class_name)); + error_with_decl (decl, "previous declaration of `%s'"); + } + + if (code == CLASS_IMPLEMENTATION_TYPE) + { + { + static tree implemented_classes = 0; + tree chain = implemented_classes; + for (chain = implemented_classes; chain; chain = TREE_CHAIN (chain)) + if (TREE_VALUE (chain) == class_name) + { + error ("reimplementation of class `%s'", + IDENTIFIER_POINTER (class_name)); + return error_mark_node; + } + implemented_classes = perm_tree_cons (NULL_TREE, class_name, + implemented_classes); + } + + /* Pre-build the following entities - for speed/convenience. */ + if (!self_id) + self_id = get_identifier ("self"); + if (!ucmd_id) + ucmd_id = get_identifier ("_cmd"); + if (!unused_list) + unused_list + = build_tree_list (get_identifier ("__unused__"), NULL_TREE); + if (!objc_super_template) + objc_super_template = build_super_template (); + + /* Reset for multiple classes per file. */ + method_slot = 0; + + implementation_context = class; + + /* Lookup the interface for this implementation. */ + + if (!(implementation_template = lookup_interface (class_name))) + { + warning ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (class_name)); + add_class (implementation_template = implementation_context); + } + + /* If a super class has been specified in the implementation, + insure it conforms to the one specified in the interface. */ + + if (super_name + && (super_name != CLASS_SUPER_NAME (implementation_template))) + { + tree previous_name = CLASS_SUPER_NAME (implementation_template); + char *name = previous_name ? IDENTIFIER_POINTER (previous_name) : ""; + error ("conflicting super class name `%s'", + IDENTIFIER_POINTER (super_name)); + error ("previous declaration of `%s'", name); + } + + else if (! super_name) + { + CLASS_SUPER_NAME (implementation_context) + = CLASS_SUPER_NAME (implementation_template); + } + } + + else if (code == CLASS_INTERFACE_TYPE) + { + if (lookup_interface (class_name)) + warning ("duplicate interface declaration for class `%s'", + IDENTIFIER_POINTER (class_name)); + else + add_class (class); + + if (protocol_list) + CLASS_PROTOCOL_LIST (class) + = lookup_and_install_protocols (protocol_list); + } + + else if (code == CATEGORY_INTERFACE_TYPE) + { + tree class_category_is_assoc_with; + + /* For a category, class_name is really the name of the class that + the following set of methods will be associated with. We must + find the interface so that can derive the objects template. */ + + if (!(class_category_is_assoc_with = lookup_interface (class_name))) + { + error ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (class_name)); + exit (FATAL_EXIT_CODE); + } + else + add_category (class_category_is_assoc_with, class); + + if (protocol_list) + CLASS_PROTOCOL_LIST (class) + = lookup_and_install_protocols (protocol_list); + } + + else if (code == CATEGORY_IMPLEMENTATION_TYPE) + { + /* Pre-build the following entities for speed/convenience. */ + if (!self_id) + self_id = get_identifier ("self"); + if (!ucmd_id) + ucmd_id = get_identifier ("_cmd"); + if (!unused_list) + unused_list + = build_tree_list (get_identifier ("__unused__"), NULL_TREE); + if (!objc_super_template) + objc_super_template = build_super_template (); + + /* Reset for multiple classes per file. */ + method_slot = 0; + + implementation_context = class; + + /* For a category, class_name is really the name of the class that + the following set of methods will be associated with. We must + find the interface so that can derive the objects template. */ + + if (!(implementation_template = lookup_interface (class_name))) + { + error ("Cannot find interface declaration for `%s'", + IDENTIFIER_POINTER (class_name)); + exit (FATAL_EXIT_CODE); + } + } + return class; +} + +tree +continue_class (class) + tree class; +{ + if (TREE_CODE (class) == CLASS_IMPLEMENTATION_TYPE + || TREE_CODE (class) == CATEGORY_IMPLEMENTATION_TYPE) + { + struct imp_entry *imp_entry; + tree ivar_context; + + /* Check consistency of the instance variables. */ + + if (CLASS_IVARS (class)) + check_ivars (implementation_template, class); + + /* code generation */ + + ivar_context = build_private_template (implementation_template); + + if (!objc_class_template) + build_class_template (); + + if (!(imp_entry + = (struct imp_entry *) xmalloc (sizeof (struct imp_entry)))) + perror ("unable to allocate in objc-tree.c"); + + imp_entry->next = imp_list; + imp_entry->imp_context = class; + imp_entry->imp_template = implementation_template; + + synth_forward_declarations (); + imp_entry->class_decl = UOBJC_CLASS_decl; + imp_entry->meta_decl = UOBJC_METACLASS_decl; + + /* Append to front and increment count. */ + imp_list = imp_entry; + if (TREE_CODE (class) == CLASS_IMPLEMENTATION_TYPE) + imp_count++; + else + cat_count++; + + return ivar_context; + } + + else if (TREE_CODE (class) == CLASS_INTERFACE_TYPE) + { + tree record = xref_tag (RECORD_TYPE, CLASS_NAME (class)); + + if (!TYPE_FIELDS (record)) + { + finish_struct (record, build_ivar_chain (class, 0), NULL_TREE); + CLASS_STATIC_TEMPLATE (class) = record; + + /* Mark this record as a class template for static typing. */ + TREE_STATIC_TEMPLATE (record) = 1; + } + + return NULL_TREE; + } + + else + return error_mark_node; +} + +/* This is called once we see the "@end" in an interface/implementation. */ + +void +finish_class (class) + tree class; +{ + if (TREE_CODE (class) == CLASS_IMPLEMENTATION_TYPE) + { + /* All code generation is done in finish_objc. */ + + if (implementation_template != implementation_context) + { + /* Ensure that all method listed in the interface contain bodies. */ + check_methods (CLASS_CLS_METHODS (implementation_template), + CLASS_CLS_METHODS (implementation_context), '+'); + check_methods (CLASS_NST_METHODS (implementation_template), + CLASS_NST_METHODS (implementation_context), '-'); + + if (CLASS_PROTOCOL_LIST (implementation_template)) + check_protocols (CLASS_PROTOCOL_LIST (implementation_template), + "class", + IDENTIFIER_POINTER (CLASS_NAME (implementation_context))); + } + } + + else if (TREE_CODE (class) == CATEGORY_IMPLEMENTATION_TYPE) + { + tree category = CLASS_CATEGORY_LIST (implementation_template); + + /* Find the category interface from the class it is associated with. */ + while (category) + { + if (CLASS_SUPER_NAME (class) == CLASS_SUPER_NAME (category)) + break; + category = CLASS_CATEGORY_LIST (category); + } + + if (category) + { + /* Ensure all method listed in the interface contain bodies. */ + check_methods (CLASS_CLS_METHODS (category), + CLASS_CLS_METHODS (implementation_context), '+'); + check_methods (CLASS_NST_METHODS (category), + CLASS_NST_METHODS (implementation_context), '-'); + + if (CLASS_PROTOCOL_LIST (category)) + check_protocols (CLASS_PROTOCOL_LIST (category), + "category", + IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context))); + } + } + + else if (TREE_CODE (class) == CLASS_INTERFACE_TYPE) + { + tree decl_specs; + char *class_name = IDENTIFIER_POINTER (CLASS_NAME (class)); + char *string = (char *) alloca (strlen (class_name) + 3); + + /* extern struct objc_object *_<my_name>; */ + + sprintf (string, "_%s", class_name); + + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_EXTERN]); + decl_specs = tree_cons (NULL_TREE, objc_object_reference, decl_specs); + define_decl (build1 (INDIRECT_REF, NULL_TREE, get_identifier (string)), + decl_specs); + } +} + +static tree +add_protocol (protocol) + tree protocol; +{ + /* Put protocol on list in reverse order. */ + TREE_CHAIN (protocol) = protocol_chain; + protocol_chain = protocol; + return protocol_chain; +} + +static tree +lookup_protocol (ident) + tree ident; +{ + tree chain; + + for (chain = protocol_chain; chain; chain = TREE_CHAIN (chain)) + { + if (ident == PROTOCOL_NAME (chain)) + return chain; + } + + return NULL_TREE; +} + +tree +start_protocol (code, name, list) + enum tree_code code; + tree name; + tree list; +{ + tree protocol; + + if (!doing_objc_thang) + objc_fatal (); + + /* This is as good a place as any. Need to invoke push_tag_toplevel. */ + if (!objc_protocol_template) + objc_protocol_template = build_protocol_template (); + + protocol = make_node (code); + TYPE_BINFO (protocol) = make_tree_vec (2); + + PROTOCOL_NAME (protocol) = name; + PROTOCOL_LIST (protocol) = list; + + lookup_and_install_protocols (list); + + if (lookup_protocol (name)) + warning ("duplicate declaration for protocol `%s'", + IDENTIFIER_POINTER (name)); + else + add_protocol (protocol); + + PROTOCOL_FORWARD_DECL (protocol) = NULL_TREE; + + return protocol; +} + +void +finish_protocol (protocol) + tree protocol; +{ +} + + +/* "Encode" a data type into a string, which grows in util_obstack. + ??? What is the FORMAT? Someone please document this! */ + +static void +encode_type_qualifiers (declspecs) + tree declspecs; +{ + tree spec; + + for (spec = declspecs; spec; spec = TREE_CHAIN (spec)) + { + if (ridpointers[(int) RID_CONST] == TREE_VALUE (spec)) + obstack_1grow (&util_obstack, 'r'); + else if (ridpointers[(int) RID_IN] == TREE_VALUE (spec)) + obstack_1grow (&util_obstack, 'n'); + else if (ridpointers[(int) RID_INOUT] == TREE_VALUE (spec)) + obstack_1grow (&util_obstack, 'N'); + else if (ridpointers[(int) RID_OUT] == TREE_VALUE (spec)) + obstack_1grow (&util_obstack, 'o'); + else if (ridpointers[(int) RID_BYCOPY] == TREE_VALUE (spec)) + obstack_1grow (&util_obstack, 'O'); + else if (ridpointers[(int) RID_ONEWAY] == TREE_VALUE (spec)) + obstack_1grow (&util_obstack, 'V'); + } +} + +/* Encode a pointer type. */ + +static void +encode_pointer (type, curtype, format) + tree type; + int curtype; + int format; +{ + tree pointer_to = TREE_TYPE (type); + + if (TREE_CODE (pointer_to) == RECORD_TYPE) + { + if (TYPE_NAME (pointer_to) + && TREE_CODE (TYPE_NAME (pointer_to)) == IDENTIFIER_NODE) + { + char *name = IDENTIFIER_POINTER (TYPE_NAME (pointer_to)); + + if (strcmp (name, TAG_OBJECT) == 0) /* '@' */ + { + obstack_1grow (&util_obstack, '@'); + return; + } + else if (TREE_STATIC_TEMPLATE (pointer_to)) + { + if (generating_instance_variables) + { + obstack_1grow (&util_obstack, '@'); + obstack_1grow (&util_obstack, '"'); + obstack_grow (&util_obstack, name, strlen (name)); + obstack_1grow (&util_obstack, '"'); + return; + } + else + { + obstack_1grow (&util_obstack, '@'); + return; + } + } + else if (strcmp (name, TAG_CLASS) == 0) /* '#' */ + { + obstack_1grow (&util_obstack, '#'); + return; + } +#ifndef OBJC_INT_SELECTORS + else if (strcmp (name, TAG_SELECTOR) == 0) /* ':' */ + { + obstack_1grow (&util_obstack, ':'); + return; + } +#endif /* OBJC_INT_SELECTORS */ + } + } + else if (TREE_CODE (pointer_to) == INTEGER_TYPE + && TYPE_MODE (pointer_to) == QImode) + { + obstack_1grow (&util_obstack, '*'); + return; + } + + /* We have a type that does not get special treatment. */ + + /* NeXT extension */ + obstack_1grow (&util_obstack, '^'); + encode_type (pointer_to, curtype, format); +} + +static void +encode_array (type, curtype, format) + tree type; + int curtype; + int format; +{ + tree an_int_cst = TYPE_SIZE (type); + tree array_of = TREE_TYPE (type); + char buffer[40]; + + /* An incomplete array is treated like a pointer. */ + if (an_int_cst == NULL) + { + encode_pointer (type, curtype, format); + return; + } + + sprintf (buffer, "[%d", + (TREE_INT_CST_LOW (an_int_cst) + / TREE_INT_CST_LOW (TYPE_SIZE (array_of)))); + + obstack_grow (&util_obstack, buffer, strlen (buffer)); + encode_type (array_of, curtype, format); + obstack_1grow (&util_obstack, ']'); + return; +} + +static void +encode_aggregate_within (type, curtype, format, left, right) + tree type; + int curtype; + int format; + char left; + char right; +{ + if (obstack_object_size (&util_obstack) > 0 + && *(obstack_next_free (&util_obstack) - 1) == '^') + { + tree name = TYPE_NAME (type); + + /* we have a reference; this is a NeXT extension. */ + + if (obstack_object_size (&util_obstack) - curtype == 1 + && format == OBJC_ENCODE_INLINE_DEFS) + { + /* Output format of struct for first level only. */ + tree fields = TYPE_FIELDS (type); + + if (name && TREE_CODE (name) == IDENTIFIER_NODE) + { + obstack_1grow (&util_obstack, left); + obstack_grow (&util_obstack, + IDENTIFIER_POINTER (name), + strlen (IDENTIFIER_POINTER (name))); + obstack_1grow (&util_obstack, '='); + } + else + { + obstack_1grow (&util_obstack, left); + obstack_grow (&util_obstack, "?=", 2); + } + + for ( ; fields; fields = TREE_CHAIN (fields)) + encode_field_decl (fields, curtype, format); + + obstack_1grow (&util_obstack, right); + } + + else if (name && TREE_CODE (name) == IDENTIFIER_NODE) + { + obstack_1grow (&util_obstack, left); + obstack_grow (&util_obstack, + IDENTIFIER_POINTER (name), + strlen (IDENTIFIER_POINTER (name))); + obstack_1grow (&util_obstack, right); + } + + else + { + /* We have an untagged structure or a typedef. */ + obstack_1grow (&util_obstack, left); + obstack_1grow (&util_obstack, '?'); + obstack_1grow (&util_obstack, right); + } + } + + else + { + tree name = TYPE_NAME (type); + tree fields = TYPE_FIELDS (type); + + if (format == OBJC_ENCODE_INLINE_DEFS + || generating_instance_variables) + { + obstack_1grow (&util_obstack, left); + if (name && TREE_CODE (name) == IDENTIFIER_NODE) + obstack_grow (&util_obstack, + IDENTIFIER_POINTER (name), + strlen (IDENTIFIER_POINTER (name))); + else + obstack_1grow (&util_obstack, '?'); + + obstack_1grow (&util_obstack, '='); + + for (; fields; fields = TREE_CHAIN (fields)) + { + if (generating_instance_variables) + { + tree fname = DECL_NAME (fields); + + obstack_1grow (&util_obstack, '"'); + if (fname && TREE_CODE (fname) == IDENTIFIER_NODE) + { + obstack_grow (&util_obstack, + IDENTIFIER_POINTER (fname), + strlen (IDENTIFIER_POINTER (fname))); + } + + obstack_1grow (&util_obstack, '"'); + } + + encode_field_decl (fields, curtype, format); + } + + obstack_1grow (&util_obstack, right); + } + + else + { + obstack_1grow (&util_obstack, left); + if (name && TREE_CODE (name) == IDENTIFIER_NODE) + obstack_grow (&util_obstack, + IDENTIFIER_POINTER (name), + strlen (IDENTIFIER_POINTER (name))); + else + /* We have an untagged structure or a typedef. */ + obstack_1grow (&util_obstack, '?'); + + obstack_1grow (&util_obstack, right); + } + } +} + +static void +encode_aggregate (type, curtype, format) + tree type; + int curtype; + int format; +{ + enum tree_code code = TREE_CODE (type); + + switch (code) + { + case RECORD_TYPE: + { + encode_aggregate_within(type, curtype, format, '{', '}'); + break; + } + case UNION_TYPE: + { + encode_aggregate_within(type, curtype, format, '(', ')'); + break; + } + + case ENUMERAL_TYPE: + obstack_1grow (&util_obstack, 'i'); + break; + } +} + +/* Support bitfields. The current version of Objective-C does not support + them. The string will consist of one or more "b:n"'s where n is an + integer describing the width of the bitfield. Currently, classes in + the kit implement a method "-(char *)describeBitfieldStruct:" that + simulates this. If they do not implement this method, the archiver + assumes the bitfield is 16 bits wide (padded if necessary) and packed + according to the GNU compiler. After looking at the "kit", it appears + that all classes currently rely on this default behavior, rather than + hand generating this string (which is tedious). */ + +static void +encode_bitfield (width, format) + int width; + int format; +{ + char buffer[40]; + sprintf (buffer, "b%d", width); + obstack_grow (&util_obstack, buffer, strlen (buffer)); +} + +/* FORMAT will be OBJC_ENCODE_INLINE_DEFS or OBJC_ENCODE_DONT_INLINE_DEFS. */ + +static void +encode_type (type, curtype, format) + tree type; + int curtype; + int format; +{ + enum tree_code code = TREE_CODE (type); + + if (code == INTEGER_TYPE) + { + if (TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) == 0 + && TREE_INT_CST_HIGH (TYPE_MIN_VALUE (type)) == 0) + { + /* Unsigned integer types. */ + + if (TYPE_MODE (type) == QImode) + obstack_1grow (&util_obstack, 'C'); + else if (TYPE_MODE (type) == HImode) + obstack_1grow (&util_obstack, 'S'); + else if (TYPE_MODE (type) == SImode) + { + if (type == long_unsigned_type_node) + obstack_1grow (&util_obstack, 'L'); + else + obstack_1grow (&util_obstack, 'I'); + } + else if (TYPE_MODE (type) == DImode) + obstack_1grow (&util_obstack, 'Q'); + } + + else + /* Signed integer types. */ + { + if (TYPE_MODE (type) == QImode) + obstack_1grow (&util_obstack, 'c'); + else if (TYPE_MODE (type) == HImode) + obstack_1grow (&util_obstack, 's'); + else if (TYPE_MODE (type) == SImode) + { + if (type == long_integer_type_node) + obstack_1grow (&util_obstack, 'l'); + else + obstack_1grow (&util_obstack, 'i'); + } + + else if (TYPE_MODE (type) == DImode) + obstack_1grow (&util_obstack, 'q'); + } + } + + else if (code == REAL_TYPE) + { + /* Floating point types. */ + + if (TYPE_MODE (type) == SFmode) + obstack_1grow (&util_obstack, 'f'); + else if (TYPE_MODE (type) == DFmode + || TYPE_MODE (type) == TFmode) + obstack_1grow (&util_obstack, 'd'); + } + + else if (code == VOID_TYPE) + obstack_1grow (&util_obstack, 'v'); + + else if (code == ARRAY_TYPE) + encode_array (type, curtype, format); + + else if (code == POINTER_TYPE) + encode_pointer (type, curtype, format); + + else if (code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE) + encode_aggregate (type, curtype, format); + + else if (code == FUNCTION_TYPE) /* '?' */ + obstack_1grow (&util_obstack, '?'); +} + +static void +encode_field_decl (field_decl, curtype, format) + tree field_decl; + int curtype; + int format; +{ + tree type; + + /* If this field is obviously a bitfield, or is a bitfield that has been + clobbered to look like a ordinary integer mode, go ahead and generate + the bitfield typing information. */ + type = TREE_TYPE (field_decl); + if (DECL_BIT_FIELD (field_decl)) + encode_bitfield (DECL_FIELD_SIZE (field_decl), format); + else if (TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST + && DECL_FIELD_SIZE (field_decl) + && TYPE_MODE (type) > DECL_MODE (field_decl)) + encode_bitfield (DECL_FIELD_SIZE (field_decl), format); + else + encode_type (TREE_TYPE (field_decl), curtype, format); +} + +static tree +expr_last (complex_expr) + tree complex_expr; +{ + tree next; + + if (complex_expr) + while ((next = TREE_OPERAND (complex_expr, 0))) + complex_expr = next; + + return complex_expr; +} + +/* The selector of the current method, + or NULL if we aren't compiling a method. */ + +tree +maybe_objc_method_name (decl) + tree decl; +{ + if (method_context) + return METHOD_SEL_NAME (method_context); + else + return 0; +} + +/* Transform a method definition into a function definition as follows: + - synthesize the first two arguments, "self" and "_cmd". */ + +void +start_method_def (method) + tree method; +{ + tree decl_specs; + + /* Required to implement _msgSuper. */ + method_context = method; + UOBJC_SUPER_decl = NULL_TREE; + + /* Must be called BEFORE start_function. */ + pushlevel (0); + + /* Generate prototype declarations for arguments..."new-style". */ + + if (TREE_CODE (method_context) == INSTANCE_METHOD_DECL) + decl_specs = build_tree_list (NULL_TREE, uprivate_record); + else + /* Really a `struct objc_class *'. However, we allow people to + assign to self, which changes its type midstream. */ + decl_specs = build_tree_list (NULL_TREE, objc_object_reference); + + push_parm_decl (build_tree_list + (build_tree_list (decl_specs, + build1 (INDIRECT_REF, NULL_TREE, self_id)), + build_tree_list (unused_list, NULL_TREE))); + +#ifdef OBJC_INT_SELECTORS + decl_specs = build_tree_list (NULL_TREE, ridpointers[(int) RID_UNSIGNED]); + decl_specs = tree_cons (NULL_TREE, ridpointers[(int) RID_INT], decl_specs); + push_parm_decl (build_tree_list (build_tree_list (decl_specs, ucmd_id), + build_tree_list (unused_list, NULL_TREE))); +#else /* not OBJC_INT_SELECTORS */ + decl_specs = build_tree_list (NULL_TREE, + xref_tag (RECORD_TYPE, + get_identifier (TAG_SELECTOR))); + push_parm_decl (build_tree_list + (build_tree_list (decl_specs, + build1 (INDIRECT_REF, NULL_TREE, ucmd_id)), + build_tree_list (unused_list, NULL_TREE))); +#endif /* not OBJC_INT_SELECTORS */ + + /* Generate argument declarations if a keyword_decl. */ + if (METHOD_SEL_ARGS (method)) + { + tree arglist = METHOD_SEL_ARGS (method); + do + { + tree arg_spec = TREE_PURPOSE (TREE_TYPE (arglist)); + tree arg_decl = TREE_VALUE (TREE_TYPE (arglist)); + + if (arg_decl) + { + tree last_expr = expr_last (arg_decl); + + /* Unite the abstract decl with its name. */ + TREE_OPERAND (last_expr, 0) = KEYWORD_ARG_NAME (arglist); + push_parm_decl (build_tree_list + (build_tree_list (arg_spec, arg_decl), + build_tree_list (NULL_TREE, NULL_TREE))); + + /* Unhook: restore the abstract declarator. */ + TREE_OPERAND (last_expr, 0) = NULL_TREE; + } + + else + push_parm_decl (build_tree_list + (build_tree_list (arg_spec, + KEYWORD_ARG_NAME (arglist)), + build_tree_list (NULL_TREE, NULL_TREE))); + + arglist = TREE_CHAIN (arglist); + } + while (arglist); + } + + if (METHOD_ADD_ARGS (method) > (tree)1) + { + /* We have a variable length selector - in "prototype" format. */ + tree akey = TREE_PURPOSE (METHOD_ADD_ARGS (method)); + while (akey) + { + /* This must be done prior to calling pushdecl. pushdecl is + going to change our chain on us. */ + tree nextkey = TREE_CHAIN (akey); + pushdecl (akey); + akey = nextkey; + } + } +} + +static void +warn_with_method (message, mtype, method) + char *message; + int mtype; + tree method; +{ + if (count_error (1) == 0) + return; + + report_error_function (DECL_SOURCE_FILE (method)); + + fprintf (stderr, "%s:%d: warning: ", + DECL_SOURCE_FILE (method), DECL_SOURCE_LINE (method)); + bzero (errbuf, BUFSIZE); + fprintf (stderr, "%s `%c%s'\n", + message, mtype, gen_method_decl (method, errbuf)); +} + +/* Return 1 if METHOD is consistent with PROTO. */ + +static int +comp_method_with_proto (method, proto) + tree method, proto; +{ + static tree function_type = 0; + + /* Create a function_type node once. */ + if (!function_type) + { + push_obstacks_nochange (); + end_temporary_allocation (); + function_type = make_node (FUNCTION_TYPE); + pop_obstacks (); + } + + /* Install argument types - normally set by build_function_type. */ + TYPE_ARG_TYPES (function_type) = get_arg_type_list (proto, METHOD_DEF, 0); + + /* install return type */ + TREE_TYPE (function_type) = groktypename (TREE_TYPE (proto)); + + return comptypes (TREE_TYPE (METHOD_DEFINITION (method)), function_type); +} + +/* Return 1 if PROTO1 is consistent with PROTO2. */ + +static int +comp_proto_with_proto (proto1, proto2) + tree proto1, proto2; +{ + static tree function_type1 = 0, function_type2 = 0; + + /* Create a couple function_type node's once. */ + if (!function_type1) + { + push_obstacks_nochange (); + end_temporary_allocation (); + function_type1 = make_node (FUNCTION_TYPE); + function_type2 = make_node (FUNCTION_TYPE); + pop_obstacks (); + } + + /* Install argument types; normally set by build_function_type. */ + TYPE_ARG_TYPES (function_type1) = get_arg_type_list (proto1, METHOD_REF, 0); + TYPE_ARG_TYPES (function_type2) = get_arg_type_list (proto2, METHOD_REF, 0); + + /* Install return type. */ + TREE_TYPE (function_type1) = groktypename (TREE_TYPE (proto1)); + TREE_TYPE (function_type2) = groktypename (TREE_TYPE (proto2)); + + return comptypes (function_type1, function_type2); +} + +/* - Generate an identifier for the function. the format is "_n_cls", + where 1 <= n <= nMethods, and cls is the name the implementation we + are processing. + - Install the return type from the method declaration. + - If we have a prototype, check for type consistency. */ + +static void +really_start_method (method, parmlist) + tree method, parmlist; +{ + tree sc_spec, ret_spec, ret_decl, decl_specs; + tree method_decl, method_id; + char *buf, *sel_name, *class_name, *cat_name; + + /* Synth the storage class & assemble the return type. */ + sc_spec = tree_cons (NULL_TREE, ridpointers[(int) RID_STATIC], NULL_TREE); + ret_spec = TREE_PURPOSE (TREE_TYPE (method)); + decl_specs = chainon (sc_spec, ret_spec); + + sel_name = IDENTIFIER_POINTER (METHOD_SEL_NAME (method)); + class_name = IDENTIFIER_POINTER (CLASS_NAME (implementation_context)); + cat_name = ((TREE_CODE (implementation_context) + == CLASS_IMPLEMENTATION_TYPE) + ? NULL + : IDENTIFIER_POINTER (CLASS_SUPER_NAME (implementation_context))); + method_slot++; + + /* Make sure this is big enough for any plausible method label. */ + buf = (char *) alloca (50 + strlen (sel_name) + strlen (class_name) + + (cat_name ? strlen (cat_name) : 0)); + + OBJC_GEN_METHOD_LABEL (buf, TREE_CODE (method) == INSTANCE_METHOD_DECL, + class_name, cat_name, sel_name, method_slot); + + method_id = get_identifier (buf); + + method_decl = build_nt (CALL_EXPR, method_id, parmlist, NULL_TREE); + + /* Check the declarator portion of the return type for the method. */ + if ((ret_decl = TREE_VALUE (TREE_TYPE (method)))) + { + /* Unite the complex decl (specified in the abstract decl) with the + function decl just synthesized..(int *), (int (*)()), (int (*)[]). */ + tree save_expr = expr_last (ret_decl); + + TREE_OPERAND (save_expr, 0) = method_decl; + method_decl = ret_decl; + + /* Fool the parser into thinking it is starting a function. */ + start_function (decl_specs, method_decl, NULL_TREE, NULL_TREE, 0); + + /* Unhook: this has the effect of restoring the abstract declarator. */ + TREE_OPERAND (save_expr, 0) = NULL_TREE; + } + + else + { + TREE_VALUE (TREE_TYPE (method)) = method_decl; + + /* Fool the parser into thinking it is starting a function. */ + start_function (decl_specs, method_decl, NULL_TREE, NULL_TREE, 0); + + /* Unhook: this has the effect of restoring the abstract declarator. */ + TREE_VALUE (TREE_TYPE (method)) = NULL_TREE; + } + + METHOD_DEFINITION (method) = current_function_decl; + + if (implementation_template != implementation_context) + { + tree proto; + + if (TREE_CODE (method) == INSTANCE_METHOD_DECL) + proto = lookup_instance_method_static (implementation_template, + METHOD_SEL_NAME (method)); + else + proto = lookup_class_method_static (implementation_template, + METHOD_SEL_NAME (method)); + + if (proto && ! comp_method_with_proto (method, proto)) + { + char type = (TREE_CODE (method) == INSTANCE_METHOD_DECL ? '-' : '+'); + + warn_with_method ("conflicting types for", type, method); + warn_with_method ("previous declaration of", type, proto); + } + } +} + +/* The following routine is always called...this "architecture" is to + accommodate "old-style" variable length selectors. + + - a:a b:b // prototype ; id c; id d; // old-style. */ + +void +continue_method_def () +{ + tree parmlist; + + if (METHOD_ADD_ARGS (method_context) == (tree)1) + /* We have a `, ...' immediately following the selector. */ + parmlist = get_parm_info (0); + else + parmlist = get_parm_info (1); /* place a `void_at_end' */ + + /* Set self_decl from the first argument...this global is used by + build_ivar_reference calling build_indirect_ref. */ + self_decl = TREE_PURPOSE (parmlist); + + poplevel (0, 0, 0); + really_start_method (method_context, parmlist); + store_parm_decls (); +} + +/* Called by the parser, from the `pushlevel' production. */ + +void +add_objc_decls () +{ + if (!UOBJC_SUPER_decl) + { + UOBJC_SUPER_decl = start_decl (get_identifier (UTAG_SUPER), + build_tree_list (NULL_TREE, + objc_super_template), + 0, NULL_TREE, NULL_TREE); + + finish_decl (UOBJC_SUPER_decl, NULL_TREE, NULL_TREE); + + /* This prevents `unused variable' warnings when compiling with -Wall. */ + TREE_USED (UOBJC_SUPER_decl) = 1; + DECL_ARTIFICIAL (UOBJC_SUPER_decl) = 1; + } +} + +/* _n_Method (id self, SEL sel, ...) + { + struct objc_super _S; + _msgSuper ((_S.self = self, _S.class = _cls, &_S), ...); + } */ + +tree +get_super_receiver () +{ + if (method_context) + { + tree super_expr, super_expr_list; + + /* Set receiver to self. */ + super_expr = build_component_ref (UOBJC_SUPER_decl, self_id); + super_expr = build_modify_expr (super_expr, NOP_EXPR, self_decl); + super_expr_list = build_tree_list (NULL_TREE, super_expr); + + /* Set class to begin searching. */ + super_expr = build_component_ref (UOBJC_SUPER_decl, + get_identifier ("class")); + + if (TREE_CODE (implementation_context) == CLASS_IMPLEMENTATION_TYPE) + { + /* [_cls, __cls]Super are "pre-built" in + synth_forward_declarations. */ + + super_expr = build_modify_expr (super_expr, NOP_EXPR, + ((TREE_CODE (method_context) + == INSTANCE_METHOD_DECL) + ? ucls_super_ref + : uucls_super_ref)); + } + + else + /* We have a category. */ + { + tree super_name = CLASS_SUPER_NAME (implementation_template); + tree super_class; + + if (!super_name) + { + error ("no super class declared in interface for `%s'", + IDENTIFIER_POINTER (CLASS_NAME (implementation_template))); + return error_mark_node; + } + + if (flag_next_runtime) + { + super_class = get_class_reference (super_name); + if (TREE_CODE (method_context) == CLASS_METHOD_DECL) + super_class + = build_component_ref (build_indirect_ref (super_class, "->"), + get_identifier ("isa")); + } + else + { + add_class_reference (super_name); + super_class = (TREE_CODE (method_context) == INSTANCE_METHOD_DECL + ? objc_get_class_decl : objc_get_meta_class_decl); + assemble_external (super_class); + super_class + = build_function_call + (super_class, + build_tree_list + (NULL_TREE, + my_build_string (IDENTIFIER_LENGTH (super_name) + 1, + IDENTIFIER_POINTER (super_name)))); + } + + TREE_TYPE (super_class) = TREE_TYPE (ucls_super_ref); + super_expr = build_modify_expr (super_expr, NOP_EXPR, super_class); + } + + chainon (super_expr_list, build_tree_list (NULL_TREE, super_expr)); + + super_expr = build_unary_op (ADDR_EXPR, UOBJC_SUPER_decl, 0); + chainon (super_expr_list, build_tree_list (NULL_TREE, super_expr)); + + return build_compound_expr (super_expr_list); + } + else + { + error ("[super ...] must appear in a method context"); + return error_mark_node; + } +} + +static tree +encode_method_def (func_decl) + tree func_decl; +{ + tree parms; + int stack_size; + int max_parm_end = 0; + char buffer[40]; + tree result; + + /* Return type. */ + encode_type (TREE_TYPE (TREE_TYPE (func_decl)), + obstack_object_size (&util_obstack), + OBJC_ENCODE_INLINE_DEFS); + + /* Stack size. */ + for (parms = DECL_ARGUMENTS (func_decl); parms; + parms = TREE_CHAIN (parms)) + { + int parm_end = (forwarding_offset (parms) + + (TREE_INT_CST_LOW (TYPE_SIZE (TREE_TYPE (parms))) + / BITS_PER_UNIT)); + + if (!offset_is_register && parm_end > max_parm_end) + max_parm_end = parm_end; + } + + stack_size = max_parm_end - OBJC_FORWARDING_MIN_OFFSET; + + sprintf (buffer, "%d", stack_size); + obstack_grow (&util_obstack, buffer, strlen (buffer)); + + /* Argument types. */ + for (parms = DECL_ARGUMENTS (func_decl); parms; + parms = TREE_CHAIN (parms)) + { + /* Type. */ + encode_type (TREE_TYPE (parms), + obstack_object_size (&util_obstack), + OBJC_ENCODE_INLINE_DEFS); + + /* Compute offset. */ + sprintf (buffer, "%d", forwarding_offset (parms)); + + /* Indicate register. */ + if (offset_is_register) + obstack_1grow (&util_obstack, '+'); + + obstack_grow (&util_obstack, buffer, strlen (buffer)); + } + + obstack_1grow (&util_obstack, 0); + result = get_identifier (obstack_finish (&util_obstack)); + obstack_free (&util_obstack, util_firstobj); + return result; +} + +void +finish_method_def () +{ + METHOD_ENCODING (method_context) = encode_method_def (current_function_decl); + + finish_function (0); + + /* Required to implement _msgSuper. This must be done AFTER finish_function, + since the optimizer may find "may be used before set" errors. */ + method_context = NULL_TREE; +} + +int +lang_report_error_function (decl) + tree decl; +{ + if (method_context) + { + fprintf (stderr, "In method `%s'\n", + IDENTIFIER_POINTER (METHOD_SEL_NAME (method_context))); + return 1; + } + + else + return 0; +} + +static int +is_complex_decl (type) + tree type; +{ + return (TREE_CODE (type) == ARRAY_TYPE + || TREE_CODE (type) == FUNCTION_TYPE + || (TREE_CODE (type) == POINTER_TYPE && ! IS_ID (type))); +} + + +/* Code to convert a decl node into text for a declaration in C. */ + +static char tmpbuf[256]; + +static void +adorn_decl (decl, str) + tree decl; + char *str; +{ + enum tree_code code = TREE_CODE (decl); + + if (code == ARRAY_REF) + { + tree an_int_cst = TREE_OPERAND (decl, 1); + + if (an_int_cst && TREE_CODE (an_int_cst) == INTEGER_CST) + sprintf (str + strlen (str), "[%d]", TREE_INT_CST_LOW (an_int_cst)); + else + strcat (str, "[]"); + } + + else if (code == ARRAY_TYPE) + { + tree an_int_cst = TYPE_SIZE (decl); + tree array_of = TREE_TYPE (decl); + + if (an_int_cst && TREE_CODE (an_int_cst) == INTEGER_TYPE) + sprintf (str + strlen (str), "[%d]", + (TREE_INT_CST_LOW (an_int_cst) + / TREE_INT_CST_LOW (TYPE_SIZE (array_of)))); + else + strcat (str, "[]"); + } + + else if (code == CALL_EXPR) + { + tree chain = TREE_PURPOSE (TREE_OPERAND (decl, 1)); + + strcat (str, "("); + while (chain) + { + gen_declaration (chain, str); + chain = TREE_CHAIN (chain); + if (chain) + strcat (str, ", "); + } + strcat (str, ")"); + } + + else if (code == FUNCTION_TYPE) + { + tree chain = TYPE_ARG_TYPES (decl); + + strcat (str, "("); + while (chain && TREE_VALUE (chain) != void_type_node) + { + gen_declaration (TREE_VALUE (chain), str); + chain = TREE_CHAIN (chain); + if (chain && TREE_VALUE (chain) != void_type_node) + strcat (str, ", "); + } + strcat (str, ")"); + } + + else if (code == INDIRECT_REF) + { + strcpy (tmpbuf, "*"); + if (TREE_TYPE (decl) && TREE_CODE (TREE_TYPE (decl)) == TREE_LIST) + { + tree chain; + + for (chain = nreverse (copy_list (TREE_TYPE (decl))); + chain; + chain = TREE_CHAIN (chain)) + { + if (TREE_CODE (TREE_VALUE (chain)) == IDENTIFIER_NODE) + { + strcat (tmpbuf, " "); + strcat (tmpbuf, IDENTIFIER_POINTER (TREE_VALUE (chain))); + } + } + if (str[0]) + strcat (tmpbuf, " "); + } + strcat (tmpbuf, str); + strcpy (str, tmpbuf); + } + + else if (code == POINTER_TYPE) + { + strcpy (tmpbuf, "*"); + if (TREE_READONLY (decl) || TYPE_VOLATILE (decl)) + { + if (TREE_READONLY (decl)) + strcat (tmpbuf, " const"); + if (TYPE_VOLATILE (decl)) + strcat (tmpbuf, " volatile"); + if (str[0]) + strcat (tmpbuf, " "); + } + strcat (tmpbuf, str); + strcpy (str, tmpbuf); + } +} + +static char * +gen_declarator (decl, buf, name) + tree decl; + char *buf; + char *name; +{ + if (decl) + { + enum tree_code code = TREE_CODE (decl); + char *str; + tree op; + int wrap = 0; + + switch (code) + { + case ARRAY_REF: + case INDIRECT_REF: + case CALL_EXPR: + op = TREE_OPERAND (decl, 0); + + /* We have a pointer to a function or array...(*)(), (*)[] */ + if ((code == ARRAY_REF || code == CALL_EXPR) + && op && TREE_CODE (op) == INDIRECT_REF) + wrap = 1; + + str = gen_declarator (op, buf, name); + + if (wrap) + { + strcpy (tmpbuf, "("); + strcat (tmpbuf, str); + strcat (tmpbuf, ")"); + strcpy (str, tmpbuf); + } + + adorn_decl (decl, str); + break; + + case ARRAY_TYPE: + case FUNCTION_TYPE: + case POINTER_TYPE: + strcpy (buf, name); + str = buf; + + /* This clause is done iteratively rather than recursively. */ + do + { + op = (is_complex_decl (TREE_TYPE (decl)) + ? TREE_TYPE (decl) : NULL_TREE); + + adorn_decl (decl, str); + + /* We have a pointer to a function or array...(*)(), (*)[] */ + if (code == POINTER_TYPE + && op && (TREE_CODE (op) == FUNCTION_TYPE + || TREE_CODE (op) == ARRAY_TYPE)) + { + strcpy (tmpbuf, "("); + strcat (tmpbuf, str); + strcat (tmpbuf, ")"); + strcpy (str, tmpbuf); + } + + decl = (is_complex_decl (TREE_TYPE (decl)) + ? TREE_TYPE (decl) : NULL_TREE); + } + + while (decl && (code = TREE_CODE (decl))) + ; + + break; + + case IDENTIFIER_NODE: + /* Will only happen if we are processing a "raw" expr-decl. */ + strcpy (buf, IDENTIFIER_POINTER (decl)); + return buf; + } + + return str; + } + + else + /* We have an abstract declarator or a _DECL node. */ + { + strcpy (buf, name); + return buf; + } +} + +static void +gen_declspecs (declspecs, buf, raw) + tree declspecs; + char *buf; + int raw; +{ + if (raw) + { + tree chain; + + for (chain = nreverse (copy_list (declspecs)); + chain; chain = TREE_CHAIN (chain)) + { + tree aspec = TREE_VALUE (chain); + + if (TREE_CODE (aspec) == IDENTIFIER_NODE) + strcat (buf, IDENTIFIER_POINTER (aspec)); + else if (TREE_CODE (aspec) == RECORD_TYPE) + { + if (TYPE_NAME (aspec)) + { + tree protocol_list = TYPE_PROTOCOL_LIST (aspec); + + if (! TREE_STATIC_TEMPLATE (aspec)) + strcat (buf, "struct "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (aspec))); + + /* NEW!!! */ + if (protocol_list) + { + tree chain = protocol_list; + + strcat (buf, " <"); + while (chain) + { + strcat (buf, + IDENTIFIER_POINTER + (PROTOCOL_NAME (TREE_VALUE (chain)))); + chain = TREE_CHAIN (chain); + if (chain) + strcat (buf, ", "); + } + strcat (buf, ">"); + } + } + + else + strcat (buf, "untagged struct"); + } + + else if (TREE_CODE (aspec) == UNION_TYPE) + { + if (TYPE_NAME (aspec)) + { + if (! TREE_STATIC_TEMPLATE (aspec)) + strcat (buf, "union "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (aspec))); + } + else + strcat (buf, "untagged union"); + } + + else if (TREE_CODE (aspec) == ENUMERAL_TYPE) + { + if (TYPE_NAME (aspec)) + { + if (! TREE_STATIC_TEMPLATE (aspec)) + strcat (buf, "enum "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (aspec))); + } + else + strcat (buf, "untagged enum"); + } + + else if (TREE_CODE (aspec) == TYPE_DECL && DECL_NAME (aspec)) + strcat (buf, IDENTIFIER_POINTER (DECL_NAME (aspec))); + + else if (IS_ID (aspec)) + { + tree protocol_list = TYPE_PROTOCOL_LIST (aspec); + + strcat (buf, "id"); + if (protocol_list) + { + tree chain = protocol_list; + + strcat (buf, " <"); + while (chain) + { + strcat (buf, + IDENTIFIER_POINTER + (PROTOCOL_NAME (TREE_VALUE (chain)))); + chain = TREE_CHAIN (chain); + if (chain) + strcat (buf, ", "); + } + strcat (buf, ">"); + } + } + if (TREE_CHAIN (chain)) + strcat (buf, " "); + } + } + else + { + /* Type qualifiers. */ + if (TREE_READONLY (declspecs)) + strcat (buf, "const "); + if (TYPE_VOLATILE (declspecs)) + strcat (buf, "volatile "); + + switch (TREE_CODE (declspecs)) + { + /* Type specifiers. */ + + case INTEGER_TYPE: + declspecs = TYPE_MAIN_VARIANT (declspecs); + + /* Signed integer types. */ + + if (declspecs == short_integer_type_node) + strcat (buf, "short int "); + else if (declspecs == integer_type_node) + strcat (buf, "int "); + else if (declspecs == long_integer_type_node) + strcat (buf, "long int "); + else if (declspecs == long_long_integer_type_node) + strcat (buf, "long long int "); + else if (declspecs == signed_char_type_node + || declspecs == char_type_node) + strcat (buf, "char "); + + /* Unsigned integer types. */ + + else if (declspecs == short_unsigned_type_node) + strcat (buf, "unsigned short "); + else if (declspecs == unsigned_type_node) + strcat (buf, "unsigned int "); + else if (declspecs == long_unsigned_type_node) + strcat (buf, "unsigned long "); + else if (declspecs == long_long_unsigned_type_node) + strcat (buf, "unsigned long long "); + else if (declspecs == unsigned_char_type_node) + strcat (buf, "unsigned char "); + break; + + case REAL_TYPE: + declspecs = TYPE_MAIN_VARIANT (declspecs); + + if (declspecs == float_type_node) + strcat (buf, "float "); + else if (declspecs == double_type_node) + strcat (buf, "double "); + else if (declspecs == long_double_type_node) + strcat (buf, "long double "); + break; + + case RECORD_TYPE: + if (TYPE_NAME (declspecs) + && TREE_CODE (TYPE_NAME (declspecs)) == IDENTIFIER_NODE) + { + tree protocol_list = TYPE_PROTOCOL_LIST (declspecs); + + if (! TREE_STATIC_TEMPLATE (declspecs)) + strcat (buf, "struct "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (declspecs))); + + if (protocol_list) + { + tree chain = protocol_list; + + strcat (buf, " <"); + while (chain) + { + strcat (buf, + IDENTIFIER_POINTER + (PROTOCOL_NAME (TREE_VALUE (chain)))); + chain = TREE_CHAIN (chain); + if (chain) + strcat (buf, ", "); + } + strcat (buf, ">"); + } + } + + else + strcat (buf, "untagged struct"); + + strcat (buf, " "); + break; + + case UNION_TYPE: + if (TYPE_NAME (declspecs) + && TREE_CODE (TYPE_NAME (declspecs)) == IDENTIFIER_NODE) + { + strcat (buf, "union "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (declspecs))); + strcat (buf, " "); + } + + else + strcat (buf, "untagged union "); + break; + + case ENUMERAL_TYPE: + if (TYPE_NAME (declspecs) + && TREE_CODE (TYPE_NAME (declspecs)) == IDENTIFIER_NODE) + { + strcat (buf, "enum "); + strcat (buf, IDENTIFIER_POINTER (TYPE_NAME (declspecs))); + strcat (buf, " "); + } + + else + strcat (buf, "untagged enum "); + break; + + case VOID_TYPE: + strcat (buf, "void "); + break; + + case POINTER_TYPE: + { + tree protocol_list = TYPE_PROTOCOL_LIST (declspecs); + + strcat (buf, "id"); + if (protocol_list) + { + tree chain = protocol_list; + + strcat (buf, " <"); + while (chain) + { + strcat (buf, + IDENTIFIER_POINTER + (PROTOCOL_NAME (TREE_VALUE (chain)))); + chain = TREE_CHAIN (chain); + if (chain) + strcat (buf, ", "); + } + + strcat (buf, ">"); + } + } + } + } +} + +static char * +gen_declaration (atype_or_adecl, buf) + tree atype_or_adecl; + char *buf; +{ + char declbuf[256]; + + if (TREE_CODE (atype_or_adecl) == TREE_LIST) + { + tree declspecs; /* "identifier_node", "record_type" */ + tree declarator; /* "array_ref", "indirect_ref", "call_expr"... */ + + /* We have a "raw", abstract declarator (typename). */ + declarator = TREE_VALUE (atype_or_adecl); + declspecs = TREE_PURPOSE (atype_or_adecl); + + gen_declspecs (declspecs, buf, 1); + if (declarator) + { + strcat (buf, " "); + strcat (buf, gen_declarator (declarator, declbuf, "")); + } + } + + else + { + tree atype; + tree declspecs; /* "integer_type", "real_type", "record_type"... */ + tree declarator; /* "array_type", "function_type", "pointer_type". */ + + if (TREE_CODE (atype_or_adecl) == FIELD_DECL + || TREE_CODE (atype_or_adecl) == PARM_DECL + || TREE_CODE (atype_or_adecl) == FUNCTION_DECL) + atype = TREE_TYPE (atype_or_adecl); + else + /* Assume we have a *_type node. */ + atype = atype_or_adecl; + + if (is_complex_decl (atype)) + { + tree chain; + + /* Get the declaration specifier; it is at the end of the list. */ + declarator = chain = atype; + do + chain = TREE_TYPE (chain); /* not TREE_CHAIN (chain); */ + while (is_complex_decl (chain)); + declspecs = chain; + } + + else + { + declspecs = atype; + declarator = NULL_TREE; + } + + gen_declspecs (declspecs, buf, 0); + + if (TREE_CODE (atype_or_adecl) == FIELD_DECL + || TREE_CODE (atype_or_adecl) == PARM_DECL + || TREE_CODE (atype_or_adecl) == FUNCTION_DECL) + { + char *decl_name = (DECL_NAME (atype_or_adecl) + ? IDENTIFIER_POINTER (DECL_NAME (atype_or_adecl)) + : ""); + + if (declarator) + { + strcat (buf, " "); + strcat (buf, gen_declarator (declarator, declbuf, decl_name)); + } + + else if (decl_name[0]) + { + strcat (buf, " "); + strcat (buf, decl_name); + } + } + else if (declarator) + { + strcat (buf, " "); + strcat (buf, gen_declarator (declarator, declbuf, "")); + } + } + + return buf; +} + +#define RAW_TYPESPEC(meth) (TREE_VALUE (TREE_PURPOSE (TREE_TYPE (meth)))) + +static char * +gen_method_decl (method, buf) + tree method; + char *buf; +{ + tree chain; + + if (RAW_TYPESPEC (method) != objc_object_reference) + { + strcpy (buf, "("); + gen_declaration (TREE_TYPE (method), buf); + strcat (buf, ")"); + } + + chain = METHOD_SEL_ARGS (method); + if (chain) + { + /* We have a chain of keyword_decls. */ + do + { + if (KEYWORD_KEY_NAME (chain)) + strcat (buf, IDENTIFIER_POINTER (KEYWORD_KEY_NAME (chain))); + + strcat (buf, ":"); + if (RAW_TYPESPEC (chain) != objc_object_reference) + { + strcat (buf, "("); + gen_declaration (TREE_TYPE (chain), buf); + strcat (buf, ")"); + } + + strcat (buf, IDENTIFIER_POINTER (KEYWORD_ARG_NAME (chain))); + if ((chain = TREE_CHAIN (chain))) + strcat (buf, " "); + } + while (chain); + + if (METHOD_ADD_ARGS (method) == (tree)1) + strcat (buf, ", ..."); + else if (METHOD_ADD_ARGS (method)) + { + /* We have a tree list node as generate by get_parm_info. */ + chain = TREE_PURPOSE (METHOD_ADD_ARGS (method)); + + /* Know we have a chain of parm_decls. */ + while (chain) + { + strcat (buf, ", "); + gen_declaration (chain, buf); + chain = TREE_CHAIN (chain); + } + } + } + + else + /* We have a unary selector. */ + strcat (buf, IDENTIFIER_POINTER (METHOD_SEL_NAME (method))); + + return buf; +} + +/* Debug info. */ + +static void +dump_interface (fp, chain) + FILE *fp; + tree chain; +{ + char *buf = (char *)xmalloc (256); + char *my_name = IDENTIFIER_POINTER (CLASS_NAME (chain)); + tree ivar_decls = CLASS_RAW_IVARS (chain); + tree nst_methods = CLASS_NST_METHODS (chain); + tree cls_methods = CLASS_CLS_METHODS (chain); + + fprintf (fp, "\n@interface %s", my_name); + + if (CLASS_SUPER_NAME (chain)) + { + char *super_name = IDENTIFIER_POINTER (CLASS_SUPER_NAME (chain)); + fprintf (fp, " : %s\n", super_name); + } + else + fprintf (fp, "\n"); + + if (ivar_decls) + { + fprintf (fp, "{\n"); + do + { + bzero (buf, 256); + fprintf (fp, "\t%s;\n", gen_declaration (ivar_decls, buf)); + ivar_decls = TREE_CHAIN (ivar_decls); + } + while (ivar_decls); + fprintf (fp, "}\n"); + } + + while (nst_methods) + { + bzero (buf, 256); + fprintf (fp, "- %s;\n", gen_method_decl (nst_methods, buf)); + nst_methods = TREE_CHAIN (nst_methods); + } + + while (cls_methods) + { + bzero (buf, 256); + fprintf (fp, "+ %s;\n", gen_method_decl (cls_methods, buf)); + cls_methods = TREE_CHAIN (cls_methods); + } + fprintf (fp, "\n@end"); +} + +/* Demangle function for Objective-C */ +static const char * +objc_demangle (mangled) + const char *mangled; +{ + char *demangled, *cp; + + if (mangled[0] == '_' && + (mangled[1] == 'i' || mangled[1] == 'c') && + mangled[2] == '_') + { + cp = demangled = xmalloc(strlen(mangled) + 2); + if (mangled[1] == 'i') + *cp++ = '-'; /* for instance method */ + else + *cp++ = '+'; /* for class method */ + *cp++ = '['; /* opening left brace */ + strcpy(cp, mangled+3); /* tack on the rest of the mangled name */ + while (*cp && *cp == '_') + cp++; /* skip any initial underbars in class name */ + cp = strchr(cp, '_'); /* find first non-initial underbar */ + if (cp == NULL) + { + free(demangled); /* not mangled name */ + return mangled; + } + if (cp[1] == '_') /* easy case: no category name */ + { + *cp++ = ' '; /* replace two '_' with one ' ' */ + strcpy(cp, mangled + (cp - demangled) + 2); + } + else + { + *cp++ = '('; /* less easy case: category name */ + cp = strchr(cp, '_'); + if (cp == 0) + { + free(demangled); /* not mangled name */ + return mangled; + } + *cp++ = ')'; + *cp++ = ' '; /* overwriting 1st char of method name... */ + strcpy(cp, mangled + (cp - demangled)); /* get it back */ + } + while (*cp && *cp == '_') + cp++; /* skip any initial underbars in method name */ + for (; *cp; cp++) + if (*cp == '_') + *cp = ':'; /* replace remaining '_' with ':' */ + *cp++ = ']'; /* closing right brace */ + *cp++ = 0; /* string terminator */ + return demangled; + } + else + return mangled; /* not an objc mangled name */ +} + +static const char * +objc_printable_name (decl, kind) + tree decl; + char **kind; +{ + return objc_demangle (IDENTIFIER_POINTER (DECL_NAME (decl))); +} + +static void +init_objc () +{ + /* Add the special tree codes of Objective C to the tables. */ + +#define LAST_CODE LAST_AND_UNUSED_TREE_CODE + + gcc_obstack_init (&util_obstack); + util_firstobj = (char *) obstack_finish (&util_obstack); + + tree_code_type + = (char **) xrealloc (tree_code_type, + sizeof (char *) * LAST_OBJC_TREE_CODE); + tree_code_length + = (int *) xrealloc (tree_code_length, + sizeof (int) * LAST_OBJC_TREE_CODE); + tree_code_name + = (char **) xrealloc (tree_code_name, + sizeof (char *) * LAST_OBJC_TREE_CODE); + bcopy ((char *) objc_tree_code_type, + (char *) (tree_code_type + (int) LAST_CODE), + (((int) LAST_OBJC_TREE_CODE - (int) LAST_CODE) + * sizeof (char *))); + bcopy ((char *) objc_tree_code_length, + (char *) (tree_code_length + (int) LAST_CODE), + (((int) LAST_OBJC_TREE_CODE - (int) LAST_CODE) + * sizeof (int))); + bcopy ((char *) objc_tree_code_name, + (char *) (tree_code_name + (int) LAST_CODE), + (((int) LAST_OBJC_TREE_CODE - (int) LAST_CODE) + * sizeof (char *))); + + errbuf = (char *)xmalloc (BUFSIZE); + hash_init (); + synth_module_prologue (); + + /* Change the default error function */ + decl_printable_name = (char* (*)()) objc_printable_name; +} + +static void +finish_objc () +{ + struct imp_entry *impent; + tree chain; + /* The internally generated initializers appear to have missing braces. + Don't warn about this. */ + int save_warn_missing_braces = warn_missing_braces; + warn_missing_braces = 0; + + generate_forward_declaration_to_string_table (); + +#ifdef OBJC_PROLOGUE + OBJC_PROLOGUE; +#endif + + /* Process the static instances here because initialization of objc_symtab + depends on them. */ + if (objc_static_instances) + generate_static_references (); + + if (implementation_context || class_names_chain + || meth_var_names_chain || meth_var_types_chain || sel_ref_chain) + generate_objc_symtab_decl (); + + for (impent = imp_list; impent; impent = impent->next) + { + implementation_context = impent->imp_context; + implementation_template = impent->imp_template; + + UOBJC_CLASS_decl = impent->class_decl; + UOBJC_METACLASS_decl = impent->meta_decl; + + if (TREE_CODE (implementation_context) == CLASS_IMPLEMENTATION_TYPE) + { + /* all of the following reference the string pool... */ + generate_ivar_lists (); + generate_dispatch_tables (); + generate_shared_structures (); + } + else + { + generate_dispatch_tables (); + generate_category (implementation_context); + } + } + + /* If we are using an array of selectors, we must always + finish up the array decl even if no selectors were used. */ + if (! flag_next_runtime || sel_ref_chain) + build_selector_translation_table (); + + if (protocol_chain) + generate_protocols (); + + if (implementation_context || class_names_chain || objc_static_instances + || meth_var_names_chain || meth_var_types_chain || sel_ref_chain) + { + /* Arrange for Objc data structures to be initialized at run time. */ + char *init_name = build_module_descriptor (); + if (init_name) + assemble_constructor (init_name); + } + + /* Dump the class references. This forces the appropriate classes + to be linked into the executable image, preserving unix archive + semantics. This can be removed when we move to a more dynamically + linked environment. */ + + for (chain = cls_ref_chain; chain; chain = TREE_CHAIN (chain)) + { + handle_class_ref (chain); + if (TREE_PURPOSE (chain)) + generate_classref_translation_entry (chain); + } + + for (impent = imp_list; impent; impent = impent->next) + handle_impent (impent); + + /* Dump the string table last. */ + + generate_strings (); + + if (flag_gen_declaration) + { + add_class (implementation_context); + dump_interface (gen_declaration_file, implementation_context); + } + + if (warn_selector) + { + int slot; + hash hsh; + + /* Run through the selector hash tables and print a warning for any + selector which has multiple methods. */ + + for (slot = 0; slot < SIZEHASHTABLE; slot++) + for (hsh = cls_method_hash_list[slot]; hsh; hsh = hsh->next) + if (hsh->list) + { + tree meth = hsh->key; + char type = (TREE_CODE (meth) == INSTANCE_METHOD_DECL + ? '-' : '+'); + attr loop; + + warning ("potential selector conflict for method `%s'", + IDENTIFIER_POINTER (METHOD_SEL_NAME (meth))); + warn_with_method ("found", type, meth); + for (loop = hsh->list; loop; loop = loop->next) + warn_with_method ("found", type, loop->value); + } + + for (slot = 0; slot < SIZEHASHTABLE; slot++) + for (hsh = nst_method_hash_list[slot]; hsh; hsh = hsh->next) + if (hsh->list) + { + tree meth = hsh->key; + char type = (TREE_CODE (meth) == INSTANCE_METHOD_DECL + ? '-' : '+'); + attr loop; + + warning ("potential selector conflict for method `%s'", + IDENTIFIER_POINTER (METHOD_SEL_NAME (meth))); + warn_with_method ("found", type, meth); + for (loop = hsh->list; loop; loop = loop->next) + warn_with_method ("found", type, loop->value); + } + } + + warn_missing_braces = save_warn_missing_braces; +} + +/* Subroutines of finish_objc. */ + +static void +generate_classref_translation_entry (chain) + tree chain; +{ + tree expr, name, decl_specs, decl, sc_spec; + tree type; + + type = TREE_TYPE (TREE_PURPOSE (chain)); + + expr = add_objc_string (TREE_VALUE (chain), class_names); + expr = build_c_cast (type, expr); /* cast! */ + + name = DECL_NAME (TREE_PURPOSE (chain)); + + sc_spec = build_tree_list (NULL_TREE, ridpointers[(int) RID_STATIC]); + + /* static struct objc_class * _OBJC_CLASS_REFERENCES_n = ...; */ + decl_specs = tree_cons (NULL_TREE, type, sc_spec); + + /* The decl that is returned from start_decl is the one that we + forward declared in build_class_reference. */ + decl = start_decl (name, decl_specs, 1, NULL_TREE, NULL_TREE); + finish_decl (decl, expr, NULL_TREE); + return; +} + +static void +handle_class_ref (chain) + tree chain; +{ + char *name = IDENTIFIER_POINTER (TREE_VALUE (chain)); + if (! flag_next_runtime) + { + tree decl; + char *string = (char *) alloca (strlen (name) + 30); + tree exp; + + sprintf (string, "%sobjc_class_name_%s", + (flag_next_runtime ? "." : "__"), name); + + /* Make a decl for this name, so we can use its address in a tree. */ + decl = build_decl (VAR_DECL, get_identifier (string), char_type_node); + DECL_EXTERNAL (decl) = 1; + TREE_PUBLIC (decl) = 1; + + pushdecl (decl); + rest_of_decl_compilation (decl, 0, 0, 0); + + /* Make following constant read-only (why not)? */ + readonly_data_section (); + + exp = build1 (ADDR_EXPR, string_type_node, decl); + + /* Align the section properly. */ + assemble_constant_align (exp); + + /* Inform the assembler about this new external thing. */ + assemble_external (decl); + + /* Output a constant to reference this address. */ + output_constant (exp, int_size_in_bytes (string_type_node)); + } + else + { + /* This overreliance on our assembler (i.e. lack of portability) + should be dealt with at some point. The GNU strategy (above) + won't work either, but it is a start. */ + char *string = (char *) alloca (strlen (name) + 30); + sprintf (string, ".reference .objc_class_name_%s", name); + assemble_asm (my_build_string (strlen (string) + 1, string)); + } +} + +static void +handle_impent (impent) + struct imp_entry *impent; +{ + implementation_context = impent->imp_context; + implementation_template = impent->imp_template; + + if (TREE_CODE (impent->imp_context) == CLASS_IMPLEMENTATION_TYPE) + { + char *class_name = IDENTIFIER_POINTER (CLASS_NAME (impent->imp_context)); + char *string = (char *) alloca (strlen (class_name) + 30); + + if (flag_next_runtime) + { + /* Grossly unportable. + People should know better than to assume + such things about assembler syntax! */ + sprintf (string, ".objc_class_name_%s=0", class_name); + assemble_asm (my_build_string (strlen (string) + 1, string)); + + sprintf (string, ".globl .objc_class_name_%s", class_name); + assemble_asm (my_build_string (strlen (string) + 1, string)); + } + + else + { + sprintf (string, "%sobjc_class_name_%s", + (flag_next_runtime ? "." : "__"), class_name); + assemble_global (string); + assemble_label (string); + } + } + + else if (TREE_CODE (impent->imp_context) == CATEGORY_IMPLEMENTATION_TYPE) + { + char *class_name = IDENTIFIER_POINTER (CLASS_NAME (impent->imp_context)); + char *class_super_name + = IDENTIFIER_POINTER (CLASS_SUPER_NAME (impent->imp_context)); + char *string = (char *) alloca (strlen (class_name) + + strlen (class_super_name) + 30); + + /* Do the same for categories. Even though no references to these + symbols are generated automatically by the compiler, it gives + you a handle to pull them into an archive by hand. */ + if (flag_next_runtime) + { + /* Grossly unportable. */ + sprintf (string, ".objc_category_name_%s_%s=0", + class_name, class_super_name); + assemble_asm (my_build_string (strlen (string) + 1, string)); + + sprintf (string, ".globl .objc_category_name_%s_%s", + class_name, class_super_name); + assemble_asm (my_build_string (strlen (string) + 1, string)); + } + + else + { + sprintf (string, "%sobjc_category_name_%s_%s", + (flag_next_runtime ? "." : "__"), + class_name, class_super_name); + assemble_global (string); + assemble_label (string); + } + } +} + +#ifdef DEBUG + +static void +objc_debug (fp) + FILE *fp; +{ + char *buf = (char *)xmalloc (256); + + { /* dump function prototypes */ + tree loop = UOBJC_MODULES_decl; + + fprintf (fp, "\n\nfunction prototypes:\n"); + while (loop) + { + if (TREE_CODE (loop) == FUNCTION_DECL && DECL_INITIAL (loop)) + { + /* We have a function definition: generate prototype. */ + bzero (errbuf, BUFSIZE); + gen_declaration (loop, errbuf); + fprintf (fp, "%s;\n", errbuf); + } + loop = TREE_CHAIN (loop); + } + } + { + /* Dump global chains. */ + tree loop; + int i, index = 0, offset = 0; + hash hashlist; + + for (i = 0; i < SIZEHASHTABLE; i++) + { + if (hashlist = nst_method_hash_list[i]) + { + fprintf (fp, "\n\nnst_method_hash_list[%d]:\n", i); + do + { + bzero (buf, 256); + fprintf (fp, "-%s;\n", gen_method_decl (hashlist->key, buf)); + hashlist = hashlist->next; + } + while (hashlist); + } + } + + for (i = 0; i < SIZEHASHTABLE; i++) + { + if (hashlist = cls_method_hash_list[i]) + { + fprintf (fp, "\n\ncls_method_hash_list[%d]:\n", i); + do + { + bzero (buf, 256); + fprintf (fp, "-%s;\n", gen_method_decl (hashlist->key, buf)); + hashlist = hashlist->next; + } + while (hashlist); + } + } + + fprintf (fp, "\nsel_refdef_chain:\n"); + for (loop = sel_refdef_chain; loop; loop = TREE_CHAIN (loop)) + { + fprintf (fp, "(index: %4d offset: %4d) %s\n", index, offset, + IDENTIFIER_POINTER (TREE_VALUE (loop))); + index++; + /* add one for the '\0' character */ + offset += IDENTIFIER_LENGTH (TREE_VALUE (loop)) + 1; + } + + fprintf (fp, "\n (max_selector_index: %4d.\n", max_selector_index); + } +} +#endif + +void +print_lang_statistics () +{ +} diff --git a/gnu/usr.bin/gcc/objc/objc-act.h b/gnu/usr.bin/gcc/objc/objc-act.h new file mode 100644 index 00000000000..65224de84f4 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/objc-act.h @@ -0,0 +1,117 @@ +/* Declarations for objc-act.c. + Copyright (C) 1990 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/*** Public Interface (procedures) ***/ + +/* used by yyparse */ + +void finish_file PROTO((void)); +tree start_class PROTO((enum tree_code, tree, tree, tree)); +tree continue_class PROTO((tree)); +void finish_class PROTO((tree)); +void start_method_def PROTO((tree)); +void continue_method_def PROTO((void)); +void finish_method_def PROTO((void)); +tree start_protocol PROTO((enum tree_code, tree, tree)); +void finish_protocol PROTO((tree)); +void add_objc_decls PROTO((void)); + +tree is_ivar PROTO((tree, tree)); +int is_private PROTO((tree)); +int is_public PROTO((tree, tree)); +tree add_instance_variable PROTO((tree, int, tree, tree, tree)); +tree add_class_method PROTO((tree, tree)); +tree add_instance_method PROTO((tree, tree)); +tree get_super_receiver PROTO((void)); +tree get_class_ivars PROTO((tree)); +tree get_class_reference PROTO((tree)); +tree get_static_reference PROTO((tree, tree)); +tree get_object_reference PROTO((tree)); +tree build_message_expr PROTO((tree)); +tree build_selector_expr PROTO((tree)); +tree build_ivar_reference PROTO((tree)); +tree build_keyword_decl PROTO((tree, tree, tree)); +tree build_method_decl PROTO((enum tree_code, tree, tree, tree)); +tree build_protocol_expr PROTO((tree)); +tree build_objc_string_object PROTO((tree)); + +extern tree objc_ivar_chain; +extern tree objc_method_context; + +void objc_declare_alias PROTO((tree, tree)); +void objc_declare_class PROTO((tree)); + +extern int objc_receiver_context; + +/* the following routines are used to implement statically typed objects */ + +int objc_comptypes PROTO((tree, tree, int)); +void objc_check_decl PROTO((tree)); + +/* NeXT extensions */ + +tree build_encode_expr PROTO((tree)); + +/* Objective-C structures */ + +/* KEYWORD_DECL */ +#define KEYWORD_KEY_NAME(DECL) ((DECL)->decl.name) +#define KEYWORD_ARG_NAME(DECL) ((DECL)->decl.arguments) + +/* INSTANCE_METHOD_DECL, CLASS_METHOD_DECL */ +#define METHOD_SEL_NAME(DECL) ((DECL)->decl.name) +#define METHOD_SEL_ARGS(DECL) ((DECL)->decl.arguments) +#define METHOD_ADD_ARGS(DECL) ((DECL)->decl.result) +#define METHOD_DEFINITION(DECL) ((DECL)->decl.initial) +#define METHOD_ENCODING(DECL) ((DECL)->decl.context) + +/* CLASS_INTERFACE_TYPE, CLASS_IMPLEMENTATION_TYPE, + CATEGORY_INTERFACE_TYPE, CATEGORY_IMPLEMENTATION_TYPE, + PROTOCOL_INTERFACE_TYPE */ +#define CLASS_NAME(CLASS) ((CLASS)->type.name) +#define CLASS_SUPER_NAME(CLASS) ((CLASS)->type.context) +#define CLASS_IVARS(CLASS) TREE_VEC_ELT (TYPE_BINFO (CLASS), 0) +#define CLASS_RAW_IVARS(CLASS) TREE_VEC_ELT (TYPE_BINFO (CLASS), 1) +#define CLASS_NST_METHODS(CLASS) ((CLASS)->type.minval) +#define CLASS_CLS_METHODS(CLASS) ((CLASS)->type.maxval) +#define CLASS_STATIC_TEMPLATE(CLASS) TREE_VEC_ELT (TYPE_BINFO (CLASS), 2) +#define CLASS_CATEGORY_LIST(CLASS) TREE_VEC_ELT (TYPE_BINFO (CLASS), 3) +#define CLASS_PROTOCOL_LIST(CLASS) TREE_VEC_ELT (TYPE_BINFO (CLASS), 4) +#define PROTOCOL_NAME(CLASS) ((CLASS)->type.name) +#define PROTOCOL_LIST(CLASS) TREE_VEC_ELT (TYPE_BINFO (CLASS), 0) +#define PROTOCOL_NST_METHODS(CLASS) ((CLASS)->type.minval) +#define PROTOCOL_CLS_METHODS(CLASS) ((CLASS)->type.maxval) +#define PROTOCOL_FORWARD_DECL(CLASS) TREE_VEC_ELT (TYPE_BINFO (CLASS), 1) +#define TYPE_PROTOCOL_LIST(TYPE) ((TYPE)->type.context) + +/* Define the Objective-C or Objective-C++ language-specific tree codes. */ + +#define DEFTREECODE(SYM, NAME, TYPE, LENGTH) SYM, +enum objc_tree_code { +#ifdef OBJCPLUS + dummy_tree_code = LAST_CPLUS_TREE_CODE, +#else + dummy_tree_code = LAST_AND_UNUSED_TREE_CODE, +#endif +#include "objc-tree.def" + LAST_OBJC_TREE_CODE +}; +#undef DEFTREECODE diff --git a/gnu/usr.bin/gcc/objc/objc-api.h b/gnu/usr.bin/gcc/objc/objc-api.h index c801033abe6..860aee4206a 100644 --- a/gnu/usr.bin/gcc/objc/objc-api.h +++ b/gnu/usr.bin/gcc/objc/objc-api.h @@ -1,5 +1,5 @@ /* GNU Objective-C Runtime API. - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of GNU CC. @@ -29,7 +29,9 @@ Boston, MA 02111-1307, USA. */ #include "objc/objc.h" #include "objc/hash.h" +#include "objc/thr.h" #include <stdio.h> +#include <stdarg.h> /* For functions which return Method_t */ #define METHOD_NULL (Method_t)0 @@ -73,6 +75,59 @@ struct objc_method_description #define _C_STRUCT_E '}' +/* +** Error handling +** +** Call objc_error() or objc_verror() to record an error; this error +** routine will generally exit the program but not necessarily if the +** user has installed his own error handler. +** +** Call objc_set_error_handler to assign your own function for +** handling errors. The function should return YES if it is ok +** to continue execution, or return NO or just abort if the +** program should be stopped. The default error handler is just to +** print a message on stderr. +** +** The error handler function should be of type objc_error_handler +** The first parameter is an object instance of relevance. +** The second parameter is an error code. +** The third parameter is a format string in the printf style. +** The fourth parameter is a variable list of arguments. +*/ +extern void objc_error(id object, int code, const char* fmt, ...); +extern void objc_verror(id object, int code, const char* fmt, va_list ap); +typedef BOOL (*objc_error_handler)(id, int code, const char *fmt, va_list ap); +objc_error_handler objc_set_error_handler(objc_error_handler func); + +/* +** Error codes +** These are used by the runtime library, and your +** error handling may use them to determine if the error is +** hard or soft thus whether execution can continue or abort. +*/ +#define OBJC_ERR_UNKNOWN 0 /* Generic error */ + +#define OBJC_ERR_OBJC_VERSION 1 /* Incorrect runtime version */ +#define OBJC_ERR_GCC_VERSION 2 /* Incorrect compiler version */ +#define OBJC_ERR_MODULE_SIZE 3 /* Bad module size */ +#define OBJC_ERR_PROTOCOL_VERSION 4 /* Incorrect protocol version */ + +#define OBJC_ERR_MEMORY 10 /* Out of memory */ + +#define OBJC_ERR_RECURSE_ROOT 20 /* Attempt to archive the root + object more than once. */ +#define OBJC_ERR_BAD_DATA 21 /* Didn't read expected data */ +#define OBJC_ERR_BAD_KEY 22 /* Bad key for object */ +#define OBJC_ERR_BAD_CLASS 23 /* Unknown class */ +#define OBJC_ERR_BAD_TYPE 24 /* Bad type specification */ +#define OBJC_ERR_NO_READ 25 /* Cannot read stream */ +#define OBJC_ERR_NO_WRITE 26 /* Cannot write stream */ +#define OBJC_ERR_STREAM_VERSION 27 /* Incorrect stream version */ +#define OBJC_ERR_BAD_OPCODE 28 /* Bad opcode */ + +#define OBJC_ERR_UNIMPLEMENTED 30 /* Method is not implemented */ + +#define OBJC_ERR_BAD_STATE 40 /* Bad thread state */ /* ** Set this variable nonzero to print a line describing each @@ -81,6 +136,16 @@ struct objc_method_description extern BOOL objc_trace; +/* For every class which happens to have statically allocated instances in + this module, one OBJC_STATIC_INSTANCES is allocated by the compiler. + INSTANCES is NULL terminated and points to all statically allocated + instances of this class. */ +struct objc_static_instances +{ + char *class_name; + id instances[0]; +}; + /* ** Whereas a Module (defined further down) is the root (typically) of a file, ** a Symtab is the root of the class and category definitions within the @@ -97,23 +162,16 @@ typedef struct objc_symtab { unsigned short cat_def_cnt; /* Number of categories compiled (defined) in the module. */ + void *defs[1]; /* Variable array of pointers. cls_def_cnt of type Class followed by cat_def_cnt of - type Category_t. */ + type Category_t, followed + by a NULL terminated array + of objc_static_instances. */ } Symtab, *Symtab_t; -/* For every class which happens to have statically allocated instances in - this module, one OBJC_STATIC_INSTANCES is allocated by the compiler. - INSTANCES is NULL terminated and points to all statically allocated - instances of this class. */ -struct objc_static_instances -{ - char *class_name; - id instances[0]; -}; - /* ** The compiler generates one of these structures for each module that ** composes the executable (eg main.m). @@ -130,12 +188,10 @@ typedef struct objc_module { module was generated. The name includes the path. */ - /* Pointer to a NULL terminated array of objc_static_instances. */ - struct objc_static_instances **statics; - Symtab_t symtab; /* Pointer to the Symtab of the module. The Symtab - holds an array of pointers to + holds an array of + pointers to the classes and categories defined in the module. */ } Module, *Module_t; @@ -308,12 +364,54 @@ extern Class (*_objc_lookup_class)(const char *name); */ extern void (*_objc_load_callback)(Class class, Category* category); +/* +** Hook functions for allocating, copying and disposing of instances +*/ extern id (*_objc_object_alloc)(Class class); - extern id (*_objc_object_copy)(id object); - extern id (*_objc_object_dispose)(id object); +/* +** Standard functions for memory allocation and disposal. +** Users should use these functions in their ObjC programs so +** that they work properly with garbage collectors as well as +** can take advantage of the exception/error handling available. +*/ +void * +objc_malloc(size_t size); + +void * +objc_atomic_malloc(size_t size); + +void * +objc_valloc(size_t size); + +void * +objc_realloc(void *mem, size_t size); + +void * +objc_calloc(size_t nelem, size_t size); + +void +objc_free(void *mem); + +/* +** Hook functions for memory allocation and disposal. +** This makes it easy to substitute garbage collection systems +** such as Boehm's GC by assigning these function pointers +** to the GC's allocation routines. By default these point +** to the ANSI standard malloc, realloc, free, etc. +** +** Users should call the normal objc routines above for +** memory allocation and disposal within their programs. +*/ +extern void *(*_objc_malloc)(size_t); +extern void *(*_objc_atomic_malloc)(size_t); +extern void *(*_objc_valloc)(size_t); +extern void *(*_objc_realloc)(void *, size_t); +extern void *(*_objc_calloc)(size_t, size_t); +extern void (*_objc_free)(void *); + Method_t class_get_class_method(MetaClass class, SEL aSel); Method_t class_get_instance_method(Class class, SEL aSel); @@ -405,6 +503,12 @@ method_get_imp(Method_t method) IMP get_imp (Class class, SEL sel); +/* Redefine on NeXTSTEP so as not to conflict with system function */ +#ifdef __NeXT__ +#define object_copy gnu_object_copy +#define object_dispose gnu_object_dispose +#endif + id object_copy(id object); id object_dispose(id object); @@ -471,6 +575,9 @@ object_is_meta_class(id object) return CLS_ISMETA((Class)object); } +struct sarray* +objc_get_uninstalled_dtable(void); + #endif /* not __objc_api_INCLUDE_GNU */ diff --git a/gnu/usr.bin/gcc/objc/objc-list.h b/gnu/usr.bin/gcc/objc/objc-list.h new file mode 100644 index 00000000000..19760906238 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/objc-list.h @@ -0,0 +1,147 @@ +/* Generic single linked list to keep various information + Copyright (C) 1993, 1994, 1996 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#ifndef __GNU_OBJC_LIST_H +#define __GNU_OBJC_LIST_H + +struct objc_list { + void *head; + struct objc_list *tail; +}; + +/* Return a cons cell produced from (head . tail) */ + +static inline struct objc_list* +list_cons(void* head, struct objc_list* tail) +{ + struct objc_list* cell; + + cell = (struct objc_list*)objc_malloc(sizeof(struct objc_list)); + cell->head = head; + cell->tail = tail; + return cell; +} + +/* Return the length of a list, list_length(NULL) returns zero */ + +static inline int +list_length(struct objc_list* list) +{ + int i = 0; + while(list) + { + i += 1; + list = list->tail; + } + return i; +} + +/* Return the Nth element of LIST, where N count from zero. If N + larger than the list length, NULL is returned */ + +static inline void* +list_nth(int index, struct objc_list* list) +{ + while(index-- != 0) + { + if(list->tail) + list = list->tail; + else + return 0; + } + return list->head; +} + +/* Remove the element at the head by replacing it by its successor */ + +static inline void +list_remove_head(struct objc_list** list) +{ + if ((*list)->tail) + { + struct objc_list* tail = (*list)->tail; /* fetch next */ + *(*list) = *tail; /* copy next to list head */ + objc_free(tail); /* free next */ + } + else /* only one element in list */ + { + objc_free(*list); + (*list) = 0; + } +} + + +/* Remove the element with `car' set to ELEMENT */ + +static inline void +list_remove_elem(struct objc_list** list, void* elem) +{ + while (*list) { + if ((*list)->head == elem) + list_remove_head(list); + list = &((*list)->tail); + } +} + +/* Map FUNCTION over all elements in LIST */ + +static inline void +list_mapcar(struct objc_list* list, void(*function)(void*)) +{ + while(list) + { + (*function)(list->head); + list = list->tail; + } +} + +/* Return element that has ELEM as car */ + +static inline struct objc_list** +list_find(struct objc_list** list, void* elem) +{ + while(*list) + { + if ((*list)->head == elem) + return list; + list = &((*list)->tail); + } + return NULL; +} + +/* Free list (backwards recursive) */ + +static void +list_free(struct objc_list* list) +{ + if(list) + { + list_free(list->tail); + objc_free(list); + } +} +#endif __GNU_OBJC_LIST_H diff --git a/gnu/usr.bin/gcc/objc/objc-tree.def b/gnu/usr.bin/gcc/objc/objc-tree.def new file mode 100644 index 00000000000..03f0c715776 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/objc-tree.def @@ -0,0 +1,37 @@ +/* This file contains the definitions and documentation for the + additional tree codes used in the Objective C front end (see tree.def + for the standard codes). + Copyright (C) 1990 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/* Objective-C types. */ +DEFTREECODE (CLASS_INTERFACE_TYPE, "class_interface_type", "t", 0) +DEFTREECODE (CLASS_IMPLEMENTATION_TYPE, "class_implementation_type", "t", 0) +DEFTREECODE (CATEGORY_INTERFACE_TYPE, "category_interface_type", "t", 0) +DEFTREECODE (CATEGORY_IMPLEMENTATION_TYPE,"category_implementation_type","t",0) +DEFTREECODE (PROTOCOL_INTERFACE_TYPE, "protocol_interface_type", "t", 0) + +/* Objective-C decls. */ +DEFTREECODE (KEYWORD_DECL, "keyword_decl", "d", 0) +DEFTREECODE (INSTANCE_METHOD_DECL, "instance_method_decl", "d", 0) +DEFTREECODE (CLASS_METHOD_DECL, "class_method_decl", "d", 0) + +/* Objective-C constants. */ +DEFTREECODE (OBJC_STRING_CST, "objc_string_cst", "c", 3) diff --git a/gnu/usr.bin/gcc/objc/objc.gperf b/gnu/usr.bin/gcc/objc/objc.gperf new file mode 100644 index 00000000000..407459f1589 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/objc.gperf @@ -0,0 +1,64 @@ +%{ +/* Command-line: gperf -p -j1 -i 1 -g -o -t -N is_reserved_word -k1,3,$ objc.gperf */ +%} +struct resword { char *name; short token; enum rid rid; }; +%% +@defs, DEFS, NORID +@encode, ENCODE, NORID +@end, END, NORID +@implementation, IMPLEMENTATION, NORID +@interface, INTERFACE, NORID +@public, PUBLIC, NORID +@selector, SELECTOR, NORID +__alignof, ALIGNOF, NORID +__alignof__, ALIGNOF, NORID +__asm, ASM, NORID +__asm__, ASM, NORID +__attribute, ATTRIBUTE, NORID +__attribute__, ATTRIBUTE, NORID +__const, TYPE_QUAL, RID_CONST +__const__, TYPE_QUAL, RID_CONST +__extension__, EXTENSION, NORID +__inline, SCSPEC, RID_INLINE +__inline__, SCSPEC, RID_INLINE +__signed, TYPESPEC, RID_SIGNED +__signed__, TYPESPEC, RID_SIGNED +__typeof, TYPEOF, NORID +__typeof__, TYPEOF, NORID +__volatile, TYPE_QUAL, RID_VOLATILE +__volatile__, TYPE_QUAL, RID_VOLATILE +asm, ASM, NORID +auto, SCSPEC, RID_AUTO +break, BREAK, NORID +case, CASE, NORID +char, TYPESPEC, RID_CHAR +const, TYPE_QUAL, RID_CONST +continue, CONTINUE, NORID +default, DEFAULT, NORID +do, DO, NORID +double, TYPESPEC, RID_DOUBLE +else, ELSE, NORID +enum, ENUM, NORID +extern, SCSPEC, RID_EXTERN +float, TYPESPEC, RID_FLOAT +for, FOR, NORID +goto, GOTO, NORID +if, IF, NORID +inline, SCSPEC, RID_INLINE +int, TYPESPEC, RID_INT +long, TYPESPEC, RID_LONG +register, SCSPEC, RID_REGISTER +return, RETURN, NORID +short, TYPESPEC, RID_SHORT +signed, TYPESPEC, RID_SIGNED +sizeof, SIZEOF, NORID +static, SCSPEC, RID_STATIC +struct, STRUCT, NORID +switch, SWITCH, NORID +typedef, SCSPEC, RID_TYPEDEF +typeof, TYPEOF, NORID +union, UNION, NORID +unsigned, TYPESPEC, RID_UNSIGNED +void, TYPESPEC, RID_VOID +volatile, TYPE_QUAL, RID_VOLATILE +while, WHILE, NORID diff --git a/gnu/usr.bin/gcc/objc/objc.h b/gnu/usr.bin/gcc/objc/objc.h index 979c5c81126..e48b0fd5bfb 100644 --- a/gnu/usr.bin/gcc/objc/objc.h +++ b/gnu/usr.bin/gcc/objc/objc.h @@ -1,5 +1,5 @@ /* Basic data types for Objective C. - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc. This file is part of GNU CC. @@ -36,7 +36,11 @@ extern "C" { /* ** Definition of the boolean type. */ +#ifdef __vxworks +typedef int BOOL; +#else typedef unsigned char BOOL; +#endif #define YES (BOOL)1 #define NO (BOOL)0 @@ -103,9 +107,9 @@ struct objc_class { unsigned long info; /* Bit mask. See class masks defined above. */ long instance_size; /* Size in bytes of the class. - The sum of the class definition - and all super class - definitions. */ + The sum of the class + definition and all super + class definitions. */ struct objc_ivar_list* ivars; /* Pointer to a structure that describes the instance variables in the class diff --git a/gnu/usr.bin/gcc/objc/objects.c b/gnu/usr.bin/gcc/objc/objects.c index ebaf11765ac..3e68334c924 100644 --- a/gnu/usr.bin/gcc/objc/objects.c +++ b/gnu/usr.bin/gcc/objc/objects.c @@ -1,5 +1,5 @@ /* GNU Objective C Runtime class related functions - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -31,9 +31,9 @@ id __objc_object_alloc(Class); id __objc_object_dispose(id); id __objc_object_copy(id); -id (*_objc_object_alloc)(Class) = __objc_object_alloc; -id (*_objc_object_dispose)(id) = __objc_object_dispose; -id (*_objc_object_copy)(id) = __objc_object_copy; +id (*_objc_object_alloc)(Class) = __objc_object_alloc; /* !T:SINGLE */ +id (*_objc_object_dispose)(id) = __objc_object_dispose; /* !T:SINGLE */ +id (*_objc_object_copy)(id) = __objc_object_copy; /* !T:SINGLE */ id class_create_instance(Class class) @@ -66,19 +66,19 @@ object_dispose(id object) if (_objc_object_dispose) (*_objc_object_dispose)(object); else - free(object); + objc_free(object); } return nil; } id __objc_object_alloc(Class class) { - return (id)__objc_xmalloc(class->instance_size); + return (id)objc_malloc(class->instance_size); } id __objc_object_dispose(id object) { - free(object); + objc_free(object); return 0; } diff --git a/gnu/usr.bin/gcc/objc/runtime.h b/gnu/usr.bin/gcc/objc/runtime.h index 0f4510f3ac7..b0eae4a222d 100644 --- a/gnu/usr.bin/gcc/objc/runtime.h +++ b/gnu/usr.bin/gcc/objc/runtime.h @@ -1,5 +1,5 @@ /* GNU Objective C Runtime internal declarations - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -37,21 +37,29 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "objc/objc.h" /* core data types */ #include "objc/objc-api.h" /* runtime api functions */ +#include "objc/thr.h" /* thread and mutex support */ + #include "objc/hash.h" /* hash structures */ -#include "objc/list.h" /* linear lists */ +#include "objc/objc-list.h" /* linear lists */ extern void __objc_add_class_to_hash(Class); /* (objc-class.c) */ -extern void __objc_init_selector_tables(); /* (objc-sel.c) */ -extern void __objc_init_class_tables(); /* (objc-class.c) */ -extern void __objc_init_dispatch_tables(); /* (objc-dispatch.c) */ +extern void __objc_init_selector_tables(void); /* (objc-sel.c) */ +extern void __objc_init_class_tables(void); /* (objc-class.c) */ +extern void __objc_init_dispatch_tables(void); /* (objc-dispatch.c) */ extern void __objc_install_premature_dtable(Class); /* (objc-dispatch.c) */ -extern void __objc_resolve_class_links(); /* (objc-class.c) */ +extern void __objc_resolve_class_links(void); /* (objc-class.c) */ extern void __objc_register_selectors_from_class(Class); /* (objc-sel.c) */ extern void __objc_update_dispatch_table_for_class (Class);/* (objc-msg.c) */ + +extern int __objc_init_thread_system(void); /* thread.c */ +extern int __objc_fini_thread_system(void); /* thread.c */ +extern void __objc_print_dtable_stats(void); /* sendmsg.c */ + extern void class_add_method_list(Class, MethodList_t); -extern void objc_error(id object, const char* fmt, va_list); -extern void (*_objc_error)(id, const char*, va_list); +/* Registering instance methods as class methods for root classes */ +extern void __objc_register_instance_methods_to_class(Class); +extern Method_t search_for_method_in_list(MethodList_t list, SEL op); /* True when class links has been resolved */ extern BOOL __objc_class_links_resolved; @@ -59,6 +67,12 @@ extern BOOL __objc_class_links_resolved; /* Number of selectors stored in each of the selector tables */ extern int __objc_selector_max_index; +/* Mutex locking __objc_selector_max_index and its arrays. */ +extern objc_mutex_t __objc_runtime_mutex; + +/* Number of threads which are alive. */ +extern int __objc_runtime_threads_alive; + #ifdef DEBUG #define DEBUG_PRINTF(format, args...) printf (format, ## args) #else @@ -67,7 +81,7 @@ extern int __objc_selector_max_index; BOOL __objc_responds_to (id object, SEL sel); /* for internal use only! */ SEL __sel_register_typed_name (const char*, const char*, - struct objc_selector*); + struct objc_selector*, BOOL is_const); #endif /* not __objc_runtime_INCLUDE_GNU */ diff --git a/gnu/usr.bin/gcc/objc/sarray.c b/gnu/usr.bin/gcc/objc/sarray.c index 9ed219681c8..7e40fba750f 100644 --- a/gnu/usr.bin/gcc/objc/sarray.c +++ b/gnu/usr.bin/gcc/objc/sarray.c @@ -1,5 +1,5 @@ /* Sparse Arrays for Objective C dispatch tables - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc. This file is part of GNU CC. @@ -25,13 +25,16 @@ Boston, MA 02111-1307, USA. */ the executable file might be covered by the GNU General Public License. */ #include "objc/sarray.h" +#include "objc/runtime.h" #include <stdio.h> #include "assert.h" -int nbuckets = 0; -int nindices = 0; -int narrays = 0; -int idxsize = 0; +int nbuckets = 0; /* !T:MUTEX */ +int nindices = 0; /* !T:MUTEX */ +int narrays = 0; /* !T:MUTEX */ +int idxsize = 0; /* !T:MUTEX */ + +static void * first_free_data = NULL; /* !T:MUTEX */ #ifdef OBJC_SPARSE2 const char* __objc_sparse2_id = "2 level sparse indices"; @@ -43,16 +46,62 @@ const char* __objc_sparse3_id = "3 level sparse indices"; #ifdef __alpha__ const void *memcpy (void*, const void*, size_t); -void free (const void*); #endif +/* This function removes any structures left over from free operations + that were not safe in a multi-threaded environment. */ +void +sarray_remove_garbage(void) +{ + void **vp; + void *np; + + objc_mutex_lock(__objc_runtime_mutex); + + vp = first_free_data; + first_free_data = NULL; + + while (vp) { + np = *vp; + objc_free(vp); + vp = np; + } + + objc_mutex_unlock(__objc_runtime_mutex); +} + +/* Free a block of dynamically allocated memory. If we are in multi-threaded + mode, it is ok to free it. If not, we add it to the garbage heap to be + freed later. */ + +static void +sarray_free_garbage(void *vp) +{ + objc_mutex_lock(__objc_runtime_mutex); + + if (__objc_runtime_threads_alive == 1) { + objc_free(vp); + if (first_free_data) + sarray_remove_garbage(); + } + else { + *(void **)vp = first_free_data; + first_free_data = vp; + } + + objc_mutex_unlock(__objc_runtime_mutex); +} + +/* sarray_at_put : copies data in such a way as to be thread reader safe. */ void sarray_at_put(struct sarray* array, sidx index, void* element) { #ifdef OBJC_SPARSE3 struct sindex** the_index; + struct sindex* new_index; #endif struct sbucket** the_bucket; + struct sbucket* new_bucket; #ifdef OBJC_SPARSE3 size_t ioffset; #endif @@ -96,22 +145,24 @@ sarray_at_put(struct sarray* array, sidx index, void* element) if ((*the_index) == array->empty_index) { /* The index was previously empty, allocate a new */ - *the_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); - memcpy(*the_index, array->empty_index, sizeof(struct sindex)); - (*the_index)->version = array->version; + new_index = (struct sindex*)objc_malloc(sizeof(struct sindex)); + memcpy(new_index, array->empty_index, sizeof(struct sindex)); + new_index->version.version = array->version.version; + *the_index = new_index; /* Prepared for install. */ the_bucket = &((*the_index)->buckets[boffset]); - nindices += 1; - } else if ((*the_index)->version != array->version) { + nindices += 1; + } else if ((*the_index)->version.version != array->version.version) { /* This index must be lazy copied */ struct sindex* old_index = *the_index; - *the_index = (struct sindex*)__objc_xmalloc(sizeof(struct sindex)); - memcpy( *the_index,old_index, sizeof(struct sindex)); - (*the_index)->version = array->version; + new_index = (struct sindex*)objc_malloc(sizeof(struct sindex)); + memcpy( new_index, old_index, sizeof(struct sindex)); + new_index->version.version = array->version.version; + *the_index = new_index; /* Prepared for install. */ the_bucket = &((*the_index)->buckets[boffset]); - nindices += 1; + nindices += 1; } #endif /* OBJC_SPARSE3 */ @@ -122,18 +173,23 @@ sarray_at_put(struct sarray* array, sidx index, void* element) /* The bucket was previously empty (or something like that), */ /* allocate a new. This is the effect of `lazy' allocation */ - *the_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); - memcpy((void *) *the_bucket, (const void*)array->empty_bucket, sizeof(struct sbucket)); - (*the_bucket)->version = array->version; + new_bucket = (struct sbucket*)objc_malloc(sizeof(struct sbucket)); + memcpy((void *) new_bucket, (const void*)array->empty_bucket, + sizeof(struct sbucket)); + new_bucket->version.version = array->version.version; + *the_bucket = new_bucket; /* Prepared for install. */ + nbuckets += 1; - } else if ((*the_bucket)->version != array->version) { + } else if ((*the_bucket)->version.version != array->version.version) { /* Perform lazy copy. */ struct sbucket* old_bucket = *the_bucket; - *the_bucket = (struct sbucket*)__objc_xmalloc(sizeof(struct sbucket)); - memcpy( *the_bucket,old_bucket, sizeof(struct sbucket)); - (*the_bucket)->version = array->version; + new_bucket = (struct sbucket*)objc_malloc(sizeof(struct sbucket)); + memcpy( new_bucket, old_bucket, sizeof(struct sbucket)); + new_bucket->version.version = array->version.version; + *the_bucket = new_bucket; /* Prepared for install. */ + nbuckets += 1; } @@ -151,42 +207,48 @@ sarray_at_put_safe(struct sarray* array, sidx index, void* element) struct sarray* sarray_new (int size, void* default_element) { + struct sarray* arr; #ifdef OBJC_SPARSE3 size_t num_indices = ((size-1)/(INDEX_CAPACITY))+1; + struct sindex ** new_indices; #else /* OBJC_SPARSE2 */ size_t num_indices = ((size-1)/BUCKET_SIZE)+1; + struct sbucket ** new_buckets; #endif int counter; - struct sarray* arr; assert(size > 0); /* Allocate core array */ - arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); - arr->version = 0; - narrays += 1; + arr = (struct sarray*) objc_malloc(sizeof(struct sarray)); + arr->version.version = 0; /* Initialize members */ #ifdef OBJC_SPARSE3 arr->capacity = num_indices*INDEX_CAPACITY; - arr->indices = (struct sindex**) - __objc_xmalloc(sizeof(struct sindex*)*num_indices); - idxsize += num_indices; + new_indices = (struct sindex**) + objc_malloc(sizeof(struct sindex*)*num_indices); - arr->empty_index = (struct sindex*) __objc_xmalloc(sizeof(struct sindex)); - arr->empty_index->version = 0; + arr->empty_index = (struct sindex*) objc_malloc(sizeof(struct sindex)); + arr->empty_index->version.version = 0; + + narrays += 1; + idxsize += num_indices; nindices += 1; #else /* OBJC_SPARSE2 */ arr->capacity = num_indices*BUCKET_SIZE; - arr->buckets = (struct sbucket**) - __objc_xmalloc(sizeof(struct sbucket*)*num_indices); + new_buckets = (struct sbucket**) + objc_malloc(sizeof(struct sbucket*)*num_indices); + + narrays += 1; idxsize += num_indices; #endif - arr->empty_bucket = (struct sbucket*) __objc_xmalloc(sizeof(struct sbucket)); - arr->empty_bucket->version = 0; + arr->empty_bucket = (struct sbucket*) objc_malloc(sizeof(struct sbucket)); + arr->empty_bucket->version.version = 0; + nbuckets += 1; arr->ref_count = 1; @@ -200,20 +262,28 @@ sarray_new (int size, void* default_element) arr->empty_index->buckets[counter] = arr->empty_bucket; for (counter=0; counter<num_indices; counter++) - arr->indices[counter] = arr->empty_index; + new_indices[counter] = arr->empty_index; #else /* OBJC_SPARSE2 */ for (counter=0; counter<num_indices; counter++) - arr->buckets[counter] = arr->empty_bucket; + new_buckets[counter] = arr->empty_bucket; #endif - + +#ifdef OBJC_SPARSE3 + arr->indices = new_indices; +#else /* OBJC_SPARSE2 */ + arr->buckets = new_buckets; +#endif + return arr; } -/* Reallocate the sparse array to hold `newsize' entries */ +/* Reallocate the sparse array to hold `newsize' entries + Note: We really allocate and then free. We have to do this to ensure that + any concurrent readers notice the update. */ void sarray_realloc(struct sarray* array, int newsize) @@ -223,11 +293,17 @@ sarray_realloc(struct sarray* array, int newsize) size_t new_max_index = ((newsize-1)/INDEX_CAPACITY); size_t rounded_size = (new_max_index+1)*INDEX_CAPACITY; + struct sindex ** new_indices; + struct sindex ** old_indices; + #else /* OBJC_SPARSE2 */ size_t old_max_index = (array->capacity-1)/BUCKET_SIZE; size_t new_max_index = ((newsize-1)/BUCKET_SIZE); size_t rounded_size = (new_max_index+1)*BUCKET_SIZE; + struct sbucket ** new_buckets; + struct sbucket ** old_buckets; + #endif int counter; @@ -235,87 +311,72 @@ sarray_realloc(struct sarray* array, int newsize) assert(newsize > 0); /* The size is the same, just ignore the request */ - if(rounded_size == array->capacity) + if(rounded_size <= array->capacity) return; assert(array->ref_count == 1); /* stop if lazy copied... */ - if(rounded_size < array->capacity) + /* We are asked to extend the array -- allocate new bucket table, */ + /* and insert empty_bucket in newly allocated places. */ + if(rounded_size > array->capacity) { + +#ifdef OBJC_SPARSE3 + new_max_index += 4; + rounded_size = (new_max_index+1)*INDEX_CAPACITY; + +#else /* OBJC_SPARSE2 */ + new_max_index += 4; + rounded_size = (new_max_index+1)*BUCKET_SIZE; +#endif + /* update capacity */ array->capacity = rounded_size; - /* free buckets above new_max_index */ - for(counter = old_max_index; counter > new_max_index; counter-- ) { #ifdef OBJC_SPARSE3 - struct sindex* idx = array->indices[counter]; - if((idx != array->empty_index) && (idx->version == array->version)) { - int c2; - for(c2=0; c2<INDEX_SIZE; c2++) { - struct sbucket* bkt = idx->buckets[c2]; - if((bkt != array->empty_bucket) && (bkt->version == array->version)) - { - free(bkt); - nbuckets -= 1; - } - } - free(idx); - nindices -= 1; - } + /* alloc to force re-read by any concurrent readers. */ + old_indices = array->indices; + new_indices = (struct sindex**) + objc_malloc((new_max_index+1)*sizeof(struct sindex*)); #else /* OBJC_SPARSE2 */ - struct sbucket* bkt = array->buckets[counter]; - if ((bkt != array->empty_bucket) && (bkt->version == array->version)) - { - free(bkt); - nbuckets -= 1; - } + old_buckets = array->buckets; + new_buckets = (struct sbucket**) + objc_malloc((new_max_index+1)*sizeof(struct sbucket*)); #endif - } - + + /* copy buckets below old_max_index (they are still valid) */ + for(counter = 0; counter <= old_max_index; counter++ ) { #ifdef OBJC_SPARSE3 - /* realloc to free the space above new_max_index */ - array->indices = (struct sindex**) - __objc_xrealloc(array->indices, - (new_max_index+1)*sizeof(struct sindex*)); + new_indices[counter] = old_indices[counter]; #else /* OBJC_SPARSE2 */ - array->buckets = (struct sbucket**) - __objc_xrealloc(array->buckets, - (new_max_index+1)*sizeof(struct sbucket*)); -#endif - idxsize -= (old_max_index-new_max_index); - - return; - } - - /* We are asked to extend the array -- reallocate the bucket table, */ - /* and insert empty_bucket in newly allocated places. */ - if(rounded_size > array->capacity) - { - /* update capacity */ - array->capacity = rounded_size; + new_buckets[counter] = old_buckets[counter]; +#endif + } #ifdef OBJC_SPARSE3 - /* realloc to make room in table above old_max_index */ - array->indices = (struct sindex**) - __objc_xrealloc(array->indices, - (new_max_index+1)*sizeof(struct sindex*)); - /* reset entries above old_max_index to empty_bucket */ for(counter = old_max_index+1; counter <= new_max_index; counter++) - array->indices[counter] = array->empty_index; - + new_indices[counter] = array->empty_index; #else /* OBJC_SPARSE2 */ - - /* realloc to make room in table above old_max_index */ - array->buckets = (struct sbucket**) - __objc_xrealloc(array->buckets, - (new_max_index+1)*sizeof(struct sbucket*)); - /* reset entries above old_max_index to empty_bucket */ for(counter = old_max_index+1; counter <= new_max_index; counter++) - array->buckets[counter] = array->empty_bucket; + new_buckets[counter] = array->empty_bucket; +#endif + +#ifdef OBJC_SPARSE3 + /* install the new indices */ + array->indices = new_indices; +#else /* OBJC_SPARSE2 */ + array->buckets = new_buckets; +#endif +#ifdef OBJC_SPARSE3 + /* free the old indices */ + sarray_free_garbage(old_indices); +#else /* OBJC_SPARSE2 */ + sarray_free_garbage(old_buckets); #endif + idxsize += (new_max_index-old_max_index); return; } @@ -326,10 +387,13 @@ sarray_realloc(struct sarray* array, int newsize) void sarray_free(struct sarray* array) { + #ifdef OBJC_SPARSE3 size_t old_max_index = (array->capacity-1)/INDEX_CAPACITY; + struct sindex ** old_indices; #else size_t old_max_index = (array->capacity-1)/BUCKET_SIZE; + struct sbucket ** old_buckets; #endif int counter = 0; @@ -338,31 +402,40 @@ sarray_free(struct sarray* array) { if(--(array->ref_count) != 0) /* There exists copies of me */ return; +#ifdef OBJC_SPARSE3 + old_indices = array->indices; +#else + old_buckets = array->buckets; +#endif + if((array->is_copy_of) && ((array->is_copy_of->ref_count - 1) == 0)) sarray_free(array->is_copy_of); /* Free all entries that do not point to empty_bucket */ for(counter = 0; counter <= old_max_index; counter++ ) { #ifdef OBJC_SPARSE3 - struct sindex* idx = array->indices[counter]; - if((idx != array->empty_index) && (idx->version == array->version)) { + struct sindex* idx = old_indices[counter]; + if((idx != array->empty_index) && + (idx->version.version == array->version.version)) { int c2; for(c2=0; c2<INDEX_SIZE; c2++) { struct sbucket* bkt = idx->buckets[c2]; - if((bkt != array->empty_bucket) && (bkt->version == array->version)) + if((bkt != array->empty_bucket) && + (bkt->version.version == array->version.version)) { - free(bkt); + sarray_free_garbage(bkt); nbuckets -= 1; } } - free(idx); + sarray_free_garbage(idx); nindices -= 1; } #else /* OBJC_SPARSE2 */ struct sbucket* bkt = array->buckets[counter]; - if ((bkt != array->empty_bucket) && (bkt->version == array->version)) + if ((bkt != array->empty_bucket) && + (bkt->version.version == array->version.version)) { - free(bkt); + sarray_free_garbage(bkt); nbuckets -= 1; } #endif @@ -370,33 +443,32 @@ sarray_free(struct sarray* array) { #ifdef OBJC_SPARSE3 /* free empty_index */ - if(array->empty_index->version == array->version) { - free(array->empty_index); + if(array->empty_index->version.version == array->version.version) { + sarray_free_garbage(array->empty_index); nindices -= 1; } #endif /* free empty_bucket */ - if(array->empty_bucket->version == array->version) { - free(array->empty_bucket); + if(array->empty_bucket->version.version == array->version.version) { + sarray_free_garbage(array->empty_bucket); nbuckets -= 1; } + idxsize -= (old_max_index+1); + narrays -= 1; #ifdef OBJC_SPARSE3 /* free bucket table */ - free(array->indices); - idxsize -= (old_max_index+1); + sarray_free_garbage(array->indices); #else /* free bucket table */ - free(array->buckets); - idxsize -= (old_max_index+1); + sarray_free_garbage(array->buckets); #endif - + /* free array */ - free(array); - narrays -= 1; + sarray_free_garbage(array); } /* This is a lazy copy. Only the core of the structure is actually */ @@ -405,37 +477,46 @@ sarray_free(struct sarray* array) { struct sarray* sarray_lazy_copy(struct sarray* oarr) { + struct sarray* arr; + #ifdef OBJC_SPARSE3 size_t num_indices = ((oarr->capacity-1)/INDEX_CAPACITY)+1; + struct sindex ** new_indices; #else /* OBJC_SPARSE2 */ size_t num_indices = ((oarr->capacity-1)/BUCKET_SIZE)+1; + struct sbucket ** new_buckets; #endif - struct sarray* arr; /* Allocate core array */ - arr = (struct sarray*) __objc_xmalloc(sizeof(struct sarray)); - memcpy( arr,oarr, sizeof(struct sarray)); - arr->version = oarr->version + 1; - arr->is_copy_of = oarr; - oarr->ref_count += 1; + arr = (struct sarray*) objc_malloc(sizeof(struct sarray)); /* !!! */ + arr->version.version = oarr->version.version + 1; +#ifdef OBJC_SPARSE3 + arr->empty_index = oarr->empty_index; +#endif + arr->empty_bucket = oarr->empty_bucket; arr->ref_count = 1; + oarr->ref_count += 1; + arr->is_copy_of = oarr; + arr->capacity = oarr->capacity; #ifdef OBJC_SPARSE3 /* Copy bucket table */ - arr->indices = (struct sindex**) - __objc_xmalloc(sizeof(struct sindex*)*num_indices); - memcpy( arr->indices,oarr->indices, + new_indices = (struct sindex**) + objc_malloc(sizeof(struct sindex*)*num_indices); + memcpy( new_indices,oarr->indices, sizeof(struct sindex*)*num_indices); + arr->indices = new_indices; #else /* Copy bucket table */ - arr->buckets = (struct sbucket**) - __objc_xmalloc(sizeof(struct sbucket*)*num_indices); - memcpy( arr->buckets,oarr->buckets, + new_buckets = (struct sbucket**) + objc_malloc(sizeof(struct sbucket*)*num_indices); + memcpy( new_buckets,oarr->buckets, sizeof(struct sbucket*)*num_indices); + arr->buckets = new_buckets; #endif idxsize += num_indices; narrays += 1; - + return arr; } diff --git a/gnu/usr.bin/gcc/objc/sarray.h b/gnu/usr.bin/gcc/objc/sarray.h index a3ec7a98373..74fa38652ba 100644 --- a/gnu/usr.bin/gcc/objc/sarray.h +++ b/gnu/usr.bin/gcc/objc/sarray.h @@ -1,7 +1,6 @@ /* Sparse Arrays for Objective C dispatch tables - Copyright (C) 1993, 1995 Free Software Foundation, Inc. - -Author: Kresten Krab Thorup + Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup. This file is part of GNU CC. @@ -42,6 +41,8 @@ extern const char* __objc_sparse3_id; #include <stddef.h> +#include "objc/thr.h" + extern int nbuckets; /* for stats */ extern int nindices; extern int narrays; @@ -108,19 +109,21 @@ union sofftype { #endif /* not PRECOMPUTE_SELECTORS */ -void * __objc_xrealloc (void *optr, size_t size); -void * __objc_xmalloc (size_t size); +union sversion { + int version; + void *next_free; +}; struct sbucket { void* elems[BUCKET_SIZE]; /* elements stored in array */ - short version; /* used for copy-on-write */ + union sversion version; /* used for copy-on-write */ }; #ifdef OBJC_SPARSE3 struct sindex { struct sbucket* buckets[INDEX_SIZE]; - short version; + union sversion version; /* used for copy-on-write */ }; #endif /* OBJC_SPARSE3 */ @@ -133,7 +136,7 @@ struct sarray { struct sbucket** buckets; #endif /* OBJC_SPARSE2 */ struct sbucket* empty_bucket; - short version; + union sversion version; /* used for copy-on-write */ short ref_count; struct sarray* is_copy_of; size_t capacity; @@ -142,10 +145,12 @@ struct sarray { struct sarray* sarray_new(int, void* default_element); void sarray_free(struct sarray*); struct sarray* sarray_lazy_copy(struct sarray*); -struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */ void sarray_realloc(struct sarray*, int new_size); void sarray_at_put(struct sarray*, sidx index, void* elem); void sarray_at_put_safe(struct sarray*, sidx index, void* elem); + +struct sarray* sarray_hard_copy(struct sarray*); /* ... like the name? */ +void sarray_remove_garbage(void); #ifdef PRECOMPUTE_SELECTORS diff --git a/gnu/usr.bin/gcc/objc/selector.c b/gnu/usr.bin/gcc/objc/selector.c index be68f4f1dbb..83c70e4ae0f 100644 --- a/gnu/usr.bin/gcc/objc/selector.c +++ b/gnu/usr.bin/gcc/objc/selector.c @@ -1,5 +1,5 @@ /* GNU Objective C Runtime selector related functions - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -31,14 +31,14 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define SELECTOR_HASH_SIZE 128 /* Tables mapping selector names to uid and opposite */ -static struct sarray* __objc_selector_array = 0; /* uid -> sel */ -static struct sarray* __objc_selector_names = 0; /* uid -> name */ -static cache_ptr __objc_selector_hash = 0; /* name -> uid */ +static struct sarray* __objc_selector_array = 0; /* uid -> sel !T:MUTEX */ +static struct sarray* __objc_selector_names = 0; /* uid -> name !T:MUTEX */ +static cache_ptr __objc_selector_hash = 0; /* name -> uid !T:MUTEX */ static void register_selectors_from_list(MethodList_t); /* Number of selectors stored in each of the above tables */ -int __objc_selector_max_index = 0; +int __objc_selector_max_index = 0; /* !T:MUTEX */ void __objc_init_selector_tables() { @@ -88,6 +88,71 @@ register_selectors_from_list (MethodList_t method_list) } +/* Register instance methods as class methods for root classes */ +void __objc_register_instance_methods_to_class(Class class) +{ + MethodList_t method_list; + MethodList_t class_method_list; + int max_methods_no = 16; + MethodList_t new_list; + Method_t curr_method; + + /* Only if a root class. */ + if(class->super_class) + return; + + /* Allocate a method list to hold the new class methods */ + new_list = objc_calloc(sizeof(struct objc_method_list) + + sizeof(struct objc_method[max_methods_no]), 1); + method_list = class->methods; + class_method_list = class->class_pointer->methods; + curr_method = &new_list->method_list[0]; + + /* Iterate through the method lists for the class */ + while (method_list) + { + int i; + + /* Iterate through the methods from this method list */ + for (i = 0; i < method_list->method_count; i++) + { + Method_t mth = &method_list->method_list[i]; + if (mth->method_name + && !search_for_method_in_list (class_method_list, + mth->method_name)) + { + /* This instance method isn't a class method. + Add it into the new_list. */ + *curr_method = *mth; + + /* Reallocate the method list if necessary */ + if(++new_list->method_count == max_methods_no) + new_list = + objc_realloc(new_list, sizeof(struct objc_method_list) + + sizeof(struct + objc_method[max_methods_no += 16])); + curr_method = &new_list->method_list[new_list->method_count]; + } + } + + method_list = method_list->method_next; + } + + /* If we created any new class methods + then attach the method list to the class */ + if (new_list->method_count) + { + new_list = + objc_realloc(new_list, sizeof(struct objc_method_list) + + sizeof(struct objc_method[new_list->method_count])); + new_list->method_next = class->class_pointer->methods; + class->class_pointer->methods = new_list; + } + + __objc_update_dispatch_table_for_class (class->class_pointer); +} + + /* Returns YES iff t1 and t2 have same method types, but we ignore the argframe layout */ BOOL @@ -122,11 +187,16 @@ sel_get_typed_uid (const char *name, const char *types) struct objc_list *l; sidx i; + objc_mutex_lock(__objc_runtime_mutex); + i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (i == 0) - return 0; + { + objc_mutex_unlock(__objc_runtime_mutex); + return 0; + } - for (l = (struct objc_list*)sarray_get (__objc_selector_array, i); + for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i); l; l = l->tail) { SEL s = (SEL)l->head; @@ -134,15 +204,18 @@ sel_get_typed_uid (const char *name, const char *types) { if (s->sel_types == types) { + objc_mutex_unlock(__objc_runtime_mutex); return s; } } else if (sel_types_match (s->sel_types, types)) { + objc_mutex_unlock(__objc_runtime_mutex); return s; } } + objc_mutex_unlock(__objc_runtime_mutex); return 0; } @@ -152,20 +225,29 @@ sel_get_any_typed_uid (const char *name) { struct objc_list *l; sidx i; - SEL s; + SEL s = NULL; + + objc_mutex_lock(__objc_runtime_mutex); i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (i == 0) - return 0; + { + objc_mutex_unlock(__objc_runtime_mutex); + return 0; + } - for (l = (struct objc_list*)sarray_get (__objc_selector_array, i); + for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i); l; l = l->tail) { s = (SEL) l->head; if (s->sel_types) - return s; + { + objc_mutex_unlock(__objc_runtime_mutex); + return s; + } } + objc_mutex_unlock(__objc_runtime_mutex); return s; } @@ -176,11 +258,18 @@ sel_get_any_uid (const char *name) struct objc_list *l; sidx i; + objc_mutex_lock(__objc_runtime_mutex); + i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (soffset_decode (i) == 0) - return 0; + { + objc_mutex_unlock(__objc_runtime_mutex); + return 0; + } + + l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i); + objc_mutex_unlock(__objc_runtime_mutex); - l = (struct objc_list*)sarray_get (__objc_selector_array, i); if (l == 0) return 0; @@ -199,11 +288,16 @@ sel_get_uid (const char *name) const char* sel_get_name (SEL selector) { + const char *ret; + + objc_mutex_lock(__objc_runtime_mutex); if ((soffset_decode((sidx)selector->sel_id) > 0) && (soffset_decode((sidx)selector->sel_id) <= __objc_selector_max_index)) - return sarray_get (__objc_selector_names, (sidx) selector->sel_id); + ret = sarray_get_safe (__objc_selector_names, (sidx) selector->sel_id); else - return 0; + ret = 0; + objc_mutex_unlock(__objc_runtime_mutex); + return ret; } BOOL @@ -227,10 +321,15 @@ sel_get_type (SEL selector) extern struct sarray* __objc_uninstalled_dtable; /* Store the passed selector name in the selector record and return its - selector value (value returned by sel_get_uid). */ + selector value (value returned by sel_get_uid). + Assumes that the calling function has locked down __objc_runtime_mutex. */ +/* is_const parameter tells us if the name and types parameters + are really constant or not. If YES then they are constant and + we can just store the pointers. If NO then we need to copy + name and types because the pointers may disappear later on. */ SEL __sel_register_typed_name (const char *name, const char *types, - struct objc_selector *orig) + struct objc_selector *orig, BOOL is_const) { struct objc_selector* j; sidx i; @@ -239,7 +338,7 @@ __sel_register_typed_name (const char *name, const char *types, i = (sidx) hash_value_for_key (__objc_selector_hash, name); if (soffset_decode (i) != 0) { - for (l = (struct objc_list*)sarray_get (__objc_selector_array, i); + for (l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i); l; l = l->tail) { SEL s = (SEL)l->head; @@ -270,11 +369,17 @@ __sel_register_typed_name (const char *name, const char *types, if (orig) j = orig; else - j = __objc_xmalloc (sizeof (struct objc_selector)); + j = objc_malloc (sizeof (struct objc_selector)); j->sel_id = (void*)i; - j->sel_types = (const char*)types; - l = (struct objc_list*)sarray_get (__objc_selector_array, i); + /* Can we use the pointer or must copy types? Don't copy if NULL */ + if ((is_const) || (types == 0)) + j->sel_types = (const char*)types; + else { + j->sel_types = (char *) objc_malloc(strlen(types)+1); + strcpy((char *)j->sel_types, types); + } + l = (struct objc_list*)sarray_get_safe (__objc_selector_array, i); } else { @@ -283,10 +388,16 @@ __sel_register_typed_name (const char *name, const char *types, if (orig) j = orig; else - j = __objc_xmalloc (sizeof (struct objc_selector)); + j = objc_malloc (sizeof (struct objc_selector)); j->sel_id = (void*)i; - j->sel_types = (const char*)types; + /* Can we use the pointer or must copy types? Don't copy if NULL */ + if ((is_const) || (types == 0)) + j->sel_types = (const char*)types; + else { + j->sel_types = (char *) objc_malloc(strlen(types)+1); + strcpy((char *)j->sel_types, types); + } l = 0; } @@ -295,11 +406,21 @@ __sel_register_typed_name (const char *name, const char *types, { int is_new = (l == 0); + const char *new_name; + + /* Can we use the pointer or must copy name? Don't copy if NULL */ + if ((is_const) || (name == 0)) + new_name = name; + else { + new_name = (char *) objc_malloc(strlen(name)+1); + strcpy((char *)new_name, name); + } + l = list_cons ((void*)j, l); - sarray_at_put_safe (__objc_selector_names, i, (void *) name); + sarray_at_put_safe (__objc_selector_names, i, (void *) new_name); sarray_at_put_safe (__objc_selector_array, i, (void *) l); if (is_new) - hash_add (&__objc_selector_hash, (void *) name, (void *) i); + hash_add (&__objc_selector_hash, (void *) new_name, (void *) i); } sarray_realloc(__objc_uninstalled_dtable, __objc_selector_max_index+1); @@ -310,12 +431,28 @@ __sel_register_typed_name (const char *name, const char *types, SEL sel_register_name (const char *name) { - return __sel_register_typed_name (name, 0, 0); + SEL ret; + + objc_mutex_lock(__objc_runtime_mutex); + /* Assume that name is not constant static memory and needs to be + copied before put into a runtime structure. is_const == NO */ + ret = __sel_register_typed_name (name, 0, 0, NO); + objc_mutex_unlock(__objc_runtime_mutex); + + return ret; } SEL sel_register_typed_name (const char *name, const char *type) { - return __sel_register_typed_name (name, type, 0); + SEL ret; + + objc_mutex_lock(__objc_runtime_mutex); + /* Assume that name and type are not constant static memory and need to + be copied before put into a runtime structure. is_const == NO */ + ret = __sel_register_typed_name (name, type, 0, NO); + objc_mutex_unlock(__objc_runtime_mutex); + + return ret; } diff --git a/gnu/usr.bin/gcc/objc/sendmsg.c b/gnu/usr.bin/gcc/objc/sendmsg.c index 79d4a6b5d5b..6f3487c260e 100644 --- a/gnu/usr.bin/gcc/objc/sendmsg.c +++ b/gnu/usr.bin/gcc/objc/sendmsg.c @@ -1,5 +1,5 @@ /* GNU Objective C Runtime message lookup - Copyright (C) 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -28,19 +28,20 @@ Boston, MA 02111-1307, USA. */ #include "runtime.h" #include "sarray.h" #include "encoding.h" +#include "runtime-info.h" /* this is how we hack STRUCT_VALUE to be 1 or 0 */ #define gen_rtx(args...) 1 #define rtx int -#if STRUCT_VALUE == 0 +#if !defined(STRUCT_VALUE) || STRUCT_VALUE == 0 #define INVISIBLE_STRUCT_RETURN 1 #else #define INVISIBLE_STRUCT_RETURN 0 #endif /* The uninstalled dispatch table */ -struct sarray* __objc_uninstalled_dtable = 0; +struct sarray* __objc_uninstalled_dtable = 0; /* !T:MUTEX */ /* Send +initialize to class */ static void __objc_send_initialize(Class); @@ -49,6 +50,14 @@ static void __objc_install_dispatch_table_for_class (Class); /* Forward declare some functions */ static void __objc_init_install_dtable(id, SEL); + +/* Various forwarding functions that are used based upon the + return type for the selector. + __objc_block_forward for structures. + __objc_double_forward for floats/doubles. + __objc_word_forward for pointers or types that fit in registers. + */ +static double __objc_double_forward(id, SEL, ...); static id __objc_word_forward(id, SEL, ...); typedef struct { id many[8]; } __big; #if INVISIBLE_STRUCT_RETURN @@ -58,13 +67,26 @@ static id #endif __objc_block_forward(id, SEL, ...); static Method_t search_for_method_in_hierarchy (Class class, SEL sel); -static Method_t search_for_method_in_list(MethodList_t list, SEL op); +Method_t search_for_method_in_list(MethodList_t list, SEL op); id nil_method(id, SEL, ...); -id -nil_method(id receiver, SEL op, ...) +/* Given a selector, return the proper forwarding implementation. */ +__inline__ +IMP +__objc_get_forward_imp (SEL sel) { - return receiver; + const char *t = sel->sel_types; + + if (t && (*t == '[' || *t == '(' || *t == '{') +#ifdef OBJC_MAX_STRUCT_BY_VALUE + && objc_sizeof_type(t) > OBJC_MAX_STRUCT_BY_VALUE +#endif + ) + return (IMP)__objc_block_forward; + else if (t && (*t == 'f' || *t == 'd')) + return (IMP)__objc_double_forward; + else + return (IMP)__objc_word_forward; } /* Given a class and selector, return the selector's implementation. */ @@ -72,54 +94,84 @@ __inline__ IMP get_imp (Class class, SEL sel) { - IMP impl; - void* res = sarray_get (class->dtable, (size_t) sel->sel_id); - if(res == __objc_init_install_dtable) - { - __objc_install_dispatch_table_for_class (class); - res = sarray_get (class->dtable, (size_t) sel->sel_id); - } + void* res = sarray_get_safe (class->dtable, (size_t) sel->sel_id); if (res == 0) { - const char *t = sel->sel_types; - if (t && (*t == '[' || *t == '(' || *t == '{')) - res = (IMP)__objc_block_forward; + /* Not a valid method */ + if(class->dtable == __objc_uninstalled_dtable) + { + /* The dispatch table needs to be installed. */ + objc_mutex_lock(__objc_runtime_mutex); + __objc_install_dispatch_table_for_class (class); + objc_mutex_unlock(__objc_runtime_mutex); + /* Call ourselves with the installed dispatch table + and get the real method */ + res = get_imp(class, sel); + } else - res = (IMP)__objc_word_forward; + { + /* The dispatch table has been installed so the + method just doesn't exist for the class. + Return the forwarding implementation. */ + res = __objc_get_forward_imp(sel); + } } return res; } -__inline__ BOOL +/* Query if an object can respond to a selector, returns YES if the +object implements the selector otherwise NO. Does not check if the +method can be forwarded. */ +__inline__ +BOOL __objc_responds_to (id object, SEL sel) { - void* res = sarray_get (object->class_pointer->dtable, (size_t) sel->sel_id); - if(res == __objc_init_install_dtable) + void* res; + + /* Install dispatch table if need be */ + if (object->class_pointer->dtable == __objc_uninstalled_dtable) { + objc_mutex_lock(__objc_runtime_mutex); __objc_install_dispatch_table_for_class (object->class_pointer); - res = sarray_get (object->class_pointer->dtable, (size_t) sel->sel_id); + objc_mutex_unlock(__objc_runtime_mutex); } + + /* Get the method from the dispatch table */ + res = sarray_get_safe (object->class_pointer->dtable, (size_t) sel->sel_id); return (res != 0); } /* This is the lookup function. All entries in the table are either a - valid method *or* one of `__objc_missing_method' which calls - forward:: etc, or `__objc_init_install_dtable' which installs the - real dtable */ -__inline__ IMP + valid method *or* zero. If zero then either the dispatch table + needs to be installed or it doesn't exist and forwarding is attempted. */ +__inline__ +IMP objc_msg_lookup(id receiver, SEL op) { IMP result; if(receiver) { - result = sarray_get(receiver->class_pointer->dtable, (sidx)op->sel_id); + result = sarray_get_safe (receiver->class_pointer->dtable, + (sidx)op->sel_id); if (result == 0) { - const char *t = op->sel_types; - if (t && (*t == '[' || *t == '(' || *t == '{')) - result = (IMP)__objc_block_forward; + /* Not a valid method */ + if(receiver->class_pointer->dtable == __objc_uninstalled_dtable) + { + /* The dispatch table needs to be installed. + This happens on the very first method call to the class. */ + __objc_init_install_dtable(receiver, op); + + /* Get real method for this in newly installed dtable */ + result = get_imp(receiver->class_pointer, op); + } else - result = (IMP)__objc_word_forward; + { + /* The dispatch table has been installed so the + method just doesn't exist for the class. + Attempt to forward the method. */ + result = __objc_get_forward_imp(op); + } } return result; } @@ -150,27 +202,25 @@ objc_msg_sendv(id object, SEL op, arglist_t arg_frame) method_get_sizeof_arguments (m)); } -void __objc_init_dispatch_tables() +void +__objc_init_dispatch_tables() { __objc_uninstalled_dtable - = sarray_new(200, __objc_init_install_dtable); + = sarray_new(200, 0); } -/* This one is a bit hairy. This function is installed in the - premature dispatch table, and thus called once for each class, - namely when the very first message is send to it. */ - -static void __objc_init_install_dtable(id receiver, SEL op) +/* This function is called by objc_msg_lookup when the + dispatch table needs to be installed; thus it is called once + for each class, namely when the very first message is sent to it. */ +static void +__objc_init_install_dtable(id receiver, SEL op) { - __label__ already_initialized; - IMP imp; - void* args; - void* result; - /* This may happen, if the programmer has taken the address of a method before the dtable was initialized... too bad for him! */ if(receiver->class_pointer->dtable != __objc_uninstalled_dtable) - goto already_initialized; + return; + + objc_mutex_lock(__objc_runtime_mutex); if(CLS_ISCLASS(receiver->class_pointer)) { @@ -198,31 +248,21 @@ static void __objc_init_install_dtable(id receiver, SEL op) else CLS_SETINITIALIZED((Class)receiver); } - -already_initialized: - - /* Get real method for this in newly installed dtable */ - imp = get_imp(receiver->class_pointer, op); - - args = __builtin_apply_args(); - result = __builtin_apply((apply_t)imp, args, 96); - if (result) - __builtin_return (result); - else - return; - + objc_mutex_unlock(__objc_runtime_mutex); } /* Install dummy table for class which causes the first message to that class (or instances hereof) to be initialized properly */ -void __objc_install_premature_dtable(Class class) +void +__objc_install_premature_dtable(Class class) { assert(__objc_uninstalled_dtable); class->dtable = __objc_uninstalled_dtable; } /* Send +initialize to class if not already done */ -static void __objc_send_initialize(Class class) +static void +__objc_send_initialize(Class class) { /* This *must* be a class object */ assert(CLS_ISCLASS(class)); @@ -250,7 +290,8 @@ static void __objc_send_initialize(Class class) for (i=0;i<method_list->method_count;i++) { method = &(method_list->method_list[i]); - if (method->method_name->sel_id == op->sel_id) { + if (method->method_name + && method->method_name->sel_id == op->sel_id) { imp = method->method_imp; break; } @@ -267,14 +308,39 @@ static void __objc_send_initialize(Class class) } } -} +} + +/* Walk on the methods list of class and install the methods in the reverse + order of the lists. Since methods added by categories are before the methods + of class in the methods list, this allows categories to substitute methods + declared in class. However if more than one category replaces the same + method nothing is guaranteed about what method will be used. + Assumes that __objc_runtime_mutex is locked down. */ +static void +__objc_install_methods_in_dtable (Class class, MethodList_t method_list) +{ + int i; + + if (!method_list) + return; + + if (method_list->method_next) + __objc_install_methods_in_dtable (class, method_list->method_next); + + for (i = 0; i < method_list->method_count; i++) + { + Method_t method = &(method_list->method_list[i]); + sarray_at_put_safe (class->dtable, + (sidx) method->method_name->sel_id, + method->method_imp); + } +} +/* Assumes that __objc_runtime_mutex is locked down. */ static void __objc_install_dispatch_table_for_class (Class class) { Class super; - MethodList_t mlist; - int counter; /* If the class has not yet had it's class links resolved, we must re-compute all class links */ @@ -289,47 +355,46 @@ __objc_install_dispatch_table_for_class (Class class) /* Allocate dtable if necessary */ if (super == 0) { + objc_mutex_lock(__objc_runtime_mutex); class->dtable = sarray_new (__objc_selector_max_index, 0); + objc_mutex_unlock(__objc_runtime_mutex); } else class->dtable = sarray_lazy_copy (super->dtable); - for (mlist = class->methods; mlist; mlist = mlist->method_next) - { - counter = mlist->method_count - 1; - while (counter >= 0) - { - Method_t method = &(mlist->method_list[counter]); - sarray_at_put_safe (class->dtable, - (sidx) method->method_name->sel_id, - method->method_imp); - counter -= 1; - } - } + __objc_install_methods_in_dtable (class, class->methods); } -void __objc_update_dispatch_table_for_class (Class class) +void +__objc_update_dispatch_table_for_class (Class class) { Class next; + struct sarray *arr; /* not yet installed -- skip it */ if (class->dtable == __objc_uninstalled_dtable) return; - sarray_free (class->dtable); /* release memory */ + objc_mutex_lock(__objc_runtime_mutex); + + arr = class->dtable; __objc_install_premature_dtable (class); /* someone might require it... */ - __objc_install_dispatch_table_for_class (class); /* could have been lazy... */ + sarray_free (arr); /* release memory */ + + /* could have been lazy... */ + __objc_install_dispatch_table_for_class (class); if (class->subclass_list) /* Traverse subclasses */ for (next = class->subclass_list; next; next = next->sibling_class) __objc_update_dispatch_table_for_class (next); + objc_mutex_unlock(__objc_runtime_mutex); } /* This function adds a method list to a class. This function is typically called by another function specific to the run-time. As - such this function does not worry about thread safe issued. + such this function does not worry about thread safe issues. This one is only called for categories. Class objects have their methods installed right away, and their selectors are made into @@ -338,9 +403,6 @@ void class_add_method_list (Class class, MethodList_t list) { int i; - static SEL initialize_sel = 0; - if (!initialize_sel) - initialize_sel = sel_register_name ("initialize"); /* Passing of a linked list is not allowed. Do multiple calls. */ assert (!list->method_next); @@ -356,24 +418,16 @@ class_add_method_list (Class class, MethodList_t list) method->method_name = sel_register_typed_name ((const char*)method->method_name, method->method_types); - - if (search_for_method_in_list (class->methods, method->method_name) - && method->method_name->sel_id != initialize_sel->sel_id) - { - /* Duplication. Print a error message an change the method name - to NULL. */ - fprintf (stderr, "attempt to add a existing method: %s\n", - sel_get_name(method->method_name)); - method->method_name = 0; - } } } /* Add the methods to the class's method list. */ list->method_next = class->methods; class->methods = list; -} + /* Update the dispatch table of class */ + __objc_update_dispatch_table_for_class (class); +} Method_t class_get_instance_method(Class class, SEL op) @@ -414,7 +468,7 @@ search_for_method_in_hierarchy (Class cls, SEL sel) /* Given a linked list of method and a method's name. Search for the named method's method structure. Return a pointer to the method's method structure if found. NULL otherwise. */ -static Method_t +Method_t search_for_method_in_list (MethodList_t list, SEL op) { MethodList_t method_list = list; @@ -447,6 +501,7 @@ search_for_method_in_list (MethodList_t list, SEL op) static retval_t __objc_forward (id object, SEL sel, arglist_t args); +/* Forwarding pointers/integers through the normal registers */ static id __objc_word_forward (id rcv, SEL op, ...) { @@ -460,6 +515,21 @@ __objc_word_forward (id rcv, SEL op, ...) return res; } +/* Specific routine for forwarding floats/double because of + architectural differences on some processors. i386s for + example which uses a floating point stack versus general + registers for floating point numbers. This forward routine + makes sure that GCC restores the proper return values */ +static double +__objc_double_forward (id rcv, SEL op, ...) +{ + void *args, *res; + + args = __builtin_apply_args (); + res = __objc_forward (rcv, op, args); + __builtin_return (res); +} + #if INVISIBLE_STRUCT_RETURN static __big #else @@ -473,6 +543,12 @@ __objc_block_forward (id rcv, SEL op, ...) res = __objc_forward (rcv, op, args); if (res) __builtin_return (res); + else +#if INVISIBLE_STRUCT_RETURN + return (__big) {0}; +#else + return nil; +#endif } @@ -482,7 +558,7 @@ static retval_t __objc_forward (id object, SEL sel, arglist_t args) { IMP imp; - static SEL frwd_sel = 0; + static SEL frwd_sel = 0; /* !T:SAFE2 */ SEL err_sel; /* first try if the object understands forward:: */ @@ -526,14 +602,19 @@ __objc_forward (id object, SEL sel, arglist_t args) /* The object doesn't respond to doesNotRecognize: or error:; Therefore, a default action is taken. */ - fprintf (stderr, "fatal: %s\n", msg); - abort (); + objc_error (object, OBJC_ERR_UNIMPLEMENTED, "%s\n", msg); + + return 0; } } -void __objc_print_dtable_stats() +void +__objc_print_dtable_stats() { int total = 0; + + objc_mutex_lock(__objc_runtime_mutex); + printf("memory usage: (%s)\n", #ifdef OBJC_SPARSE2 "2-level sparse arrays" @@ -542,17 +623,28 @@ void __objc_print_dtable_stats() #endif ); - printf("arrays: %d = %ld bytes\n", narrays, (int)narrays*sizeof(struct sarray)); + printf("arrays: %d = %ld bytes\n", narrays, + (long)narrays*sizeof(struct sarray)); total += narrays*sizeof(struct sarray); - printf("buckets: %d = %ld bytes\n", nbuckets, (int)nbuckets*sizeof(struct sbucket)); + printf("buckets: %d = %ld bytes\n", nbuckets, + (long)nbuckets*sizeof(struct sbucket)); total += nbuckets*sizeof(struct sbucket); - printf("idxtables: %d = %ld bytes\n", idxsize, (int)idxsize*sizeof(void*)); + printf("idxtables: %d = %ld bytes\n", idxsize, (long)idxsize*sizeof(void*)); total += idxsize*sizeof(void*); printf("-----------------------------------\n"); printf("total: %d bytes\n", total); printf("===================================\n"); -} - + objc_mutex_unlock(__objc_runtime_mutex); +} +/* Returns the uninstalled dispatch table indicator. + If a class' dispatch table points to __objc_uninstalled_dtable + then that means it needs its dispatch table to be installed. */ +__inline__ +struct sarray* +objc_get_uninstalled_dtable() +{ + return __objc_uninstalled_dtable; +} diff --git a/gnu/usr.bin/gcc/objc/thr-decosf1.c b/gnu/usr.bin/gcc/objc/thr-decosf1.c new file mode 100644 index 00000000000..0f7063b7e83 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-decosf1.c @@ -0,0 +1,281 @@ +/* GNU Objective C Runtime Thread Interface + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <pthread.h> +#include <objc/thr.h> +#include "runtime.h" + +/* Key structure for maintaining thread specific storage */ +static pthread_key_t _objc_thread_storage; + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + /* Initialize the thread storage key */ + return pthread_keycreate(&_objc_thread_storage, NULL); +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + /* Destroy the thread storage key */ + /* Not implemented yet */ + /* return pthread_key_delete(&_objc_thread_storage); */ + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + objc_thread_t thread_id; + pthread_t new_thread_handle; + + if (pthread_create(&new_thread_handle, pthread_attr_default, + (void *)func, arg) == 0) + { + /* ??? May not work! (64bit) */ + thread_id = *(objc_thread_t *)&new_thread_handle; + pthread_detach(&new_thread_handle); /* Fully detach thread. */ + } + else + thread_id = NULL; + + return thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + int sys_priority = 0; + + switch (priority) + { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_priority = (PRI_FG_MIN_NP + PRI_FG_MAX_NP) / 2; + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_priority = (PRI_BG_MIN_NP + PRI_BG_MAX_NP) / 2; + break; + } + + /* Change the priority. */ + if (pthread_setprio(pthread_self(), sys_priority) >= 0) + return 0; + else + /* Failed */ + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + int sys_priority; + + if ((sys_priority = pthread_getprio(pthread_self())) >= 0) { + if (sys_priority >= PRI_FG_MIN_NP && sys_priority <= PRI_FG_MAX_NP) + return OBJC_THREAD_INTERACTIVE_PRIORITY; + if (sys_priority >= PRI_BG_MIN_NP && sys_priority <= PRI_BG_MAX_NP) + return OBJC_THREAD_BACKGROUND_PRIORITY; + return OBJC_THREAD_LOW_PRIORITY; + } + + /* Failed */ + return -1; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + pthread_yield(); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* exit the thread */ + pthread_exit(&__objc_thread_exit_status); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + pthread_t self = pthread_self(); + + return (objc_thread_t) pthread_getunique_np (&self); +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + return pthread_setspecific(_objc_thread_storage, value); +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + void *value = NULL; + + if ( !(pthread_getspecific(_objc_thread_storage, &value)) ) + return value; + + return NULL; +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)), + pthread_mutexattr_default)) + return -1; + else + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend)))) + return -1; + else + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend))); +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + if (pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend))) != 1) + return -1; + else + return 0; +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend))); +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; + + /* + if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL)) + return -1; + else + return 0; + */ +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; + + /* + return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend))); + */ +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + /* Unimplemented. */ + return -1; + + /* + return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)), + (pthread_mutex_t *)(&(mutex->backend))); + */ +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; + + /* + return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend))); + */ +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; + + /* + return pthread_cond_signal((pthread_cond_t *)(&(condition->backend))); + */ +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-irix.c b/gnu/usr.bin/gcc/objc/thr-irix.c new file mode 100644 index 00000000000..528a3e3a434 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-irix.c @@ -0,0 +1,235 @@ +/* GNU Objective C Runtime Thread Interface - SGI IRIX Implementation + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/sysmp.h> +#include <sys/prctl.h> +#include <ulocks.h> +#include <objc/thr.h> +#include "runtime.h" + +/* Key structure for maintaining thread specific storage */ +static void * __objc_shared_arena_handle = NULL; + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + /* Name of IRIX arena. */ + char arena_name[64]; + + DEBUG_PRINTF("__objc_init_thread_system\n"); + + /* Construct a temporary name for arena. */ + sprintf(arena_name, "/usr/tmp/objc_%05u", (unsigned)getpid()); + + /* Up to 256 threads. Arena only for threads. */ + usconfig(CONF_INITUSERS, 256); + usconfig(CONF_ARENATYPE, US_SHAREDONLY); + + /* Initialize the arena */ + if (!(__objc_shared_arena_handle = usinit(arena_name))) + /* Failed */ + return -1; + + return 0; +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + objc_thread_t thread_id; + int sys_id; + + if ((sys_id = sproc((void *)func, PR_SALL, arg)) >= 0) + thread_id = (objc_thread_t)sys_id; + else + thread_id = NULL; + + return thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + /* Not implemented yet */ + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + /* Not implemented yet */ + return OBJC_THREAD_INTERACTIVE_PRIORITY; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + sginap(0); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* IRIX only has exit. */ + exit(__objc_thread_exit_status); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + /* Threads are processes. */ + return (objc_thread_t)get_pid(); +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + *((void **)&PRDA->usr_prda) = value; + return 0; +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + return *((void **)&PRDA->usr_prda); +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + if (!( (ulock_t)(mutex->backend) = usnewlock(__objc_shared_arena_handle) )) + return -1; + else + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + usfreelock((ulock_t)(mutex->backend), __objc_shared_arena_handle); + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + if (ussetlock((ulock_t)(mutex->backend)) == 0) + return -1; + else + return 0; +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + if (ustestlock((ulock_t)(mutex->backend)) == 0) + return -1; + else + return 0; +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + usunsetlock((ulock_t)(mutex->backend)); + return 0; +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + /* Unimplemented. */ + return -1; +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-mach.c b/gnu/usr.bin/gcc/objc/thr-mach.c new file mode 100644 index 00000000000..44af0c1e286 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-mach.c @@ -0,0 +1,312 @@ +/* GNU Objective C Runtime Thread Implementation + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + Modified for Mach threads by Bill Bumgarner <bbum@friday.com> + Condition functions added by Mircea Oancea <mircea@first.elcom.pub.ro> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <mach/mach.h> +#include <mach/cthreads.h> +#include <objc/thr.h> +#include "runtime.h" + +/* + Obtain the maximum thread priority that can set for t. Under the + mach threading model, it is possible for the developer to adjust the + maximum priority downward only-- cannot be raised without superuser + privileges. Once lowered, it cannot be raised. + */ +static int __mach_get_max_thread_priority(cthread_t t, int *base) +{ + thread_t threadP; + kern_return_t error; + struct thread_sched_info info; + unsigned int info_count=THREAD_SCHED_INFO_COUNT; + + if (t == NULL) + return -1; + + threadP = cthread_thread(t); /* get thread underlying */ + + error=thread_info(threadP, THREAD_SCHED_INFO, + (thread_info_t)&info, &info_count); + + if (error != KERN_SUCCESS) + return -1; + + if (base != NULL) + *base = info.base_priority; + + return info.max_priority; +} + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + return 0; +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + objc_thread_t thread_id; + cthread_t new_thread_handle; + + /* create thread */ + new_thread_handle = cthread_fork((cthread_fn_t)func, arg); + + if(new_thread_handle) + { + /* this is not terribly portable */ + thread_id = *(objc_thread_t *)&new_thread_handle; + cthread_detach(new_thread_handle); + } + else + thread_id = NULL; + + return thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + objc_thread_t *t = objc_thread_id(); + cthread_t cT = (cthread_t) t; + int maxPriority = __mach_get_max_thread_priority(cT, NULL); + int sys_priority = 0; + + if (maxPriority == -1) + return -1; + + switch (priority) + { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_priority = maxPriority; + break; + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_priority = (maxPriority * 2) / 3; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_priority = maxPriority / 3; + break; + default: + return -1; + } + + if (sys_priority == 0) + return -1; + + /* Change the priority */ + if (cthread_priority(cT, sys_priority, 0) == KERN_SUCCESS) + return 0; + else + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + objc_thread_t *t = objc_thread_id(); + cthread_t cT = (cthread_t) t; /* see objc_thread_id() */ + int basePriority; + int maxPriority; + int sys_priority = 0; + + int interactiveT, backgroundT, lowT; /* thresholds */ + + maxPriority = __mach_get_max_thread_priority(cT, &basePriority); + + if(maxPriority == -1) + return -1; + + if (basePriority > ( (maxPriority * 2) / 3)) + return OBJC_THREAD_INTERACTIVE_PRIORITY; + + if (basePriority > ( maxPriority / 3)) + return OBJC_THREAD_BACKGROUND_PRIORITY; + + return OBJC_THREAD_LOW_PRIORITY; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + cthread_yield(); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* exit the thread */ + cthread_exit(&__objc_thread_exit_status); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + cthread_t self = cthread_self(); + + return *(objc_thread_t *)&self; +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + cthread_set_data(cthread_self(), (any_t) value); + return 0; +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + return (void *) cthread_data(cthread_self()); +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + int err = 0; + mutex->backend = objc_malloc(sizeof(struct mutex)); + + err = mutex_init((mutex_t)(mutex->backend)); + + if (err != 0) + { + objc_free(mutex->backend); + return -1; + } + else + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + mutex_clear((mutex_t)(mutex->backend)); + + objc_free(mutex->backend); + mutex->backend = NULL; + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + mutex_lock((mutex_t)(mutex->backend)); + return 0; +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + if (mutex_try_lock((mutex_t)(mutex->backend)) == 0) + return -1; + else + return 0; +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + mutex_unlock((mutex_t)(mutex->backend)); + return 0; +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + condition->backend = objc_malloc(sizeof(struct condition)); + condition_init((condition_t)(condition->backend)); + return 0; +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + condition_clear((condition_t)(condition->backend)); + objc_free(condition->backend); + condition->backend = NULL; + return 0; +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + condition_wait((condition_t)(condition->backend), + (mutex_t)(mutex->backend)); + return 0; +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + condition_broadcast((condition_t)(condition->backend)); + return 0; +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + condition_signal((condition_t)(condition->backend)); + return 0; +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-os2.c b/gnu/usr.bin/gcc/objc/thr-os2.c new file mode 100644 index 00000000000..a0d7d436613 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-os2.c @@ -0,0 +1,267 @@ +/* GNU Objective C Runtime Thread Interface - OS/2 emx Implementation + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Thomas Baier (baier@ci.tuwien.ac.at) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <objc/thr.h> +#include "runtime.h" + +#define INCL_DOSSEMAPHORES +#define INCL_DOSPROCESS + +/* + * conflicts with objc.h: SEL, BOOL, id + * solution: prefixing those with _OS2_ before including <os2.h> + */ +#define SEL _OS2_SEL +#define BOOL _OS2_BOOL +#define id _OS2_id +#include <os2.h> +#undef id +#undef SEL +#undef BOOL + +#include <stdlib.h> + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + return 0; +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + int thread_id = 0; + + if ((thread_id = _beginthread (func,NULL,32768,arg)) < 0) + thread_id = 0; + + return (objc_thread_t)thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + ULONG sys_class = 0; + ULONG sys_priority = 0; + + /* OBJC_THREAD_INTERACTIVE_PRIORITY -> PRTYC_FOREGROUNDSERVER + * OBJC_THREAD_BACKGROUND_PRIORITY -> PRTYC_REGULAR + * OBJC_THREAD_LOW_PRIORITY -> PRTYC_IDLETIME */ + + switch (priority) { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_class = PRTYC_REGULAR; + sys_priority = 10; + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_class = PRTYC_IDLETIME; + sys_priority = 25; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_class = PRTYC_IDLETIME; + sys_priority = 0; + break; + } + + /* Change priority */ + if (!DosSetPriority (PRTYS_THREAD,sys_class,sys_priority,*_threadid)) + return 0; + else + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + PTIB ptib; + PPIB ppib; + + /* get information about current thread */ + DosGetInfoBlocks (&ptib,&ppib); + + switch (ptib->tib_ptib2->tib2_ulpri) + { + case PRTYC_IDLETIME: + case PRTYC_REGULAR: + case PRTYC_TIMECRITICAL: + case PRTYC_FOREGROUNDSERVER: + default: + return OBJC_THREAD_INTERACTIVE_PRIORITY; + } + + return -1; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + DosSleep (0); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* terminate the thread, NEVER use DosExit () */ + _endthread (); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + return (objc_thread_t) *_threadid; +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + *_threadstore () = value; + + return 0; +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + return *_threadstore (); +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + if (DosCreateMutexSem (NULL, (HMTX)(&(mutex->backend)),0L,0) > 0) + return -1; + else + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + DosCloseMutexSem ((HMTX)(mutex->backend)); + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + if (DosRequestMutexSem ((HMTX)(mutex->backend),-1L) != 0) + return -1; + else + return 0; +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + if (DosRequestMutexSem ((HMTX)(mutex->backend),0L) != 0) + return -1; + else + return 0; +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + if (DosReleaseMutexSem((HMTX)(mutex->backend)) != 0) + return -1; + else + return 0; +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + /* Unimplemented. */ + return -1; +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-posix.c b/gnu/usr.bin/gcc/objc/thr-posix.c new file mode 100644 index 00000000000..5b40f711be8 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-posix.c @@ -0,0 +1,229 @@ +/* GNU Objective C Runtime Thread Interface for POSIX compliant threads + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + Modified for Linux/Pthreads by Kai-Uwe Sattler (kus@iti.cs.uni-magdeburg.de) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <objc/thr.h> +#include "runtime.h" +#include <pthread.h> + +/* Key structure for maintaining thread specific storage */ +static pthread_key_t _objc_thread_storage; + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + /* Initialize the thread storage key */ + return pthread_key_create(&_objc_thread_storage, NULL); +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + objc_thread_t thread_id; + pthread_t new_thread_handle; + + if ( !(pthread_create(&new_thread_handle, NULL, (void *)func, arg)) ) + thread_id = *(objc_thread_t *)&new_thread_handle; + else + thread_id = NULL; + + return thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + /* Not implemented yet */ + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + /* Not implemented yet */ + return -1; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + sched_yield(); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* exit the thread */ + pthread_exit(&__objc_thread_exit_status); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + pthread_t self = pthread_self(); + + return *(objc_thread_t *)&self; +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + return pthread_setspecific(_objc_thread_storage, value); +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + return pthread_getspecific(_objc_thread_storage); +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + mutex->backend = objc_malloc(sizeof(pthread_mutex_t)); + + if (pthread_mutex_init((pthread_mutex_t *)mutex->backend, NULL)) + { + objc_free(mutex->backend); + mutex->backend = NULL; + return -1; + } + + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + if (pthread_mutex_destroy((pthread_mutex_t *)mutex->backend)) + return -1; + + objc_free(mutex->backend); + mutex->backend = NULL; + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + return pthread_mutex_lock((pthread_mutex_t *)mutex->backend); +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + return pthread_mutex_trylock((pthread_mutex_t *)mutex->backend); +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + return pthread_mutex_unlock((pthread_mutex_t *)mutex->backend); +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + condition->backend = objc_malloc(sizeof(pthread_cond_t)); + + if (pthread_cond_init((pthread_cond_t *)condition->backend, NULL)) + { + objc_free(condition->backend); + condition->backend = NULL; + return -1; + } + + return 0; +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + if (pthread_cond_destroy((pthread_cond_t *)condition->backend)) + return -1; + + objc_free(condition->backend); + condition->backend = NULL; + return 0; +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + return pthread_cond_wait((pthread_cond_t *)condition->backend, + (pthread_mutex_t *)mutex->backend); +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + return pthread_cond_broadcast((pthread_cond_t *)condition->backend); +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + return pthread_cond_signal((pthread_cond_t *)condition->backend); +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-pthreads.c b/gnu/usr.bin/gcc/objc/thr-pthreads.c new file mode 100644 index 00000000000..2efdd15bc54 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-pthreads.c @@ -0,0 +1,218 @@ +/* GNU Objective C Runtime Thread Implementation for PCThreads under GNU/Linux. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Scott Christley <scottc@net-community.com> + Condition functions added by: Mircea Oancea <mircea@first.elcom.pub.ro> + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <pcthread.h> +#include <objc/thr.h> +#include "runtime.h" + +/* Key structure for maintaining thread specific storage */ +static pthread_key_t _objc_thread_storage; + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + /* Initialize the thread storage key */ + return pthread_key_create(&_objc_thread_storage, NULL); +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + /* Destroy the thread storage key */ + /* Not implemented yet */ + /* return pthread_key_delete(&_objc_thread_storage); */ + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + objc_thread_t thread_id; + pthread_t new_thread_handle; + + if ( !(pthread_create(&new_thread_handle, NULL, (void *)func, arg)) ) + thread_id = *(objc_thread_t *)&new_thread_handle; + else + thread_id = NULL; + + return thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + /* Not implemented yet */ + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + /* Not implemented yet */ + return OBJC_THREAD_INTERACTIVE_PRIORITY; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + pthread_yield(NULL); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* exit the thread */ + pthread_exit(&__objc_thread_exit_status); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + pthread_t self = pthread_self(); + + return *(objc_thread_t *)&self; +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + return pthread_setspecific(_objc_thread_storage, value); +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + void *value = NULL; + + if ( !(pthread_getspecific(_objc_thread_storage, &value)) ) + return value; + + return NULL; +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)), NULL)) + return -1; + else + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend)))) + return -1; + else + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + return pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend))); +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + return pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend))); +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend))); +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL)) + return -1; + else + return 0; +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + return pthread_cond_destroy((pthread_cond_t *)(&(condition->backend))); +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + return pthread_cond_wait((pthread_cond_t *)(&(condition->backend)), + (pthread_mutex_t *)(&(mutex->backend))); +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend))); +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + return pthread_cond_signal((pthread_cond_t *)(&(condition->backend))); +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-single.c b/gnu/usr.bin/gcc/objc/thr-single.c new file mode 100644 index 00000000000..b196677c6b6 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-single.c @@ -0,0 +1,192 @@ +/* GNU Objective C Runtime Thread Implementation + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <objc/thr.h> +#include "runtime.h" + +/* Thread local storage for a single thread */ +static void *thread_local_storage = NULL; + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + /* No thread support available */ + return -1; +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + /* No thread support available */ + return -1; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + /* No thread support available */ + return NULL; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + /* No thread support available */ + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + return OBJC_THREAD_INTERACTIVE_PRIORITY; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + return; +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* No thread support available */ + /* Should we really exit the program */ + /* exit(&__objc_thread_exit_status); */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + /* No thread support, use 1. */ + return (objc_thread_t)1; +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + thread_local_storage = value; + return 0; +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + return thread_local_storage; +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + /* There can only be one thread, so we always get the lock */ + return 0; +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + /* There can only be one thread, so we always get the lock */ + return 0; +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + return 0; +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + return 0; +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + return 0; +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + return 0; +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + return 0; +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + return 0; +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-solaris.c b/gnu/usr.bin/gcc/objc/thr-solaris.c new file mode 100644 index 00000000000..90351b43cf6 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-solaris.c @@ -0,0 +1,259 @@ +/* GNU Objective C Runtime Thread Interface + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + Conditions added by Mircea Oancea (mircea@first.elcom.pub.ro) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <objc/thr.h> +#include "runtime.h" + +#include <thread.h> +#include <synch.h> +#include <errno.h> + +/* Key structure for maintaining thread specific storage */ +static thread_key_t __objc_thread_data_key; + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + /* Initialize the thread storage key */ + if (thr_keycreate(&__objc_thread_data_key, NULL) == 0) + return 0; + else + return -1; +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + objc_thread_t thread_id; + thread_t new_thread_id = 0; + + if (thr_create(NULL, 0, (void *)func, arg, + THR_DETACHED | THR_NEW_LWP, + &new_thread_id) == 0) + thread_id = *(objc_thread_t *)&new_thread_id; + else + thread_id = NULL; + + return thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + int sys_priority = 0; + + switch (priority) + { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_priority = 300; + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_priority = 200; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_priority = 1000; + break; + } + + /* Change priority */ + if (thr_setprio(thr_self(), sys_priority) == 0) + return 0; + else + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + int sys_priority; + + if (thr_getprio(thr_self(), &sys_priority) == 0) + { + if (sys_priority >= 250) + return OBJC_THREAD_INTERACTIVE_PRIORITY; + else if (sys_priority >= 150) + return OBJC_THREAD_BACKGROUND_PRIORITY; + return OBJC_THREAD_LOW_PRIORITY; + } + + /* Couldn't get priority. */ + return -1; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + thr_yield(); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* exit the thread */ + thr_exit(&__objc_thread_exit_status); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + return (objc_thread_t)thr_self(); +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + if (thr_setspecific(__objc_thread_data_key, value) == 0) + return 0; + else + return -1; +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + void *value = NULL; + + if (thr_getspecific(__objc_thread_data_key, &value) == 0) + return value; + + return NULL; +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + if (mutex_init( (mutex_t *)(&(mutex->backend)), USYNC_THREAD, 0)) + return -1; + else + return 0; +} + + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + mutex_destroy((mutex_t *)(&(mutex->backend))); + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + if (mutex_lock((mutex_t *)(&(mutex->backend))) != 0) + return -1; + else + return 0; +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + if (mutex_trylock((mutex_t *)(&(mutex->backend))) != 0) + return -1; + else + return 0; +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + if (mutex_unlock((mutex_t *)(&(mutex->backend))) != 0) + return -1; + else + return 0; +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + return cond_init((cond_t *)(&(condition->backend)), USYNC_THREAD, NULL); +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + return cond_destroy((cond_t *)(&(condition->backend))); +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + return cond_wait((cond_t *)(&(condition->backend)), + (mutex_t *)(&(mutex->backend))); +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + return cond_broadcast((cond_t *)(&(condition->backend))); +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + return cond_signal((cond_t *)(&(condition->backend))); +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr-win32.c b/gnu/usr.bin/gcc/objc/thr-win32.c new file mode 100644 index 00000000000..8570ffd997e --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr-win32.c @@ -0,0 +1,272 @@ +/* GNU Objective C Runtime Thread Interface - Win32 Implementation + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <objc/thr.h> +#include "runtime.h" + +#ifndef __OBJC__ +#define __OBJC__ +#endif +#include <windows.h> + +/* Key structure for maintaining thread specific storage */ +static DWORD __objc_data_tls = (DWORD)-1; + +/* Backend initialization functions */ + +/* Initialize the threads subsystem. */ +int +__objc_init_thread_system(void) +{ + /* Initialize the thread storage key */ + if ((__objc_data_tls = TlsAlloc()) != (DWORD)-1) + return 0; + else + return -1; +} + +/* Close the threads subsystem. */ +int +__objc_close_thread_system(void) +{ + if (__objc_data_tls != (DWORD)-1) + TlsFree(__objc_data_tls); + return 0; +} + +/* Backend thread functions */ + +/* Create a new thread of execution. */ +objc_thread_t +__objc_thread_detach(void (*func)(void *arg), void *arg) +{ + DWORD thread_id = 0; + HANDLE win32_handle; + + if (!(win32_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, + arg, 0, &thread_id))) + thread_id = 0; + + return (objc_thread_t)thread_id; +} + +/* Set the current thread's priority. */ +int +__objc_thread_set_priority(int priority) +{ + int sys_priority = 0; + + switch (priority) + { + case OBJC_THREAD_INTERACTIVE_PRIORITY: + sys_priority = THREAD_PRIORITY_NORMAL; + break; + default: + case OBJC_THREAD_BACKGROUND_PRIORITY: + sys_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case OBJC_THREAD_LOW_PRIORITY: + sys_priority = THREAD_PRIORITY_LOWEST; + break; + } + + /* Change priority */ + if (SetThreadPriority(GetCurrentThread(), sys_priority)) + return 0; + else + return -1; +} + +/* Return the current thread's priority. */ +int +__objc_thread_get_priority(void) +{ + int sys_priority; + + sys_priority = GetThreadPriority(GetCurrentThread()); + + switch (sys_priority) + { + case THREAD_PRIORITY_HIGHEST: + case THREAD_PRIORITY_TIME_CRITICAL: + case THREAD_PRIORITY_ABOVE_NORMAL: + case THREAD_PRIORITY_NORMAL: + return OBJC_THREAD_INTERACTIVE_PRIORITY; + + default: + case THREAD_PRIORITY_BELOW_NORMAL: + return OBJC_THREAD_BACKGROUND_PRIORITY; + + case THREAD_PRIORITY_IDLE: + case THREAD_PRIORITY_LOWEST: + return OBJC_THREAD_LOW_PRIORITY; + } + + /* Couldn't get priority. */ + return -1; +} + +/* Yield our process time to another thread. */ +void +__objc_thread_yield(void) +{ + Sleep(0); +} + +/* Terminate the current thread. */ +int +__objc_thread_exit(void) +{ + /* exit the thread */ + ExitThread(__objc_thread_exit_status); + + /* Failed if we reached here */ + return -1; +} + +/* Returns an integer value which uniquely describes a thread. */ +objc_thread_t +__objc_thread_id(void) +{ + return (objc_thread_t)GetCurrentThreadId(); +} + +/* Sets the thread's local storage pointer. */ +int +__objc_thread_set_data(void *value) +{ + if (TlsSetValue(__objc_data_tls, value)) + return 0; + else + return -1; +} + +/* Returns the thread's local storage pointer. */ +void * +__objc_thread_get_data(void) +{ + return TlsGetValue(__objc_data_tls); /* Return thread data. */ +} + +/* Backend mutex functions */ + +/* Allocate a mutex. */ +int +__objc_mutex_allocate(objc_mutex_t mutex) +{ + if ((mutex->backend = (void *)CreateMutex(NULL, 0, NULL)) == NULL) + return -1; + else + return 0; +} + +/* Deallocate a mutex. */ +int +__objc_mutex_deallocate(objc_mutex_t mutex) +{ + CloseHandle((HANDLE)(mutex->backend)); + return 0; +} + +/* Grab a lock on a mutex. */ +int +__objc_mutex_lock(objc_mutex_t mutex) +{ + int status; + + status = WaitForSingleObject((HANDLE)(mutex->backend), INFINITE); + if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED) + return -1; + else + return 0; +} + +/* Try to grab a lock on a mutex. */ +int +__objc_mutex_trylock(objc_mutex_t mutex) +{ + int status; + + status = WaitForSingleObject((HANDLE)(mutex->backend), 0); + if (status != WAIT_OBJECT_0 && status != WAIT_ABANDONED) + return -1; + else + return 0; +} + +/* Unlock the mutex */ +int +__objc_mutex_unlock(objc_mutex_t mutex) +{ + if (ReleaseMutex((HANDLE)(mutex->backend)) == 0) + return -1; + else + return 0; +} + +/* Backend condition mutex functions */ + +/* Allocate a condition. */ +int +__objc_condition_allocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Deallocate a condition. */ +int +__objc_condition_deallocate(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Wait on the condition */ +int +__objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + /* Unimplemented. */ + return -1; +} + +/* Wake up all threads waiting on this condition. */ +int +__objc_condition_broadcast(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* Wake up one thread waiting on this condition. */ +int +__objc_condition_signal(objc_condition_t condition) +{ + /* Unimplemented. */ + return -1; +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr.c b/gnu/usr.bin/gcc/objc/thr.c new file mode 100644 index 00000000000..f1c957aaa15 --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr.c @@ -0,0 +1,534 @@ +/* GNU Objective C Runtime Thread Interface + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files compiled with + GCC to produce an executable, this does not cause the resulting executable + to be covered by the GNU General Public License. This exception does not + however invalidate any other reasons why the executable file might be + covered by the GNU General Public License. */ + +#include <stdlib.h> +#include "runtime.h" + +/* Global exit status. */ +int __objc_thread_exit_status = 0; + +/* Flag which lets us know if we ever became multi threaded */ +int __objc_is_multi_threaded = 0; + +/* The hook function called when the runtime becomes multi threaded */ +objc_thread_callback _objc_became_multi_threaded = NULL; + +/* + Use this to set the hook function that will be called when the + runtime initially becomes multi threaded. + The hook function is only called once, meaning only when the + 2nd thread is spawned, not for each and every thread. + + It returns the previous hook function or NULL if there is none. + + A program outside of the runtime could set this to some function so + it can be informed; for example, the GNUstep Base Library sets it + so it can implement the NSBecomingMultiThreaded notification. + */ +objc_thread_callback objc_set_thread_callback(objc_thread_callback func) +{ + objc_thread_callback temp = _objc_became_multi_threaded; + _objc_became_multi_threaded = func; + return temp; +} + +/* + Private functions + + These functions are utilized by the frontend, but they are not + considered part of the public interface. + */ + +/* + First function called in a thread, starts everything else. + + This function is passed to the backend by objc_thread_detach + as the starting function for a new thread. + */ +struct __objc_thread_start_state +{ + SEL selector; + id object; + id argument; +}; + +static volatile void +__objc_thread_detach_function(struct __objc_thread_start_state *istate) +{ + /* Valid state? */ + if (istate) { + id (*imp)(id,SEL,id); + SEL selector = istate->selector; + id object = istate->object; + id argument = istate->argument; + + /* Don't need anymore so free it */ + objc_free(istate); + + /* Clear out the thread local storage */ + objc_thread_set_data(NULL); + + /* Check to see if we just became multi threaded */ + if (!__objc_is_multi_threaded) + { + __objc_is_multi_threaded = 1; + + /* Call the hook function */ + if (_objc_became_multi_threaded != NULL) + (*_objc_became_multi_threaded)(); + } + + /* Call the method */ + if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector))) + (*imp)(object, selector, argument); + else + objc_error(object, OBJC_ERR_UNIMPLEMENTED, + "objc_thread_detach called with bad selector.\n"); + } + else + objc_error(nil, OBJC_ERR_BAD_STATE, + "objc_thread_detach called with NULL state.\n"); + + /* Exit the thread */ + objc_thread_exit(); +} + +/* + Frontend functions + + These functions constitute the public interface to the Objective-C thread + and mutex functionality. + */ + +/* Frontend thread functions */ + +/* + Detach a new thread of execution and return its id. Returns NULL if fails. + Thread is started by sending message with selector to object. Message + takes a single argument. + */ +objc_thread_t +objc_thread_detach(SEL selector, id object, id argument) +{ + struct __objc_thread_start_state *istate; + objc_thread_t thread_id = NULL; + + /* Allocate the state structure */ + if (!(istate = (struct __objc_thread_start_state *) + objc_malloc(sizeof(*istate)))) + return NULL; + + /* Initialize the state structure */ + istate->selector = selector; + istate->object = object; + istate->argument = argument; + + /* lock access */ + objc_mutex_lock(__objc_runtime_mutex); + + /* Call the backend to spawn the thread */ + if ((thread_id = __objc_thread_detach((void *)__objc_thread_detach_function, + istate)) == NULL) + { + /* failed! */ + objc_mutex_unlock(__objc_runtime_mutex); + objc_free(istate); + return NULL; + } + + /* Increment our thread counter */ + __objc_runtime_threads_alive++; + objc_mutex_unlock(__objc_runtime_mutex); + + return thread_id; +} + +/* Set the current thread's priority. */ +int +objc_thread_set_priority(int priority) +{ + /* Call the backend */ + return __objc_thread_set_priority(priority); +} + +/* Return the current thread's priority. */ +int +objc_thread_get_priority(void) +{ + /* Call the backend */ + return __objc_thread_get_priority(); +} + +/* + Yield our process time to another thread. Any BUSY waiting that is done + by a thread should use this function to make sure that other threads can + make progress even on a lazy uniprocessor system. + */ +void +objc_thread_yield(void) +{ + /* Call the backend */ + __objc_thread_yield(); +} + +/* + Terminate the current tread. Doesn't return. + Actually, if it failed returns -1. + */ +int +objc_thread_exit(void) +{ + /* Decrement our counter of the number of threads alive */ + objc_mutex_lock(__objc_runtime_mutex); + __objc_runtime_threads_alive--; + objc_mutex_unlock(__objc_runtime_mutex); + + /* Call the backend to terminate the thread */ + return __objc_thread_exit(); +} + +/* + Returns an integer value which uniquely describes a thread. Must not be + NULL which is reserved as a marker for "no thread". + */ +objc_thread_t +objc_thread_id(void) +{ + /* Call the backend */ + return __objc_thread_id(); +} + +/* + Sets the thread's local storage pointer. + Returns 0 if successful or -1 if failed. + */ +int +objc_thread_set_data(void *value) +{ + /* Call the backend */ + return __objc_thread_set_data(value); +} + +/* + Returns the thread's local storage pointer. Returns NULL on failure. + */ +void * +objc_thread_get_data(void) +{ + /* Call the backend */ + return __objc_thread_get_data(); +} + +/* Frontend mutex functions */ + +/* + Allocate a mutex. Return the mutex pointer if successful or NULL if the + allocation failed for any reason. + */ +objc_mutex_t +objc_mutex_allocate(void) +{ + objc_mutex_t mutex; + + /* Allocate the mutex structure */ + if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex)))) + return NULL; + + /* Call backend to create the mutex */ + if (__objc_mutex_allocate(mutex)) + { + /* failed! */ + objc_free(mutex); + return NULL; + } + + /* Initialize mutex */ + mutex->owner = NULL; + mutex->depth = 0; + return mutex; +} + +/* + Deallocate a mutex. Note that this includes an implicit mutex_lock to + insure that no one else is using the lock. It is legal to deallocate + a lock if we have a lock on it, but illegal to deallocate a lock held + by anyone else. + Returns the number of locks on the thread. (1 for deallocate). + */ +int +objc_mutex_deallocate(objc_mutex_t mutex) +{ + int depth; + + /* Valid mutex? */ + if (!mutex) + return -1; + + /* Acquire lock on mutex */ + depth = objc_mutex_lock(mutex); + + /* Call backend to destroy mutex */ + if (__objc_mutex_deallocate(mutex)) + return -1; + + /* Free the mutex structure */ + objc_free(mutex); + + /* Return last depth */ + return depth; +} + +/* + Grab a lock on a mutex. If this thread already has a lock on this mutex + then we increment the lock count. If another thread has a lock on the + mutex we block and wait for the thread to release the lock. + Returns the lock count on the mutex held by this thread. + */ +int +objc_mutex_lock(objc_mutex_t mutex) +{ + objc_thread_t thread_id; + int status; + + /* Valid mutex? */ + if (!mutex) + return -1; + + /* If we already own the lock then increment depth */ + thread_id = objc_thread_id(); + if (mutex->owner == thread_id) + return ++mutex->depth; + + /* Call the backend to lock the mutex */ + status = __objc_mutex_lock(mutex); + + /* Failed? */ + if (status) + return status; + + /* Successfully locked the thread */ + mutex->owner = thread_id; + return mutex->depth = 1; +} + +/* + Try to grab a lock on a mutex. If this thread already has a lock on + this mutex then we increment the lock count and return it. If another + thread has a lock on the mutex returns -1. + */ +int +objc_mutex_trylock(objc_mutex_t mutex) +{ + objc_thread_t thread_id; + int status; + + /* Valid mutex? */ + if (!mutex) + return -1; + + /* If we already own the lock then increment depth */ + thread_id = objc_thread_id(); + if (mutex->owner == thread_id) + return ++mutex->depth; + + /* Call the backend to try to lock the mutex */ + status = __objc_mutex_trylock(mutex); + + /* Failed? */ + if (status) + return status; + + /* Successfully locked the thread */ + mutex->owner = thread_id; + return mutex->depth = 1; +} + +/* + Unlocks the mutex by one level. + Decrements the lock count on this mutex by one. + If the lock count reaches zero, release the lock on the mutex. + Returns the lock count on the mutex. + It is an error to attempt to unlock a mutex which this thread + doesn't hold in which case return -1 and the mutex is unaffected. + */ +int +objc_mutex_unlock(objc_mutex_t mutex) +{ + objc_thread_t thread_id; + int status; + + /* Valid mutex? */ + if (!mutex) + return -1; + + /* If another thread owns the lock then abort */ + thread_id = objc_thread_id(); + if (mutex->owner != thread_id) + return -1; + + /* Decrement depth and return */ + if (mutex->depth > 1) + return --mutex->depth; + + /* Depth down to zero so we are no longer the owner */ + mutex->depth = 0; + mutex->owner = NULL; + + /* Have the backend unlock the mutex */ + status = __objc_mutex_unlock(mutex); + + /* Failed? */ + if (status) + return status; + + return 0; +} + +/* Frontend condition mutex functions */ + +/* + Allocate a condition. Return the condition pointer if successful or NULL + if the allocation failed for any reason. + */ +objc_condition_t +objc_condition_allocate(void) +{ + objc_condition_t condition; + + /* Allocate the condition mutex structure */ + if (!(condition = + (objc_condition_t)objc_malloc(sizeof(struct objc_condition)))) + return NULL; + + /* Call the backend to create the condition mutex */ + if (__objc_condition_allocate(condition)) + { + /* failed! */ + objc_free(condition); + return NULL; + } + + /* Success! */ + return condition; +} + +/* + Deallocate a condition. Note that this includes an implicit + condition_broadcast to insure that waiting threads have the opportunity + to wake. It is legal to dealloc a condition only if no other + thread is/will be using it. Here we do NOT check for other threads + waiting but just wake them up. + */ +int +objc_condition_deallocate(objc_condition_t condition) +{ + /* Broadcast the condition */ + if (objc_condition_broadcast(condition)) + return -1; + + /* Call the backend to destroy */ + if (__objc_condition_deallocate(condition)) + return -1; + + /* Free the condition mutex structure */ + objc_free(condition); + + return 0; +} + +/* + Wait on the condition unlocking the mutex until objc_condition_signal() + or objc_condition_broadcast() are called for the same condition. The + given mutex *must* have the depth set to 1 so that it can be unlocked + here, so that someone else can lock it and signal/broadcast the condition. + The mutex is used to lock access to the shared data that make up the + "condition" predicate. + */ +int +objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex) +{ + objc_thread_t thread_id; + + /* Valid arguments? */ + if (!mutex || !condition) + return -1; + + /* Make sure we are owner of mutex */ + thread_id = objc_thread_id(); + if (mutex->owner != thread_id) + return -1; + + /* Cannot be locked more than once */ + if (mutex->depth > 1) + return -1; + + /* Virtually unlock the mutex */ + mutex->depth = 0; + mutex->owner = (objc_thread_t)NULL; + + /* Call the backend to wait */ + __objc_condition_wait(condition, mutex); + + /* Make ourselves owner of the mutex */ + mutex->owner = thread_id; + mutex->depth = 1; + + return 0; +} + +/* + Wake up all threads waiting on this condition. It is recommended that + the called would lock the same mutex as the threads in objc_condition_wait + before changing the "condition predicate" and make this call and unlock it + right away after this call. + */ +int +objc_condition_broadcast(objc_condition_t condition) +{ + /* Valid condition mutex? */ + if (!condition) + return -1; + + return __objc_condition_broadcast(condition); +} + +/* + Wake up one thread waiting on this condition. It is recommended that + the called would lock the same mutex as the threads in objc_condition_wait + before changing the "condition predicate" and make this call and unlock it + right away after this call. + */ +int +objc_condition_signal(objc_condition_t condition) +{ + /* Valid condition mutex? */ + if (!condition) + return -1; + + return __objc_condition_signal(condition); +} + +/* End of File */ diff --git a/gnu/usr.bin/gcc/objc/thr.h b/gnu/usr.bin/gcc/objc/thr.h new file mode 100644 index 00000000000..f904733695a --- /dev/null +++ b/gnu/usr.bin/gcc/objc/thr.h @@ -0,0 +1,143 @@ +/* Thread and mutex controls for Objective C. + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +GNU CC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2, or (at your option) any later version. + +GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +GNU CC; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you link this library with files + compiled with GCC to produce an executable, this does not cause + the resulting executable to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + + +#ifndef __thread_INCLUDE_GNU +#define __thread_INCLUDE_GNU + +#include "objc/objc.h" + +/************************************************************************* + * Universal static variables: + */ +extern int __objc_thread_exit_status; /* Global exit status. */ + +/******** + * Thread safe implementation types and functions. + */ + +/* Thread priorities */ +#define OBJC_THREAD_INTERACTIVE_PRIORITY 2 +#define OBJC_THREAD_BACKGROUND_PRIORITY 1 +#define OBJC_THREAD_LOW_PRIORITY 0 + +/* A thread */ +typedef void * objc_thread_t; + +/* This structure represents a single mutual exclusion lock. */ +struct objc_mutex +{ + volatile objc_thread_t owner; /* Id of thread that owns. */ + volatile int depth; /* # of acquires. */ + void * backend; /* Specific to backend */ +}; +typedef struct objc_mutex *objc_mutex_t; + +/* This structure represents a single condition mutex */ +struct objc_condition +{ + void * backend; /* Specific to backend */ +}; +typedef struct objc_condition *objc_condition_t; + +/* Frontend mutex functions */ +objc_mutex_t objc_mutex_allocate(void); +int objc_mutex_deallocate(objc_mutex_t mutex); +int objc_mutex_lock(objc_mutex_t mutex); +int objc_mutex_unlock(objc_mutex_t mutex); +int objc_mutex_trylock(objc_mutex_t mutex); + +/* Frontend condition mutex functions */ +objc_condition_t objc_condition_allocate(void); +int objc_condition_deallocate(objc_condition_t condition); +int objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex); +int objc_condition_signal(objc_condition_t condition); +int objc_condition_broadcast(objc_condition_t condition); + +/* Frontend thread functions */ +objc_thread_t objc_thread_detach(SEL selector, id object, id argument); +void objc_thread_yield(void); +int objc_thread_exit(void); +int objc_thread_set_priority(int priority); +int objc_thread_get_priority(void); +void * objc_thread_get_data(void); +int objc_thread_set_data(void *value); +objc_thread_t objc_thread_id(void); + +/* + Use this to set the hook function that will be called when the + runtime initially becomes multi threaded. + The hook function is only called once, meaning only when the + 2nd thread is spawned, not for each and every thread. + + It returns the previous hook function or NULL if there is none. + + A program outside of the runtime could set this to some function so + it can be informed; for example, the GNUstep Base Library sets it + so it can implement the NSBecomingMultiThreaded notification. + */ +typedef void (*objc_thread_callback)(); +objc_thread_callback objc_set_thread_callback(objc_thread_callback func); + +/* Backend initialization functions */ +int __objc_init_thread_system(void); +int __objc_fini_thread_system(void); + +/* Backend mutex functions */ +int __objc_mutex_allocate(objc_mutex_t mutex); +int __objc_mutex_deallocate(objc_mutex_t mutex); +int __objc_mutex_lock(objc_mutex_t mutex); +int __objc_mutex_trylock(objc_mutex_t mutex); +int __objc_mutex_unlock(objc_mutex_t mutex); + +/* Backend condition mutex functions */ +int __objc_condition_allocate(objc_condition_t condition); +int __objc_condition_deallocate(objc_condition_t condition); +int __objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex); +int __objc_condition_broadcast(objc_condition_t condition); +int __objc_condition_signal(objc_condition_t condition); + +/* Backend thread functions */ +objc_thread_t __objc_thread_detach(void (*func)(void *arg), void *arg); +int __objc_thread_set_priority(int priority); +int __objc_thread_get_priority(void); +void __objc_thread_yield(void); +int __objc_thread_exit(void); +objc_thread_t __objc_thread_id(void); +int __objc_thread_set_data(void *value); +void * __objc_thread_get_data(void); + +#endif /* not __thread_INCLUDE_GNU */ |