diff options
author | obrien <obrien@FreeBSD.org> | 1999-08-26 09:30:50 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 1999-08-26 09:30:50 +0000 |
commit | ad6db0c2d632cfd247d10af9fddb2c33285aa43e (patch) | |
tree | 4823bf291926b5bde4ed5526b4fbaa22fb74caad /contrib/gcc/objc | |
parent | b5506bbe0f5faa2d3e715e22f730378180017e12 (diff) | |
parent | 0bedf4fb30066e5e1d4342a1d3914dae7d37cba7 (diff) | |
download | FreeBSD-src-ad6db0c2d632cfd247d10af9fddb2c33285aa43e.zip FreeBSD-src-ad6db0c2d632cfd247d10af9fddb2c33285aa43e.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r50397,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib/gcc/objc')
29 files changed, 10524 insertions, 394 deletions
diff --git a/contrib/gcc/objc/Make-lang.in b/contrib/gcc/objc/Make-lang.in new file mode 100644 index 0000000..3774e22 --- /dev/null +++ b/contrib/gcc/objc/Make-lang.in @@ -0,0 +1,312 @@ +# Top level makefile fragment for GNU Objective-C +# Copyright (C) 1997, 1998 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$(exeext) objc-runtime +OBJECTIVE-C objective-c: cc1obj$(exeext) 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$(exeext): $(P) $(OBJC_OBJS) $(OBJS) $(LIBDEPS) + $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o $@ $(OBJC_OBJS) $(OBJS) \ + $(LIBS) + +# Objective C language specific files. + +objc-parse.o : $(srcdir)/objc/objc-parse.c \ + $(CONFIG_H) $(TREE_H) $(srcdir)/toplev.h \ + $(srcdir)/c-lex.h $(srcdir)/c-tree.h $(srcdir)/input.h \ + $(srcdir)/flags.h $(srcdir)/output.h $(srcdir)/objc/objc-act.h system.h + $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -I$(srcdir)/objc \ + -c $(srcdir)/objc/objc-parse.c + +$(srcdir)/objc/objc-parse.c : $(srcdir)/objc/objc-parse.y + cd $(srcdir)/objc; \ + $(BISON) $(BISONFLAGS) objc-parse.y -o objc-parse.c + +$(srcdir)/objc/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 + $(srcdir)/move-if-change tmp-objc-prs.y $(srcdir)/objc/objc-parse.y + +objc-act.o : $(srcdir)/objc/objc-act.c \ + $(CONFIG_H) $(TREE_H) $(RTL_H) system.h \ + $(srcdir)/c-tree.h $(srcdir)/c-lex.h $(srcdir)/toplev.h \ + $(srcdir)/flags.h $(srcdir)/objc/objc-act.h $(srcdir)/input.h \ + $(srcdir)/function.h $(srcdir)/output.h $(srcdir)/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) objc/runtime-info.h + $(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 $@ + +$(OBJC_O): $(GCC_PASSES) cc1obj$(exeext) + +# Build the Objective C runtime library. +libobjc.a: cc1obj$(exeext) 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$(exeext) + echo "" > tmp-runtime + echo "/* This file is automatically generated */" >$@ + ./cc1obj -print-objc-runtime-info tmp-runtime >>$@ + rm -f tmp-runtime +# +# 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 +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: + -rm -f objc/objc-parse.y + -rm -f objc/objc-parse.c objc/objc-parse.output + +# +# Stage hooks: + +objc.stage1: stage1-start + -mv objc/*$(objext) stage1/objc + -mv cc1obj$(exeext) stage1 + -mv libobjc.a stage1 +objc.stage2: stage2-start + -mv objc/*$(objext) stage2/objc + -mv cc1obj$(exeext) stage2 + -mv libobjc.a stage2 +objc.stage3: stage3-start + -mv objc/*$(objext) stage3/objc + -mv cc1obj$(exeext) stage3 + -mv libobjc.a stage3 +objc.stage4: stage4-start + -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. +# ??? Note that this should be fixed once the Makefile is fixed to do +# the build in the inner directory. +objc.distdir: $(srcdir)/objc/objc-parse.c + 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/contrib/gcc/objc/Makefile.in b/contrib/gcc/objc/Makefile.in new file mode 100644 index 0000000..5f1bc88 --- /dev/null +++ b/contrib/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$(exeext) + +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/contrib/gcc/objc/NXConstStr.h b/contrib/gcc/objc/NXConstStr.h index 6899c74..c979954 100644 --- a/contrib/gcc/objc/NXConstStr.h +++ b/contrib/gcc/objc/NXConstStr.h @@ -11,7 +11,7 @@ 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 -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +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 @@ -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/contrib/gcc/objc/NXConstStr.m b/contrib/gcc/objc/NXConstStr.m index d3b2117..4d2f3e1 100644 --- a/contrib/gcc/objc/NXConstStr.m +++ b/contrib/gcc/objc/NXConstStr.m @@ -11,7 +11,7 @@ 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 -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +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 @@ -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/contrib/gcc/objc/Object.h b/contrib/gcc/objc/Object.h index 7fb8602..a762acc 100644 --- a/contrib/gcc/objc/Object.h +++ b/contrib/gcc/objc/Object.h @@ -10,7 +10,7 @@ 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 -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +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 diff --git a/contrib/gcc/objc/Object.m b/contrib/gcc/objc/Object.m index 518d02a..64b52f4 100644 --- a/contrib/gcc/objc/Object.m +++ b/contrib/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. @@ -10,7 +10,7 @@ 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 -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +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 @@ -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_verror(self, OBJC_ERR_UNKNOWN, fmt, ap); va_end(ap); return nil; #undef FMT diff --git a/contrib/gcc/objc/README b/contrib/gcc/objc/README index 70cea30..f478d67 100644 --- a/contrib/gcc/objc/README +++ b/contrib/gcc/objc/README @@ -18,7 +18,7 @@ Runtime API functions The runtime is modeled after the NeXT Objective C runtime. That is, most functions have semantics as it is known from the NeXT. The names, however, have changed. All runtime API functions have names -of lowercase letters and and underscores as opposed to the +of lowercase letters and underscores as opposed to the `traditional' mixed case names. The runtime api functions are not documented as of now. Someone offered to write it, and did it, but we were not allowed to diff --git a/contrib/gcc/objc/archive.c b/contrib/gcc/objc/archive.c index 0d55152..c762fe6 100644 --- a/contrib/gcc/objc/archive.c +++ b/contrib/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; @@ -426,12 +434,6 @@ objc_write_object (struct objc_typed_stream* stream, id object) } } -#ifdef __alpha__ -extern int atoi (const char*); -extern size_t strlen(const char*); -extern size_t strcpy(char*, const char*); -#endif - __inline__ int __objc_write_class (struct objc_typed_stream* stream, struct objc_class* class) { @@ -488,7 +490,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 +523,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 +545,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 +567,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 +596,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 +623,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 +650,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 +670,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 +706,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 +754,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 +767,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 +777,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 +787,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 +833,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 +849,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 +894,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 +907,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 +951,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 +961,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 +1036,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 +1053,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 +1067,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 +1139,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 +1149,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 +1163,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 +1257,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 +1349,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 +1409,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 +1430,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 +1448,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 +1478,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 +1493,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 +1538,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 +1625,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/contrib/gcc/objc/class.c b/contrib/gcc/objc/class.c index 3617a09..44aa1b9 100644 --- a/contrib/gcc/objc/class.c +++ b/contrib/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/contrib/gcc/objc/config-lang.in b/contrib/gcc/objc/config-lang.in new file mode 100644 index 0000000..6c9b203 --- /dev/null +++ b/contrib/gcc/objc/config-lang.in @@ -0,0 +1,37 @@ +# Top level configure fragment for the GNU Objective-C Runtime Library. +# Copyright (C) 1997, 1998 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="-x objc-parse.c -x objc-parse.y " + +echo "Using \`$srcdir/objc/thr-${thread_file}.c' as Objective-C Runtime thread file." diff --git a/contrib/gcc/objc/encoding.c b/contrib/gcc/objc/encoding.c index 9620664..e6f84aa 100644 --- a/contrib/gcc/objc/encoding.c +++ b/contrib/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, 1998 Free Software Foundation, Inc. Contributed by Kresten Krab Thorup This file is part of GNU CC. @@ -111,6 +111,9 @@ objc_sizeof_type(const char* type) return sizeof(double); break; + case _C_VOID: + return sizeof(void); + break; case _C_PTR: case _C_ATOM: case _C_CHARPTR: @@ -153,7 +156,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 +224,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 +257,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 +351,7 @@ objc_skip_typespec (const char* type) case _C_FLT: case _C_DBL: case _C_VOID: + case _C_UNDEF: return ++type; break; @@ -352,7 +363,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 +388,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/contrib/gcc/objc/encoding.h b/contrib/gcc/objc/encoding.h index c956034..141e9fe 100644 --- a/contrib/gcc/objc/encoding.h +++ b/contrib/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/contrib/gcc/objc/hash.c b/contrib/gcc/objc/hash.c index a307759..7534330 100644 --- a/contrib/gcc/objc/hash.c +++ b/contrib/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/contrib/gcc/objc/hash.h b/contrib/gcc/objc/hash.h index 7a83b08..bddb791 100644 --- a/contrib/gcc/objc/hash.h +++ b/contrib/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/contrib/gcc/objc/init.c b/contrib/gcc/objc/init.c index 9560dc2..f1fea81 100644 --- a/contrib/gcc/objc/init.c +++ b/contrib/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,337 @@ 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. */ +#ifdef DEBUG +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); +} +#endif + +/* 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 +430,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 +456,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 +477,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 +503,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 +516,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 +538,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 +580,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 +592,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 +619,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 +639,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 +759,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 +782,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 +811,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/contrib/gcc/objc/makefile.dos b/contrib/gcc/objc/makefile.dos index 5321733..3e1b187 100644 --- a/contrib/gcc/objc/makefile.dos +++ b/contrib/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/contrib/gcc/objc/misc.c b/contrib/gcc/objc/misc.c index 033018e..01f9d3b 100644 --- a/contrib/gcc/objc/misc.c +++ b/contrib/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/contrib/gcc/objc/objc-act.c b/contrib/gcc/objc/objc-act.c new file mode 100644 index 0000000..0f4058b --- /dev/null +++ b/contrib/gcc/objc/objc-act.c @@ -0,0 +1,8455 @@ +/* Implement classes and message passing for Objective C. + Copyright (C) 1992, 93-95, 97, 1998 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 "system.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 "output.h" +#include "toplev.h" + +#if USE_CPPLIB +#include "cpplib.h" +extern cpp_reader parse_in; +extern cpp_options parse_options; +static int cpp_initialized; +#endif + +/* 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)); + +#if 0 +static tree init_selector PROTO((int)); +#endif +static tree build_keyword_selector PROTO((tree)); +static tree synth_id_with_class_suffix PROTO((char *, tree)); + +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, + int, int)); + +/* 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)); +#if 0 +static tree build_msg_pool_reference PROTO((int)); +#endif +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_options () +{ +} + +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 (argc, argv) + int argc; + char **argv; +{ + char *p = argv[0]; +#if USE_CPPLIB + if (! cpp_initialized) + { + cpp_reader_init (&parse_in); + parse_in.data = &parse_options; + cpp_options_init (&parse_options); + cpp_initialized = 1; + } +#endif + 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 (argc, argv); + + return 1; +} + +/* used by print-tree.c */ + +void +lang_print_xnode (file, node, indent) + FILE *file ATTRIBUTE_UNUSED; + tree node ATTRIBUTE_UNUSED; + int indent ATTRIBUTE_UNUSED; +{ +} + + +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; + 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; + 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; + + 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, 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. */ + +#if 0 +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; +} +#endif + +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 ATTRIBUTE_UNUSED; +{ +} + + +/* "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, "[%ld", + (long) (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; + int left; + int 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; + + default: + 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; +} + +#if 0 +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; +} +#endif + +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), "[%ld]", + (long) 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), "[%ld]", + (long) (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; + + default: + break; + } + + 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, ">"); + } + } + break; + + default: + break; + } + } +} + +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); + + bcopy (objc_tree_code_type, + tree_code_type + (int) LAST_CODE, + (int) LAST_OBJC_TREE_CODE - (int) LAST_CODE); + 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/contrib/gcc/objc/objc-act.h b/contrib/gcc/objc/objc-act.h new file mode 100644 index 0000000..65224de --- /dev/null +++ b/contrib/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/contrib/gcc/objc/objc-api.h b/contrib/gcc/objc/objc-api.h index c801033..9eb000b 100644 --- a/contrib/gcc/objc/objc-api.h +++ b/contrib/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. @@ -10,7 +10,7 @@ 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 -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +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 @@ -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; @@ -245,7 +301,7 @@ struct objc_protocol_list { /* ** The class number of this class. This must be the same for both the -** class and it's meta class object +** class and its meta class object */ #define CLS_GETNUMBER(cls) (__CLS_INFO(cls) >> (HOST_BITS_PER_LONG/2)) #define CLS_SETNUMBER(cls, num) \ @@ -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/contrib/gcc/objc/objc-tree.def b/contrib/gcc/objc/objc-tree.def new file mode 100644 index 0000000..9d6765b --- /dev/null +++ b/contrib/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/contrib/gcc/objc/objc.gperf b/contrib/gcc/objc/objc.gperf new file mode 100644 index 0000000..407459f --- /dev/null +++ b/contrib/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/contrib/gcc/objc/objc.h b/contrib/gcc/objc/objc.h index 979c5c8..e48b0fd 100644 --- a/contrib/gcc/objc/objc.h +++ b/contrib/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/contrib/gcc/objc/objects.c b/contrib/gcc/objc/objects.c index ebaf117..3e68334 100644 --- a/contrib/gcc/objc/objects.c +++ b/contrib/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/contrib/gcc/objc/runtime.h b/contrib/gcc/objc/runtime.h index 0f4510f..b0eae4a 100644 --- a/contrib/gcc/objc/runtime.h +++ b/contrib/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/contrib/gcc/objc/sarray.c b/contrib/gcc/objc/sarray.c index 9ed2196..7e40fba 100644 --- a/contrib/gcc/objc/sarray.c +++ b/contrib/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/contrib/gcc/objc/sarray.h b/contrib/gcc/objc/sarray.h index a3ec7a9..74fa386 100644 --- a/contrib/gcc/objc/sarray.h +++ b/contrib/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/contrib/gcc/objc/selector.c b/contrib/gcc/objc/selector.c index be68f4f..83c70e4 100644 --- a/contrib/gcc/objc/selector.c +++ b/contrib/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/contrib/gcc/objc/typedstream.h b/contrib/gcc/objc/typedstream.h index 50bd654..eb4642f 100644 --- a/contrib/gcc/objc/typedstream.h +++ b/contrib/gcc/objc/typedstream.h @@ -10,7 +10,7 @@ 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 -or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +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 |