From: Apple Date: Wed, 3 Jul 2002 19:44:03 +0000 (+0000) Subject: objc4-222.tar.gz X-Git-Tag: mac-os-x-102^0 X-Git-Url: https://git.saurik.com/apple/objc4.git/commitdiff_plain/1f20c7a710ebedf4bba6ed44c082df068af45b89 objc4-222.tar.gz --- diff --git a/APPLE_LICENSE b/APPLE_LICENSE new file mode 100644 index 0000000..84687a4 --- /dev/null +++ b/APPLE_LICENSE @@ -0,0 +1,372 @@ +APPLE PUBLIC SOURCE LICENSE +Version 1.1 - April 19,1999 + +Please read this License carefully before downloading this software. +By downloading and using this software, you are agreeing to be bound +by the terms of this License. If you do not or cannot agree to the +terms of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") publicly announces as +subject to this Apple Public Source License and which contains a +notice placed by Apple identifying such program or work as "Original +Code" and stating that it is subject to the terms of this Apple Public +Source License version 1.1 (or subsequent version thereof), as it may +be revised from time to time by Apple ("License"). As used in this +License: + +1.1 "Affected Original Code" means only those specific portions of +Original Code that allegedly infringe upon any party's intellectual +property rights or are otherwise the subject of a claim of +infringement. + +1.2 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Deploy" means to use, sublicense or distribute Covered Code other +than for Your internal research and development (R&D), and includes +without limitation, any and all internal use or distribution of +Covered Code within Your business or organization except for R&D use, +as well as direct or indirect sublicensing or distribution of Covered +Code by You to any third party in any form or manner. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of Covered Code. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non- exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 You may use, copy, modify and distribute Original Code, with or +without Modifications, solely for Your internal research and +development, provided that You must in each instance: + +(a) retain and reproduce in all copies of Original Code the copyright +and other proprietary notices and disclaimers of Apple as they appear +in the Original Code, and keep intact all notices in the Original Code +that refer to this License; + +(b) include a copy of this License with every copy of Source Code of +Covered Code and documentation You distribute, and You may not offer +or impose any terms on such Source Code that alter or restrict this +License or the recipients' rights hereunder, except as permitted under +Section 6; and + +(c) completely and accurately document all Modifications that you have +made and the date of each such Modification, designate the version of +the Original Code you used, prominently include a file carrying such +information with the Modifications, and duplicate the notice in +Exhibit A in each file of the Source Code of all such Modifications. + +2.2 You may Deploy Covered Code, provided that You must in each + instance: + +(a) satisfy all the conditions of Section 2.1 with respect to the +Source Code of the Covered Code; + +(b) make all Your Deployed Modifications publicly available in Source +Code form via electronic distribution (e.g. download from a web site) +under the terms of this License and subject to the license grants set +forth in Section 3 below, and any additional terms You may choose to +offer under Section 6. You must continue to make the Source Code of +Your Deployed Modifications available for as long as you Deploy the +Covered Code or twelve (12) months from the date of initial +Deployment, whichever is longer; + +(c) if You Deploy Covered Code containing Modifications made by You, +inform others of how to obtain those Modifications by filling out and +submitting the information found at +http://www.apple.com/publicsource/modifications.html, if available; +and + +(d) if You Deploy Covered Code in object code, executable form only, +include a prominent notice, in the code itself as well as in related +documentation, stating that Source Code of the Covered Code is +available under the terms of this License with information on how and +where to obtain such Source Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License: + +(a) You hereby grant to Apple and all third parties a non-exclusive, +royalty-free license, under Your Applicable Patent Rights and other +intellectual property rights owned or controlled by You, to use, +reproduce, modify, distribute and Deploy Your Modifications of the +same scope and extent as Apple's licenses under Sections 2.1 and 2.2; +and + +(b) You hereby grant to Apple and its subsidiaries a non-exclusive, +worldwide, royalty-free, perpetual and irrevocable license, under Your +Applicable Patent Rights and other intellectual property rights owned +or controlled by You, to use, reproduce, execute, compile, display, +perform, modify or have modified (for Apple and/or its subsidiaries), +sublicense and distribute Your Modifications, in any form, through +multiple tiers of distribution. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such +instance, You must make sure the requirements of this License are +fulfilled for the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require +additional patent licenses from Apple which Apple may grant in its +sole discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee +for, warranty, support, indemnity or liability obligations and/or +other rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered +Code. However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple. You must obtain the +recipient's agreement that any such Additional Terms are offered by +You alone, and You hereby agree to indemnify, defend and hold Apple +harmless for any liability incurred by or claims asserted against +Apple by reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be +given a distinguishing version number. Once Original Code has been +published under a particular version of this License, You may continue +to use it under the terms of that version. You may also choose to use +such Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Original Code may contain in whole or +in part pre-release, untested, or not fully tested works. The +Original Code may contain errors that could cause failures or loss of +data, and may be incomplete or contain inaccuracies. You expressly +acknowledge and agree that use of the Original Code, or any portion +thereof, is at Your sole and entire risk. THE ORIGINAL CODE IS +PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND +AND APPLE AND APPLE'S LICENSOR(S) (FOR THE PURPOSES OF SECTIONS 8 AND +9, APPLE AND APPLE'S LICENSOR(S) ARE COLLECTIVELY REFERRED TO AS +"APPLE") EXPRESSLY DISCLAIM ALL WARRANTIES AND/OR CONDITIONS, EXPRESS +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +AND/OR CONDITIONS OF MERCHANTABILITY OR SATISFACTORY QUALITY AND +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY +RIGHTS. APPLE DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE +ORIGINAL CODE WILL MEET YOUR REQUIREMENTS, OR THAT THE OPERATION OF +THE ORIGINAL CODE WILL BE UNINTERRUPTED OR ERROR- FREE, OR THAT +DEFECTS IN THE ORIGINAL CODE WILL BE CORRECTED. NO ORAL OR WRITTEN +INFORMATION OR ADVICE GIVEN BY APPLE OR AN APPLE AUTHORIZED +REPRESENTATIVE SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE +SCOPE OF THIS WARRANTY. You acknowledge that the Original Code is not +intended for use in the operation of nuclear facilities, aircraft +navigation, communication systems, or air traffic control machines in +which case the failure of the Original Code could lead to death, +personal injury, or severe physical or environmental damage. + +9. Liability. + +9.1 Infringement. If any portion of, or functionality implemented by, +the Original Code becomes the subject of a claim of infringement, +Apple may, at its option: (a) attempt to procure the rights necessary +for Apple and You to continue using the Affected Original Code; (b) +modify the Affected Original Code so that it is no longer infringing; +or (c) suspend Your rights to use, reproduce, modify, sublicense and +distribute the Affected Original Code until a final determination of +the claim is made by a court or governmental administrative agency of +competent jurisdiction and Apple lifts the suspension as set forth +below. Such suspension of rights will be effective immediately upon +Apple's posting of a notice to such effect on the Apple web site that +is used for implementation of this License. Upon such final +determination being made, if Apple is legally able, without the +payment of a fee or royalty, to resume use, reproduction, +modification, sublicensing and distribution of the Affected Original +Code, Apple will lift the suspension of rights to the Affected +Original Code by posting a notice to such effect on the Apple web site +that is used for implementation of this License. If Apple suspends +Your rights to Affected Original Code, nothing in this License shall +be construed to restrict You, at Your option and subject to applicable +law, from replacing the Affected Original Code with non-infringing +code or independently negotiating for necessary rights from such third +party. + +9.2 LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL APPLE BE +LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES +ARISING OUT OF OR RELATING TO THIS LICENSE OR YOUR USE OR INABILITY TO +USE THE ORIGINAL CODE, OR ANY PORTION THEREOF, WHETHER UNDER A THEORY +OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY +OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF +ANY REMEDY. In no event shall Apple's total liability to You for all +damages under this License exceed the amount of fifty dollars +($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac OS X", "Mac +OS X Server" or any other trademarks or trade names belonging to Apple +(collectively "Apple Marks") and no Apple Marks may be used to endorse +or promote products derived from the Original Code other than as +permitted by and in strict compliance at all times with Apple's third +party trademark usage guidelines which are posted at +http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Apple retains all rights, title and interest in and to +the Original Code and any Modifications made by or on behalf of Apple +("Apple Modifications"), and such Apple Modifications will not be +automatically subject to this License. Apple may, at its sole +discretion, choose to license such Apple Modifications under this +License, or on different terms from those contained in this License or +may choose not to license them at all. Apple's development, use, +reproduction, modification, sublicensing and distribution of Covered +Code will not be subject to this License. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will + terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; (b) immediately in the event of +the circumstances described in Section 13.5(b); or (c) automatically +without notice from Apple if You, at any time during the term of this +License, commence an action for patent infringement against Apple. + +12.2 Effect of Termination. Upon termination, You agree to +immediately stop any further use, reproduction, modification, +sublicensing and distribution of the Covered Code and to destroy all +copies of the Covered Code that are in your possession or control. +All sublicenses to the Covered Code which have been properly granted +prior to termination shall survive any termination of this License. +Provisions which, by their nature, should remain in effect beyond the +termination of this License shall survive, including but not limited +to Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. Neither party will be +liable to the other for compensation, indemnity or damages of any sort +solely as a result of terminating this License in accordance with its +terms, and termination of this License will be without prejudice to +any other right or remedy of either party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights +in the Covered Code include only those rights customarily provided to +the public as defined in this License. This customary commercial +license in technical data and software is provided in accordance with +FAR 12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between You and Apple, and You will not represent to +the contrary, whether expressly, by implication, appearance or +otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple to enforce any provision +of this License will not be deemed a waiver of future enforcement of +that or any other provision. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999 Apple Computer, Inc. All Rights +Reserved. This file contains Original Code and/or Modifications of +Original Code as defined in and that are subject to the Apple Public +Source License Version 1.1 (the "License"). You may not use this file +except in compliance with the License. Please obtain a copy of the +License at http://www.apple.com/publicsource and read it before using +this file. + +The Original Code and all software distributed under the License are +distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the +License for the specific language governing rights and limitations +under the License." diff --git a/Makefile b/Makefile index 977ac33..e7c89b0 100644 --- a/Makefile +++ b/Makefile @@ -1,44 +1,424 @@ +# use LDFLAGS not LFLAGS +# seg-addr-table, sect-order # -# Generated by the Apple Project Builder. +# Simple makefile for building objc4 on Darwin # -# NOTE: Do NOT change this file -- Project Builder maintains it. +# These make variables (or environment variables) are used +# if defined: +# SRCROOT path location of root of source hierarchy; +# defaults to ".", but must be set to a +# destination path for installsrc target. +# OBJROOT path location where .o files will be put; +# defaults to SRCROOT. +# SYMROOT path location where build products will be +# put; defaults to SRCROOT. +# DSTROOT path location where installed products will +# be put; defaults to / . +# OBJROOT and SYMROOT should not be directories shared with other +# built projects. +# PLATFORM name of platform being built on +# USER name of user building the project +# ARCHS list of archs for which to build +# RC_ARCHS more archs for which to build (build system) +# OTHER_CFLAGS other flags to be passed to compiler +# RC_CFLAGS more flags to be passed to compiler (build system) +# OTHER_LDFLAGS other flags to be passed to the link stage # -# Put all of your customizations in files called Makefile.preamble -# and Makefile.postamble (both optional), and Makefile will include them. + +# Default targets +default: build +all: build + +.SUFFIXES: +.PHONY: default all build optimized debug profile installsrc installhdrs install clean prebuild build-optimized build-debug build-profile prebuild-optimized prevuild-debug prebuild-profile compile-optimized compile-debug compile-profile link-optimized link-debug link-profile postbuild + +CURRENT_PROJECT_VERSION = 218 + +VERSION_NAME = A + +# First figure out the platform if not specified, so we can use it in the +# rest of this file. Currently defined values: Darwin +ifeq "$(PLATFORM)" "" +PLATFORM := $(shell uname) +endif + +ifndef SRCROOT +SRCROOT = . +endif + +ifndef OBJROOT +OBJROOT = $(SRCROOT) +endif + +ifndef SYMROOT +SYMROOT = $(SRCROOT) +endif + +ifndef DSTROOT +DSTROOT = / +endif + +ifeq "$(PLATFORM)" "Darwin" +CC = /usr/bin/cc +else +CC = /usr/bin/gcc +endif + +ECHO = @/bin/echo +MKDIRS = /bin/mkdir -p +CD = cd +COPY = /bin/cp +COPY_RECUR = /bin/cp -r +REMOVE = /bin/rm +REMOVE_RECUR = /bin/rm -rf +SYMLINK = /bin/ln -s +CHMOD = /bin/chmod +CHOWN = /usr/sbin/chown +TAR = /usr/bin/tar +STRIP = /usr/bin/strip +NMEDIT = /usr/bin/nmedit + +ifeq "$(PLATFORM)" "Darwin" +WARNING_FLAGS = -Wmost -Wno-precomp -Wno-four-char-constants +endif + +ARCH_LIST= +ifeq "$(PLATFORM)" "Darwin" + +ifneq "$(ARCHS)" "" +ARCH_LIST += $(ARCHS) +else +ifneq "$(RC_ARCHS)" "" +ARCH_LIST += $(RC_ARCHS) +else +ARCH_LIST += ppc +endif +endif + +ARCH_FLAGS = $(foreach A, $(ARCH_LIST), $(addprefix -arch , $(A))) + +endif + + + +ifeq "$(USER)" "" +USER = unknown +endif + +CFLAGS = -g -fno-common -pipe $(PLATFORM_CFLAGS) $(WARNING_FLAGS) -I$(SYMROOT) -I. -I$(SYMROOT)/ProjectHeaders +LDFLAGS = -framework CoreFoundation + +LIBRARY_EXT = .dylib + +PUBLIC_HEADER_INSTALLDIR = usr/include/objc +OTHER_HEADER_INSTALLDIR = usr/local/include/objc +INSTALLDIR = usr/lib + +ifeq "$(PLATFORM)" "Darwin" +CFLAGS += $(ARCH_FLAGS) +LDFLAGS += $(ARCH_FLAGS) -dynamiclib -dynamic -compatibility_version 1 -current_version $(CURRENT_PROJECT_VERSION) +endif + +CFLAGS += $(OTHER_CFLAGS) $(RC_CFLAGS) +LDFLAGS += $(OTHER_LDFLAGS) + +ifndef OPTIMIZATION_CFLAGS +OPTIMIZATION_CFLAGS = -Os +endif +ifndef DEBUG_CFLAGS +DEBUG_CFLAGS = -DDEBUG +endif +ifndef PROFILE_CFLAGS +PROFILE_CFLAGS = -DPROFILE -pg -Os +endif + +CFLAGS_OPTIMIZED = $(CFLAGS) $(OPTIMIZATION_CFLAGS) +CFLAGS_DEBUG = $(CFLAGS) $(DEBUG_CFLAGS) +CFLAGS_PROFILE = $(CFLAGS) $(PROFILE_CFLAGS) + +LDFLAGS_OPTIMIZED = $(LDFLAGS) +LDFLAGS_DEBUG = $(LDFLAGS) -g +LDFLAGS_PROFILE = $(LDFLAGS) -g -pg + +SUBDIRS = . runtime runtime/OldClasses.subproj runtime/Messengers.subproj + +# files to compile +SOURCES= +# files to not compile +OTHER_SOURCES= +# headers to install in /usr/include/objc +PUBLIC_HEADERS= +# headers that don't get installed +PRIVATE_HEADERS= +# headers to install in /usr/local/include/objc +OTHER_HEADERS= + +# runtime +SOURCES += $(addprefix runtime/, \ + Object.m Protocol.m hashtable2.m maptable.m objc-class.m objc-errors.m \ + objc-file.m objc-load.m objc-moninit.c objc-runtime.m objc-sel.m \ + ) +PUBLIC_HEADERS += $(addprefix runtime/, \ + objc-class.h objc-api.h objc-load.h objc-runtime.h objc.h Object.h \ + Protocol.h error.h hashtable2.h \ + ) +PRIVATE_HEADERS += runtime/objc-private.h runtime/objc-config.h +OTHER_HEADERS += runtime/maptable.h + +# OldClasses +SOURCES += runtime/OldClasses.subproj/List.m +PUBLIC_HEADERS += runtime/OldClasses.subproj/List.h + +# Messengers +SOURCES += runtime/Messengers.subproj/objc-msg.s +OTHER_SOURCES += runtime/Messengers.subproj/objc-msg-ppc.s runtime/Messengers.subproj/objc-msg-i386.s + +# project root +OTHER_SOURCES += Makefile APPLE_LICENSE objc-exports + +OBJECTS = $(addprefix $(OBJROOT)/, $(addsuffix .o, $(basename $(SOURCES) ) ) ) +OBJECTS_OPTIMIZED = $(OBJECTS:.o=.opt.o) +OBJECTS_DEBUG = $(OBJECTS:.o=.debug.o) +OBJECTS_PROFILE = $(OBJECTS:.o=.profile.o) + +# For simplicity, each object target depends on all objc headers. Most of +# them come close to requiring this anyway, and rebuild from scratch is fast. +DEPEND_HEADERS = $(addprefix $(SRCROOT)/, \ + $(PUBLIC_HEADERS) $(PRIVATE_HEADERS) $(OTHER_HEADERS) ) + +$(OBJROOT)/%.opt.o : $(SRCROOT)/%.m $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_OPTIMIZED) "$<" -c -o "$@" + +$(OBJROOT)/%.debug.o : $(SRCROOT)/%.m $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_DEBUG) "$<" -c -o "$@" + +$(OBJROOT)/%.profile.o : $(SRCROOT)/%.m $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_PROFILE) "$<" -c -o "$@" + +$(OBJROOT)/%.opt.o : $(SRCROOT)/%.c $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_OPTIMIZED) "$<" -c -o "$@" + +$(OBJROOT)/%.debug.o : $(SRCROOT)/%.c $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_DEBUG) "$<" -c -o "$@" + +$(OBJROOT)/%.profile.o : $(SRCROOT)/%.c $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_PROFILE) "$<" -c -o "$@" + +$(OBJROOT)/%.opt.o : $(SRCROOT)/%.s $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_OPTIMIZED) "$<" -c -o "$@" + +$(OBJROOT)/%.debug.o : $(SRCROOT)/%.s $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_DEBUG) "$<" -c -o "$@" + +$(OBJROOT)/%.profile.o : $(SRCROOT)/%.s $(DEPEND_HEADERS) + $(SILENT) $(ECHO) " ... $<" + $(SILENT) $(CC) $(CFLAGS_PROFILE) "$<" -c -o "$@" + +# Additional dependency: objc-msg.s depends on objc-msg-ppc.s and +# objc-msg-i386.s, which it includes. +$(OBJROOT)/runtime/Messengers.subproj/objc-msg.opt.o \ +$(OBJROOT)/runtime/Messengers.subproj/objc-msg.debug.o \ +$(OBJROOT)/runtime/Messengers.subproj/objc-msg.profile.o : \ + $(SRCROOT)/runtime/Messengers.subproj/objc-msg-ppc.s \ + $(SRCROOT)/runtime/Messengers.subproj/objc-msg-i386.s + + +# These are the main targets: +# build builds the library to OBJROOT and SYMROOT +# installsrc copies the sources to SRCROOT +# installhdrs install only the headers to DSTROOT +# install build, then install the headers and library to DSTROOT +# clean removes build products in OBJROOT and SYMROOT # +# optimized same as 'build' but builds optimized library only +# debug same as 'build' but builds debug library only +# profile same as 'build' but builds profile library only + +# Default build doesn't currently build the debug library. +build: prebuild build-optimized build-profile postbuild + +optimized: prebuild build-optimized postbuild +debug: prebuild build-debug postbuild +profile: prebuild build-profile postbuild + +installsrc: + $(SILENT) $(ECHO) "Installing source from . to $(SRCROOT)..." +ifeq "$(SRCROOT)" "." + $(SILENT) $(ECHO) "SRCROOT must be defined to be the destination directory; it cannot be '.'" + exit 1 +endif + $(SILENT) $(TAR) -cf $(SRCROOT)/objc4.sources.tar $(SOURCES) $(PUBLIC_HEADERS) $(PRIVATE_HEADERS) $(OTHER_HEADERS) $(OTHER_SOURCES) + $(SILENT) $(CD) $(SRCROOT) && $(TAR) -xf $(SRCROOT)/objc4.sources.tar + $(SILENT) $(REMOVE) -f $(SRCROOT)/objc4.sources.tar + +installhdrs: + $(SILENT) $(ECHO) "Installing headers from $(SRCROOT) to $(DSTROOT)/$(HEADER_INSTALLDIR)..." + + $(SILENT) $(MKDIRS) $(DSTROOT)/$(PUBLIC_HEADER_INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(PUBLIC_HEADER_INSTALLDIR)/*.h + $(SILENT) $(COPY) $(addprefix $(SRCROOT)/, $(PUBLIC_HEADERS) ) \ + $(DSTROOT)/$(PUBLIC_HEADER_INSTALLDIR) +# duplicate hashtable2.h to hashtable.h + $(SILENT) $(COPY) $(DSTROOT)/$(PUBLIC_HEADER_INSTALLDIR)/hashtable2.h \ + $(DSTROOT)/$(PUBLIC_HEADER_INSTALLDIR)/hashtable.h + $(SILENT) $(CHMOD) -w $(DSTROOT)/$(PUBLIC_HEADER_INSTALLDIR)/*.h + $(SILENT) $(CHMOD) a+r $(DSTROOT)/$(PUBLIC_HEADER_INSTALLDIR)/*.h + + $(SILENT) $(MKDIRS) $(DSTROOT)/$(OTHER_HEADER_INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(OTHER_HEADER_INSTALLDIR)/*.h + $(SILENT) $(COPY) $(addprefix $(SRCROOT)/, $(OTHER_HEADERS) ) \ + $(DSTROOT)/$(OTHER_HEADER_INSTALLDIR) + $(SILENT) $(CHMOD) -w $(DSTROOT)/$(OTHER_HEADER_INSTALLDIR)/*.h + $(SILENT) $(CHMOD) a+r $(DSTROOT)/$(OTHER_HEADER_INSTALLDIR)/*.h + + + $(SILENT) $(RM) -f $(DSTROOT)$(PUBLIC_HEADER_DIR)$(PUBLIC_HEADER_DIR_SUFFIX)/hashtable.h + + +install: build installhdrs + $(SILENT) $(ECHO) "Installing products from $(SYMROOT) to $(DSTROOT)..." + + $(SILENT) $(MKDIRS) $(DSTROOT)/$(INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(INSTALLDIR) + + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/libobjc.$(VERSION_NAME)$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) + +# optimized + $(SILENT) $(COPY) $(SYMROOT)/libobjc.$(VERSION_NAME)$(LIBRARY_EXT) $(DSTROOT)/$(INSTALLDIR) + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/libobjc.$(VERSION_NAME)$(LIBRARY_EXT) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/libobjc.$(VERSION_NAME)$(LIBRARY_EXT) + $(SILENT) $(CD) $(DSTROOT)/$(INSTALLDIR) && \ + $(SYMLINK) libobjc.$(VERSION_NAME)$(LIBRARY_EXT) libobjc$(LIBRARY_EXT) + +# debug (allowed not to exist) + -$(SILENT) $(COPY) $(SYMROOT)/libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) $(DSTROOT)/$(INSTALLDIR) + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) + -$(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) + -$(SILENT) $(CD) $(DSTROOT)/$(INSTALLDIR) && \ + test -e libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) && \ + $(SYMLINK) libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) libobjc_debug$(LIBRARY_EXT) && \ + $(SYMLINK) libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) libobjc.$(VERSION_NAME)_debug$(LIBRARY_EXT) + + +# profile (allowed not to exist) + -$(SILENT) $(COPY) $(SYMROOT)/libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) $(DSTROOT)/$(INSTALLDIR) + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) + -$(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) + -$(SILENT) $(CD) $(DSTROOT)/$(INSTALLDIR) && \ + test -e libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) && \ + $(SYMLINK) libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) libobjc_profile$(LIBRARY_EXT) && \ + $(SYMLINK) libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) libobjc.$(VERSION_NAME)_profile$(LIBRARY_EXT) + + +clean: + $(SILENT) $(ECHO) "Deleting build products..." + $(foreach A, $(ARCH_LIST), \ + $(SILENT) $(REMOVE) -f $(OBJROOT)/libobjc_debug.$A.o $(OBJROOT)/libobjc_profile.$A.o $(OBJROOT)/libobjc.$A.o ; ) + + $(SILENT) $(REMOVE) -f $(SYMROOT)/libobjc.optimized.o + $(SILENT) $(REMOVE) -f $(SYMROOT)/libobjc.debug.o + $(SILENT) $(REMOVE) -f $(SYMROOT)/libobjc.profile.o + + $(SILENT) $(REMOVE) -f $(SYMROOT)/libobjc.$(VERSION_NAME)$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(SYMROOT)/libobjc_debug.$(VERSION_NAME)$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(SYMROOT)/libobjc_profile.$(VERSION_NAME)$(LIBRARY_EXT) + + $(SILENT) $(REMOVE) -f $(OBJECTS_OPTIMIZED) + $(SILENT) $(REMOVE) -f $(OBJECTS_DEBUG) + $(SILENT) $(REMOVE) -f $(OBJECTS_PROFILE) + + $(SILENT) $(REMOVE) -rf $(SYMROOT)/ProjectHeaders + +prebuild: + $(SILENT) $(ECHO) "Prebuild-setup..." + +# Install headers into $(SYMROOT)/ProjectHeaders so #includes can find them +# even if they're not installed in /usr. + $(SILENT) $(MKDIRS) $(SYMROOT) + $(SILENT) $(REMOVE_RECUR) $(SYMROOT)/ProjectHeaders + $(SILENT) $(MKDIRS) $(SYMROOT)/ProjectHeaders + $(SILENT) $(ECHO) "Copying headers from $(SRCROOT) to $(SYMROOT)/ProjectHeaders..." + $(SILENT) $(COPY) $(addprefix $(SRCROOT)/, $(PRIVATE_HEADERS) ) $(SYMROOT)/ProjectHeaders + $(SILENT) $(MKDIRS) $(SYMROOT)/ProjectHeaders/objc + $(SILENT) $(COPY) $(addprefix $(SRCROOT)/, $(PUBLIC_HEADERS) ) $(SYMROOT)/ProjectHeaders/objc + $(SILENT) $(COPY) $(addprefix $(SRCROOT)/, $(OTHER_HEADERS) ) $(SYMROOT)/ProjectHeaders/objc + + + +build-optimized: prebuild-optimized compile-optimized link-optimized +build-debug: prebuild-debug compile-debug link-debug +build-profile: prebuild-profile compile-profile link-profile + + +prebuild-optimized: + $(SILENT) $(ECHO) "Building (optimized) ..." + $(SILENT) $(MKDIRS) $(foreach S, $(SUBDIRS), $(OBJROOT)/$(S) ) + +prebuild-debug: + $(SILENT) $(ECHO) "Building (debug) ..." + $(SILENT) $(MKDIRS) $(foreach S, $(SUBDIRS), $(OBJROOT)/$(S) ) + +prebuild-profile: + $(SILENT) $(ECHO) "Building (profile) ..." + $(SILENT) $(MKDIRS) $(foreach S, $(SUBDIRS), $(OBJROOT)/$(S) ) + -NAME = objc4 +compile-optimized: $(OBJECTS_OPTIMIZED) +compile-debug: $(OBJECTS_DEBUG) +compile-profile: $(OBJECTS_PROFILE) -PROJECTVERSION = 2.8 -PROJECT_TYPE = Aggregate -LIBRARIES = runtime +# link lib-suffix, LDFLAGS, OBJECTS +# libsuffix should be "" or _debug or _profile +ifeq "$(PLATFORM)" "Darwin" -OTHERSRCS = Makefile.preamble Makefile Makefile.postamble +define link + $(foreach A, $(ARCH_LIST), \ + $(SILENT) $(LD) -arch $A -r -o $(OBJROOT)/libobjc$1.$A.o $3 ; ) + $(foreach A, $(ARCH_LIST), \ + -$(SILENT) $(NMEDIT) -s $(SRCROOT)/objc-exports \ + $(OBJROOT)/libobjc$1.$A.o ; ) + $(SILENT) $(CC) $2 \ + -Wl,-init,__objcInit \ + -install_name /$(INSTALLDIR)/libobjc$1.$(VERSION_NAME)$(LIBRARY_EXT) \ + -o $(SYMROOT)/libobjc$1.$(VERSION_NAME)$(LIBRARY_EXT) \ + $(foreach A, $(ARCH_LIST), $(OBJROOT)/libobjc$1.$A.o ) +endef -MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles -CODE_GEN_STYLE = DYNAMIC -MAKEFILE = aggregate.make -LIBS = -DEBUG_LIBS = $(LIBS) -PROF_LIBS = $(LIBS) +else +# PLATFORM != Darwin +define link + $(SILENT) $(ECHO) "Don't know how to link for platform '$(PLATFORM)'" +endef +endif +link-optimized: + $(SILENT) $(ECHO) "Linking (optimized)..." + $(call link,,$(LDFLAGS_OPTIMIZED),$(OBJECTS_OPTIMIZED) ) + $(SILENT) $(STRIP) -x $(SYMROOT)/libobjc.$(VERSION_NAME)$(LIBRARY_EXT) -NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc -WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc -PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc -NEXTSTEP_JAVA_COMPILER = /usr/bin/javac -WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe -PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac +link-debug: + $(SILENT) $(ECHO) "Linking (debug)..." + $(call link,_debug,$(LDFLAGS_DEBUG),$(OBJECTS_DEBUG) ) -include $(MAKEFILEDIR)/platform.make +link-profile: + $(SILENT) $(ECHO) "Linking (profile)..." + $(call link,_profile,$(LDFLAGS_PROFILE),$(OBJECTS_PROFILE)) --include Makefile.preamble -include $(MAKEFILEDIR)/$(MAKEFILE) +postbuild: + $(SILENT) $(ECHO) "Done!" --include Makefile.postamble --include Makefile.dependencies diff --git a/Makefile.postamble b/Makefile.postamble deleted file mode 100644 index e69de29..0000000 diff --git a/Makefile.preamble b/Makefile.preamble deleted file mode 100644 index e41455c..0000000 --- a/Makefile.preamble +++ /dev/null @@ -1,5 +0,0 @@ - -ifeq "$(PLATFORM_OS)" "macos" - BEFORE_INSTALL += profile -endif - diff --git a/PB.project b/PB.project deleted file mode 100644 index a8d3155..0000000 --- a/PB.project +++ /dev/null @@ -1,24 +0,0 @@ -{ - DYNAMIC_CODE_GEN = YES; - FILESTABLE = { - CLASSES = (); - H_FILES = (); - OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble); - SUBPROJECTS = (runtime); - }; - LANGUAGE = English; - LOCALIZABLE_FILES = {}; - MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; - NEXTSTEP_BUILDTOOL = /bin/gnumake; - NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; - NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; - PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; - PDO_UNIX_JAVA_COMPILER = "$(JDKBINDIR)/javac"; - PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; - PROJECTNAME = objc4; - PROJECTTYPE = Aggregate; - PROJECTVERSION = 2.8; - WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; - WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; - WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; -} diff --git a/objc-exports b/objc-exports new file mode 100644 index 0000000..df930c8 --- /dev/null +++ b/objc-exports @@ -0,0 +1,144 @@ +# Functions and variables explicitly exported from ObjC. +# GrP 2002-2-4 +# Note that some commonly used functions are *not* listed in the +# ObjC headers (e.g. objc_flush_caches()) +# List.h +.objc_class_name_List +# objc-class.h +_object_setInstanceVariable +_object_getInstanceVariable +_class_createInstance +_class_createInstanceFromZone +_class_setVersion +_class_getVersion +_class_getInstanceVariable +_class_getInstanceMethod +_class_getClassMethod +_class_addMethods +_class_removeMethods +_class_poseAs +_method_getNumberOfArguments +_method_getSizeOfArguments +_method_getArgumentInfo +_class_nextMethodList +# objc-load.h +_objc_loadModules +_objc_loadModule +_objc_unloadModules +# objc-runtime.h +# fixme does anybody use objc_msgSendFew* ? +_objc_getClass +_objc_getMetaClass +_objc_msgSend +_objc_msgSend_stret +_objc_msgSendSuper +_objc_msgSendSuper_stret +_objc_msgSendv +_objc_msgSendv_stret +_objc_getClassList +_objc_getClasses +_objc_lookUpClass +_objc_addClass +_objc_setClassHandler +_objc_setMultithreaded +__alloc +__copy +__realloc +__dealloc +__zoneAlloc +__zoneRealloc +__zoneCopy +__error +# objc.h +_sel_isMapped +_sel_getName +_sel_getUid +_sel_registerName +_object_getClassName +_object_getIndexedIvars +# Object.h +.objc_class_name_Object +_object_dispose +_object_copy +_object_copyFromZone +_object_realloc +_object_reallocFromZone +# Protocol.h +.objc_class_name_Protocol +# error.h +# everything inside is declared but no longer defined?! +# hashtable2.h +_NXCreateHashTableFromZone +_NXCreateHashTable +_NXFreeHashTable +_NXEmptyHashTable +_NXResetHashTable +_NXCompareHashTables +_NXCopyHashTable +_NXCountHashTable +_NXHashMember +_NXHashGet +_NXHashInsert +_NXHashInsertIfAbsent +_NXHashRemove +_NXInitHashState +_NXNextHashState +_NXPtrHash +_NXStrHash +_NXPtrIsEqual +_NXStrIsEqual +_NXNoEffectFree +_NXReallyFree +_NXPtrPrototype +_NXStrPrototype +_NXPtrStructKeyPrototype +_NXStrStructKeyPrototype +_NXUniqueString +_NXUniqueStringWithLength +_NXUniqueStringNoCopy +_NXCopyStringBuffer +_NXCopyStringBufferFromZone +# maptable.h +_NXCreateMapTableFromZone +_NXCreateMapTable +_NXFreeMapTable +_NXResetMapTable +_NXCompareMapTables +_NXCountMapTable +_NXMapMember +_NXMapGet +_NXMapInsert +_NXMapRemove +_NXInitMapState +_NXNextMapState +_NXPtrValueMapPrototype +_NXStrValueMapPrototype +_NXObjectMapPrototype +# +# Functions that aren't in the headers but are used or are useful. +# +# sudo find / -xdev -type f -perm -0111 \! -name "libobjc*dylib" -print -exec nm -u {} \; > /tmp/all-used-symbols +# (repeat with any other disks you want checked, appending to the same file) +# nm /usr/lib/libobjc.dylib | awk '$2 ~ /^[ADST]$/' | colrm 1 11 | sort -u > /tmp/objc-exports +# (note that you need an unstripped, un-nmedited libobjc.dylib) +# grep -f /tmp/objc-exports /tmp/all-used-symbols | sort -u > /tmp/used-objc-symbols +# grep -v -f /tmp/used-objc-symbols /tmp/objc-exports | sort -u > /tmp/unused-objc-symbols +# +__class_printDuplicateCacheEntries +__class_printMethodCaches +__class_printMethodCacheStatistics +__objc_create_zone +__objc_error +__objc_flush_caches +__objc_msgForward +__objcInit +_class_lookupMethod +_class_respondsToMethod +_instrumentObjcMessageSends +_objc_getOrigClass +# magic, or garbage? +__dummy +_do_not_remove_this_dummy_function +# used by debugging tools like heap +__objc_debug_class_hash + diff --git a/runtime/Makefile b/runtime/Makefile deleted file mode 100644 index ec9acaa..0000000 --- a/runtime/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -# -# Generated by the Apple Project Builder. -# -# NOTE: Do NOT change this file -- Project Builder maintains it. -# -# Put all of your customizations in files called Makefile.preamble -# and Makefile.postamble (both optional), and Makefile will include them. -# - -NAME = runtime - -PROJECTVERSION = 2.8 -PROJECT_TYPE = Library - -HFILES = error.h hashtable2.h maptable.h objc-api.h objc-class.h\ - objc-config.h objc-load.h objc-private.h objc-runtime.h\ - objc.h Object.h Protocol.h - -MFILES = hashtable2.m maptable.m objc-class.m objc-errors.m\ - objc-file.m objc-load.m objc-runtime.m objc-sel.m Object.m\ - Protocol.m - -CFILES = objc-moninit.c - -SUBPROJECTS = Messengers.subproj OldClasses.subproj - -OTHERSRCS = Makefile.preamble Makefile Makefile.postamble - - -MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles -CURRENTLY_ACTIVE_VERSION = YES -DEPLOY_WITH_VERSION_NAME = A -CODE_GEN_STYLE = DYNAMIC -MAKEFILE = library.make -NEXTSTEP_INSTALLDIR = /usr/lib -WINDOWS_INSTALLDIR = /. -PDO_UNIX_INSTALLDIR = $(LOCAL_DEVELOPER_DIR)/Libraries -LIBS = -DEBUG_LIBS = $(LIBS) -PROF_LIBS = $(LIBS) - - -PUBLIC_HEADERS = objc-class.h objc-api.h objc-load.h objc-runtime.h\ - objc.h Object.h Protocol.h error.h hashtable2.h - -PROJECT_HEADERS = objc-runtime.h objc-class.h - - - -NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc -WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc -PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc -NEXTSTEP_JAVA_COMPILER = /usr/bin/javac -WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe -PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac - -include $(MAKEFILEDIR)/platform.make - --include Makefile.preamble - -include $(MAKEFILEDIR)/$(MAKEFILE) - --include Makefile.postamble - --include Makefile.dependencies diff --git a/runtime/Makefile.postamble b/runtime/Makefile.postamble deleted file mode 100644 index 193b70c..0000000 --- a/runtime/Makefile.postamble +++ /dev/null @@ -1,27 +0,0 @@ -############################################################################### -# Makefile.postamble -# Copyright 1997,2000 Apple Computer, Inc. -############################################################################### - -ifeq "$(PLATFORM_OS)" "macos" - -PROFILE_PRODUCT = $(PRODUCT_DIR)/$(LIBRARY_PREFIX)$(NAME)$(PROFILE_SUFFIX)$(LIBRARY_EXT) -VERSIONED_PROFILE_PRODUCT = $(PRODUCT_DIR)/$(LIBRARY_PREFIX)$(NAME)$(PROFILE_SUFFIX).$(VERSION_NAME)$(LIBRARY_EXT) -PRODUCTS += $(PROFILE_PRODUCT) $(VERSIONED_PROFILE_PRODUCT) -STRIPPED_PRODUCTS += $(VERSIONED_PROFILE_PRODUCT) -DYLIB_INSTALL_NAME = $(LIBRARY_PREFIX)$(NAME)$(BUILD_TYPE_SUFFIX).$(VERSION_NAME)$(LIBRARY_EXT) -PRODUCT = $(PRODUCT_DIR)/$(DYLIB_INSTALL_NAME) - -endif - -create-profile-lib-compat-link: - $(SYMLINK) $(notdir $(VERSIONED_PROFILE_PRODUCT)) $(DSTROOT)$(INSTALLDIR)/libobjc.A_profile.dylib - -link-hashtable: - $(RM) -f $(DSTROOT)$(PUBLIC_HEADER_DIR)$(PUBLIC_HEADER_DIR_SUFFIX)/hashtable.h - $(CP) $(DSTROOT)$(PUBLIC_HEADER_DIR)$(PUBLIC_HEADER_DIR_SUFFIX)/hashtable2.h $(DSTROOT)$(PUBLIC_HEADER_DIR)$(PUBLIC_HEADER_DIR_SUFFIX)/hashtable.h - true - -# from AFTER_POSTINSTALL -postprocess: - diff --git a/runtime/Makefile.preamble b/runtime/Makefile.preamble deleted file mode 100644 index 83a3305..0000000 --- a/runtime/Makefile.preamble +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################### -# Makefile.preamble -# Copyright 1997,2000 Apple Computer, Inc. -############################################################################### - - -NAME = objc -OTHER_CFLAGS += -Wno-unused -OTHER_LIBTOOL_FLAGS += -Wl,-init,__objcInit -FRAMEWORKS += -framework CoreFoundation -HEADER_PATHS += -I$(NEXT_ROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/System.framework/PrivateHeaders -AFTER_INSTALL += create-profile-lib-compat-link - -OTHER_CFLAGS += -DNSBUILDINGOBJC -I$(SYMROOT) -OTHER_LDFLAGS = - -AFTER_INSTALLHDRS += link-hashtable -AFTER_POSTINSTALL += postprocess - -PUBLIC_HEADER_DIR = /usr/include -PRIVATE_HEADER_DIR = /usr/local/include - -# If, in a subproject, you want to append to the parent's PUBLIC_HEADER_DIR# -# (say, to add a subdirectory like "/sys"), you can use: -PUBLIC_HEADER_DIR_SUFFIX = /objc -PRIVATE_HEADER_DIR_SUFFIX = /objc - -OTHER_PUBLIC_HEADERS = -OTHER_PRIVATE_HEADERS = maptable.h -OTHER_PROJECT_HEADERS = $(PUBLIC_HEADERS) $(OTHER_PRIVATE_HEADERS) objc-private.h objc-config.h - diff --git a/runtime/Messengers.subproj/Makefile b/runtime/Messengers.subproj/Makefile deleted file mode 100644 index df1daed..0000000 --- a/runtime/Messengers.subproj/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# Generated by the Apple Project Builder. -# -# NOTE: Do NOT change this file -- Project Builder maintains it. -# -# Put all of your customizations in files called Makefile.preamble -# and Makefile.postamble (both optional), and Makefile will include them. -# - -NAME = Messengers - -PROJECTVERSION = 2.8 -PROJECT_TYPE = Component - -OTHERLINKED = objc-msg.s - -OTHERSRCS = Makefile.preamble Makefile Makefile.postamble\ - objc-msg-i386.s objc-msg-ppc.s - -OTHERLINKEDOFILES = objc-msg.o - -MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles -CODE_GEN_STYLE = DYNAMIC -MAKEFILE = subproj.make -LIBS = -DEBUG_LIBS = $(LIBS) -PROF_LIBS = $(LIBS) - - - - -NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc -WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc -PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc -NEXTSTEP_JAVA_COMPILER = /usr/bin/javac -WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe -PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac - -include $(MAKEFILEDIR)/platform.make - --include Makefile.preamble - -include $(MAKEFILEDIR)/$(MAKEFILE) - --include Makefile.postamble - --include Makefile.dependencies diff --git a/runtime/Messengers.subproj/Makefile.postamble b/runtime/Messengers.subproj/Makefile.postamble deleted file mode 100644 index db6b4a2..0000000 --- a/runtime/Messengers.subproj/Makefile.postamble +++ /dev/null @@ -1,100 +0,0 @@ -############################################################################### -# Makefile.postamble -# Copyright 1997, Apple Computer, Inc. -# -# Use this makefile, which is imported after all other makefiles, to -# override attributes for a project's Makefile environment. This allows you -# to take advantage of the environment set up by the other Makefiles. -# You can also define custom rules at the end of this file. -# -############################################################################### -# -# These variables are exported by the standard makefiles and can be -# used in any customizations you make. They are *outputs* of -# the Makefiles and should be used, not set. -# -# PRODUCTS: products to install. All of these products will be placed in -# the directory $(DSTROOT)$(INSTALLDIR) -# GLOBAL_RESOURCE_DIR: The directory to which resources are copied. -# LOCAL_RESOURCE_DIR: The directory to which localized resources are copied. -# OFILE_DIR: Directory into which .o object files are generated. -# DERIVED_SRC_DIR: Directory used for all other derived files -# -# ALL_CFLAGS: flags to pass when compiling .c files -# ALL_MFLAGS: flags to pass when compiling .m files -# ALL_CCFLAGS: flags to pass when compiling .cc, .cxx, and .C files -# ALL_MMFLAGS: flags to pass when compiling .mm, .mxx, and .M files -# ALL_PRECOMPFLAGS: flags to pass when precompiling .h files -# ALL_LDFLAGS: flags to pass when linking object files -# ALL_LIBTOOL_FLAGS: flags to pass when libtooling object files -# ALL_PSWFLAGS: flags to pass when processing .psw and .pswm (pswrap) files -# ALL_RPCFLAGS: flags to pass when processing .rpc (rpcgen) files -# ALL_YFLAGS: flags to pass when processing .y (yacc) files -# ALL_LFLAGS: flags to pass when processing .l (lex) files -# -# NAME: name of application, bundle, subproject, palette, etc. -# LANGUAGE: langage in which the project is written (default "English") -# LOCAL_RESOURCES: localized resources (e.g. nib's, images) of project -# GLOBAL_RESOURCES: non-localized resources of project -# -# SRCROOT: base directory in which to place the new source files -# SRCPATH: relative path from SRCROOT to present subdirectory -# -# INSTALLDIR: Directory the product will be installed into by 'install' target -# PUBLIC_HDR_INSTALLDIR: where to install public headers. Don't forget -# to prefix this with DSTROOT when you use it. -# PRIVATE_HDR_INSTALLDIR: where to install private headers. Don't forget -# to prefix this with DSTROOT when you use it. -# -# EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows) -# -############################################################################### - -# Some compiler flags can be overridden here for certain build situations. -# -# WARNING_CFLAGS: flag used to set warning level (defaults to -Wmost) -# DEBUG_SYMBOLS_CFLAGS: debug-symbol flag passed to all builds (defaults -# to -g) -# DEBUG_BUILD_CFLAGS: flags passed during debug builds (defaults to -DDEBUG) -# OPTIMIZE_BUILD_CFLAGS: flags passed during optimized builds (defaults -# to -O) -# PROFILE_BUILD_CFLAGS: flags passed during profile builds (defaults -# to -pg -DPROFILE) -# LOCAL_DIR_INCLUDE_DIRECTIVE: flag used to add current directory to -# the include path (defaults to -I.) -# DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags -# passed to ld/libtool (defaults to nothing) - - -# Library and Framework projects only: -# INSTALL_NAME_DIRECTIVE: This directive ensures that executables linked -# against the framework will run against the correct version even if -# the current version of the framework changes. You may override this -# to "" as an alternative to using the DYLD_LIBRARY_PATH during your -# development cycle, but be sure to restore it before installing. - - -# Ownership and permissions of files installed by 'install' target - -#INSTALL_AS_USER = root - # User/group ownership -#INSTALL_AS_GROUP = wheel - # (probably want to set both of these) -#INSTALL_PERMISSIONS = - # If set, 'install' chmod's executable to this - - -# Options to strip. Note: -S strips debugging symbols (executables can be stripped -# down further with -x or, if they load no bundles, with no options at all). - -#STRIPFLAGS = -S - - -######################################################################### -# Put rules to extend the behavior of the standard Makefiles here. Include them in -# the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble. -# -# You should avoid redefining things like "install" or "app", as they are -# owned by the top-level Makefile API and no context has been set up for where -# derived files should go. -# diff --git a/runtime/Messengers.subproj/Makefile.preamble b/runtime/Messengers.subproj/Makefile.preamble deleted file mode 100644 index 83f25c7..0000000 --- a/runtime/Messengers.subproj/Makefile.preamble +++ /dev/null @@ -1,123 +0,0 @@ -############################################################################### -# Makefile.preamble -# Copyright 1997, Apple Computer, Inc. -# -# Use this makefile for configuring the standard application makefiles -# associated with ProjectBuilder. It is included before the main makefile. -# In Makefile.preamble you set attributes for a project, so they are available -# to the project's makefiles. In contrast, you typically write additional rules or -# override built-in behavior in the Makefile.postamble. -# -# Each directory in a project tree (main project plus subprojects) should -# have its own Makefile.preamble and Makefile.postamble. -############################################################################### -# -# Before the main makefile is included for this project, you may set: -# -# MAKEFILEDIR: Directory in which to find $(MAKEFILE) -# MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make) - -# Compiler/linker flags added to the defaults: The OTHER_* variables will be -# inherited by all nested sub-projects, but the LOCAL_ versions of the same -# variables will not. Put your -I, -D, -U, and -L flags in ProjectBuilder's -# Build Attributes inspector if at all possible. To override the default flags -# that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble. The -# variables below are *inputs* to the build process and distinct from the override -# settings done (less often) in the Makefile.postamble. -# -# OTHER_CFLAGS, LOCAL_CFLAGS: additional flags to pass to the compiler -# Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m, -# .cc, .cxx, .C, and .M files. There is no need to respecify the -# flags in OTHER_MFLAGS, etc. -# OTHER_MFLAGS, LOCAL_MFLAGS: additional flags for .m files -# OTHER_CCFLAGS, LOCAL_CCFLAGS: additional flags for .cc, .cxx, and ...C files -# OTHER_MMFLAGS, LOCAL_MMFLAGS: additional flags for .mm and .M files -# OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS: additional flags used when -# precompiling header files -# OTHER_LDFLAGS, LOCAL_LDFLAGS: additional flags passed to ld and libtool -# OTHER_PSWFLAGS, LOCAL_PSWFLAGS: additional flags passed to pswrap -# OTHER_RPCFLAGS, LOCAL_RPCFLAGS: additional flags passed to rpcgen -# OTHER_YFLAGS, LOCAL_YFLAGS: additional flags passed to yacc -# OTHER_LFLAGS, LOCAL_LFLAGS: additional flags passed to lex - -# These variables provide hooks enabling you to add behavior at almost every -# stage of the make: -# -# BEFORE_PREBUILD: targets to build before installing headers for a subproject -# AFTER_PREBUILD: targets to build after installing headers for a subproject -# BEFORE_BUILD_RECURSION: targets to make before building subprojects -# BEFORE_BUILD: targets to make before a build, but after subprojects -# AFTER_BUILD: targets to make after a build -# -# BEFORE_INSTALL: targets to build before installing the product -# AFTER_INSTALL: targets to build after installing the product -# BEFORE_POSTINSTALL: targets to build before postinstalling every subproject -# AFTER_POSTINSTALL: targts to build after postinstalling every subproject -# -# BEFORE_INSTALLHDRS: targets to build before installing headers for a -# subproject -# AFTER_INSTALLHDRS: targets to build after installing headers for a subproject -# BEFORE_INSTALLSRC: targets to build before installing source for a subproject -# AFTER_INSTALLSRC: targets to build after installing source for a subproject -# -# BEFORE_DEPEND: targets to build before building dependencies for a -# subproject -# AFTER_DEPEND: targets to build after building dependencies for a -# subproject -# -# AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is -# updated every time the project is built. If NO, the dependency -# file is only built when the depend target is invoked. - -# Framework-related variables: -# FRAMEWORK_DLL_INSTALLDIR: On Windows platforms, this variable indicates -# where to put the framework's DLL. This variable defaults to -# $(INSTALLDIR)/../Executables - -# Library-related variables: -# PUBLIC_HEADER_DIR: Determines where public exported header files -# should be installed. Do not include $(DSTROOT) in this value -- -# it is prefixed automatically. -# PRIVATE_HEADER_DIR: Determines where private exported header files -# should be installed. Do not include $(DSTROOT) in this value -- -# it is prefixed automatically. -# LIBRARY_STYLE: This may be either STATIC or DYNAMIC, and determines -# whether the libraries produced are statically linked when they -# are used or if they are dynamically loadable. <> -# LIBRARY_DLL_INSTALLDIR: On Windows platforms, this variable indicates -# where to put the library's DLL. This variable defaults to -# $(INSTALLDIR)/../Executables -# -# INSTALL_AS_USER: owner of the intalled products (default root) -# INSTALL_AS_GROUP: group of the installed products (default wheel) -# INSTALL_PERMISSION: permissions of the installed product (default o+rX) -# -# OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be -# passed on the command line to recursive invocations of make. Note that -# the values in OTHER_*FLAGS are inherited by subprojects automatically -- -# you do not have to (and shouldn't) add OTHER_*FLAGS to -# OTHER_RECURSIVE_VARIABLES. - -# Additional headers to export beyond those in the PB.project: -# OTHER_PUBLIC_HEADERS -# OTHER_PROJECT_HEADERS -# OTHER_PRIVATE_HEADERS - -# Additional files for the project's product: <> -# OTHER_RESOURCES: (non-localized) resources for this project -# OTHER_OFILES: relocatables to be linked into this project -# OTHER_LIBS: more libraries to link against -# OTHER_PRODUCT_DEPENDS: other dependencies of this project -# OTHER_SOURCEFILES: other source files maintained by .pre/postamble -# OTHER_GARBAGE: additional files to be removed by `make clean' - -# Set this to YES if you don't want a final libtool call for a library/framework. -# BUILD_OFILES_LIST_ONLY - -# To include a version string, project source must exist in a directory named -# $(NAME).%d[.%d][.%d] and the following line must be uncommented. -# OTHER_GENERATED_OFILES = $(VERS_OFILE) - -# This definition will suppress stripping of debug symbols when an executable -# is installed. By default it is YES. -# STRIP_ON_INSTALL = NO diff --git a/runtime/Messengers.subproj/PB.project b/runtime/Messengers.subproj/PB.project deleted file mode 100644 index d82efb2..0000000 --- a/runtime/Messengers.subproj/PB.project +++ /dev/null @@ -1,31 +0,0 @@ -{ - DYNAMIC_CODE_GEN = YES; - FILESTABLE = { - CLASSES = (); - H_FILES = (); - OTHER_LINKED = ("objc-msg.s"); - OTHER_SOURCES = ( - Makefile.preamble, - Makefile, - Makefile.postamble, - "objc-msg-i386.s", - "objc-msg-ppc.s" - ); - SUBPROJECTS = (); - }; - LANGUAGE = English; - LOCALIZABLE_FILES = {}; - MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; - NEXTSTEP_BUILDTOOL = /bin/gnumake; - NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; - NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; - PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; - PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; - PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; - PROJECTNAME = Messengers; - PROJECTTYPE = Component; - PROJECTVERSION = 2.8; - WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; - WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; - WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; -} diff --git a/runtime/Messengers.subproj/objc-msg-i386.s b/runtime/Messengers.subproj/objc-msg-i386.s index 6bd3565..efd482d 100644 --- a/runtime/Messengers.subproj/objc-msg-i386.s +++ b/runtime/Messengers.subproj/objc-msg-i386.s @@ -39,7 +39,6 @@ kEight = 8 #endif -#if defined(OBJC_COLLECTING_CACHE) // _objc_entryPoints and _objc_exitPoints are used by objc // to get the critical regions for which method caches // cannot be garbage collected. @@ -47,6 +46,8 @@ .data .globl _objc_entryPoints _objc_entryPoints: + .long __cache_getImp + .long __cache_getMethod .long _objc_msgSend .long _objc_msgSend_stret .long _objc_msgSendSuper @@ -55,12 +56,13 @@ _objc_entryPoints: .globl _objc_exitPoints _objc_exitPoints: + .long LGetImpExit + .long LGetMethodExit .long LMsgSendExit .long LMsgSendStretExit .long LMsgSendSuperExit .long LMsgSendSuperStretExit .long 0 -#endif /******************************************************************** @@ -271,18 +273,20 @@ $0: ///////////////////////////////////////////////////////////////////// // // -// CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER, cacheMissLabel +// CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER | CACHE_GET, cacheMissLabel // // Locate the implementation for a selector in a class method cache. // // Takes: WORD_RETURN (first parameter is at sp+4) // STRUCT_RETURN (struct address is at sp+4, first parameter at sp+8) -// MSG_SEND (first parameter is receiver) +// MSG_SEND (first parameter is receiver) // MSG_SENDSUPER (first parameter is address of objc_super structure) +// CACHE_GET (first parameter is class; return method triplet) // // cacheMissLabel = label to branch to iff method is not cached // -// On exit: (found) imp in eax register +// On exit: (found) MSG_SEND and MSG_SENDSUPER: return imp in eax +// (found) CACHE_GET: return method triplet in eax // (not found) jumps to cacheMissLabel // ///////////////////////////////////////////////////////////////////// @@ -295,8 +299,9 @@ STRUCT_RETURN = 1 // Values to specify to method lookup macros whether the first argument // is an object/class reference or a 'objc_super' structure. -MSG_SEND = 0 -MSG_SENDSUPER = 1 +MSG_SEND = 0 // first argument is receiver, search the isa +MSG_SENDSUPER = 1 // first argument is objc_super, search the class +CACHE_GET = 2 // first argument is class, search that class .macro CacheLookup @@ -306,19 +311,23 @@ MSG_SENDSUPER = 1 .if $1 == MSG_SEND // MSG_SEND movl isa(%eax), %eax // class = self->isa movl selector(%esp), %ecx // get selector -.else // MSG_SENDSUPER +.elseif $1 == MSG_SENDSUPER // MSG_SENDSUPER movl super(%esp), %eax // get objc_super address movl class(%eax), %eax // class = caller->class movl selector(%esp), %ecx // get selector +.else // CACHE_GET + movl selector(%esp), %ecx // get selector - class already in eax .endif .else // Struct return .if $1 == MSG_SEND // MSG_SEND (stret) movl isa(%eax), %eax // class = self->isa movl (selector_stret)(%esp), %ecx // get selector -.else // MSG_SENDSUPER (stret) +.elseif $1 == MSG_SENDSUPER // MSG_SENDSUPER (stret) movl super_stret(%esp), %eax // get objc_super address movl class(%eax), %eax // class = caller->class movl (selector_stret)(%esp), %ecx // get selector +.else // CACHE_GET + !! This should not happen. .endif .endif @@ -334,9 +343,14 @@ MSG_SENDSUPER = 1 leal buckets(%eax), %edi // buckets = &cache->buckets movl mask(%eax), %esi // mask = cache->mask movl %ecx, %edx // index = selector +#ifdef NO_MACRO_CONSTS + shrl $kTwo, %edx // index = selector >> 2 +#else + shrl $2, %edx // index = selector >> 2 +#endif // search the receiver's cache -LMsgSendProbeCache_$0_$1: +LMsgSendProbeCache_$0_$1_$2: #if defined(OBJC_INSTRUMENTED) inc %ebx // probeCount += 1 #endif @@ -344,19 +358,19 @@ LMsgSendProbeCache_$0_$1: movl (%edi, %edx, 4), %eax // method = buckets[index] testl %eax, %eax // check for end of bucket - je LMsgSendCacheMiss_$0_$1 // go to cache miss code + je LMsgSendCacheMiss_$0_$1_$2 // go to cache miss code cmpl method_name(%eax), %ecx // check for method name match - je LMsgSendCacheHit_$0_$1 // go handle cache hit + je LMsgSendCacheHit_$0_$1_$2 // go handle cache hit inc %edx // bump index ... - jmp LMsgSendProbeCache_$0_$1// ... and loop + jmp LMsgSendProbeCache_$0_$1_$2 // ... and loop // not found in cache: restore state and go to callers handler -LMsgSendCacheMiss_$0_$1: +LMsgSendCacheMiss_$0_$1_$2: #if defined(OBJC_INSTRUMENTED) popl %edx // retrieve cache pointer movl mask(%edx), %esi // mask = cache->mask testl %esi, %esi // a mask of zero is only for the... - je LMsgSendMissInstrumentDone_$0 // ... emptyCache, do not record anything + je LMsgSendMissInstrumentDone_$0_$1_$2 // ... emptyCache, do not record anything // locate and update the CacheInstrumentation structure inc %esi // entryCount = mask + 1 @@ -376,15 +390,15 @@ LMsgSendCacheMiss_$0_$1: movl %edi, missProbes(%esi) // cacheData->missProbes += probeCount movl maxMissProbes(%esi), %edi// if (cacheData->maxMissProbes < probeCount) cmpl %ebx, %edi // - jge LMsgSendMaxMissProbeOK_$0 // + jge LMsgSendMaxMissProbeOK_$0_$1_$2 // movl %ebx, maxMissProbes(%esi)// cacheData->maxMissProbes = probeCount -LMsgSendMaxMissProbeOK_$0: +LMsgSendMaxMissProbeOK_$0_$1_$2: // update cache miss probe histogram cmpl $CACHE_HISTOGRAM_SIZE, %ebx // pin probeCount to max index - jl LMsgSendMissHistoIndexSet_$0 + jl LMsgSendMissHistoIndexSet_$0_$1_$2 movl $(CACHE_HISTOGRAM_SIZE-1), %ebx -LMsgSendMissHistoIndexSet_$0: +LMsgSendMissHistoIndexSet_$0_$1_$2: LEA_STATIC_DATA %esi, _CacheMissHistogram, EXTERNAL_SYMBOL #ifdef NO_MACRO_CONSTS shll $kTwo, %ebx // convert probeCount to histogram index @@ -395,7 +409,7 @@ LMsgSendMissHistoIndexSet_$0: movl 0(%esi), %edi // get current tally inc %edi // movl %edi, 0(%esi) // tally += 1 -LMsgSendMissInstrumentDone_$0: +LMsgSendMissInstrumentDone_$0_$1_$2: popl %ebx // restore non-volatile register #endif @@ -403,9 +417,9 @@ LMsgSendMissInstrumentDone_$0: .if $1 == MSG_SEND // MSG_SEND popl %esi // restore callers register popl %edi // restore callers register - movl self(%esp), %eax // get messaged object + movl self(%esp), %eax // get messaged object movl isa(%eax), %eax // get objects class -.else // MSG_SENDSUPER +.elseif $1 == MSG_SENDSUPER // MSG_SENDSUPER // replace "super" arg with "receiver" movl super+8(%esp), %edi // get super structure movl receiver(%edi), %esi // get messaged object @@ -413,6 +427,9 @@ LMsgSendMissInstrumentDone_$0: movl class(%edi), %eax // get messaged class popl %esi // restore callers register popl %edi // restore callers register +.else // CACHE_GET + popl %esi // restore callers register + popl %edi // restore callers register .endif .else // Struct return .if $1 == MSG_SEND // MSG_SEND (stret) @@ -420,7 +437,7 @@ LMsgSendMissInstrumentDone_$0: popl %edi // restore callers register movl self_stret(%esp), %eax // get messaged object movl isa(%eax), %eax // get objects class -.else // MSG_SENDSUPER (stret) +.elseif $1 == MSG_SENDSUPER // MSG_SENDSUPER (stret) // replace "super" arg with "receiver" movl super_stret+8(%esp), %edi// get super structure movl receiver(%edi), %esi // get messaged object @@ -428,6 +445,8 @@ LMsgSendMissInstrumentDone_$0: movl class(%edi), %eax // get messaged class popl %esi // restore callers register popl %edi // restore callers register +.else // CACHE_GET + !! This should not happen. .endif .endif @@ -435,12 +454,12 @@ LMsgSendMissInstrumentDone_$0: // eax points to matching cache entry .align 4, 0x90 -LMsgSendCacheHit_$0_$1: +LMsgSendCacheHit_$0_$1_$2: #if defined(OBJC_INSTRUMENTED) popl %edx // retrieve cache pointer movl mask(%edx), %esi // mask = cache->mask testl %esi, %esi // a mask of zero is only for the... - je LMsgSendHitInstrumentDone_$0_$1 // ... emptyCache, do not record anything + je LMsgSendHitInstrumentDone_$0_$1_$2 // ... emptyCache, do not record anything // locate and update the CacheInstrumentation structure inc %esi // entryCount = mask + 1 @@ -460,15 +479,15 @@ LMsgSendCacheHit_$0_$1: movl %edi, hitProbes(%esi) // cacheData->hitProbes += probeCount movl maxHitProbes(%esi), %edi// if (cacheData->maxHitProbes < probeCount) cmpl %ebx, %edi - jge LMsgSendMaxHitProbeOK_$0_$1 + jge LMsgSendMaxHitProbeOK_$0_$1_$2 movl %ebx, maxHitProbes(%esi)// cacheData->maxHitProbes = probeCount -LMsgSendMaxHitProbeOK_$0_$1: +LMsgSendMaxHitProbeOK_$0_$1_$2: // update cache hit probe histogram cmpl $CACHE_HISTOGRAM_SIZE, %ebx // pin probeCount to max index - jl LMsgSendHitHistoIndexSet_$0_$1 + jl LMsgSendHitHistoIndexSet_$0_$1_$2 movl $(CACHE_HISTOGRAM_SIZE-1), %ebx -LMsgSendHitHistoIndexSet_$0_$1: +LMsgSendHitHistoIndexSet_$0_$1_$2: LEA_STATIC_DATA %esi, _CacheHitHistogram, EXTERNAL_SYMBOL #ifdef NO_MACRO_CONSTS shll $kTwo, %ebx // convert probeCount to histogram index @@ -479,12 +498,16 @@ LMsgSendHitHistoIndexSet_$0_$1: movl 0(%esi), %edi // get current tally inc %edi // movl %edi, 0(%esi) // tally += 1 -LMsgSendHitInstrumentDone_$0_$1: +LMsgSendHitInstrumentDone_$0_$1_$2: popl %ebx // restore non-volatile register #endif // load implementation address, restore state, and we're done +.if $1 == CACHE_GET + // method triplet is already in eax +.else movl method_imp(%eax), %eax // imp = method->method_imp +.endif .if $0 == WORD_RETURN // Regular word return .if $1 == MSG_SENDSUPER // MSG_SENDSUPER @@ -555,6 +578,73 @@ HAVE_CALL_EXTERN_lookupMethodAndLoadCache = 1 PICIFY(func) ; \ jmp %edx ; + + + +/******************************************************************** + * Method _cache_getMethod(Class cls, SEL sel) + * + * If found, returns method triplet pointer. + * If not found, returns NULL. + * + * NOTE: _cache_getMethod never returns any cache entry whose implementation + * is _objc_msgForward. It returns NULL instead. This prevents thread- + * safety and memory management bugs in _class_lookupMethodAndLoadCache. + * See _class_lookupMethodAndLoadCache for details. + ********************************************************************/ + + ENTRY __cache_getMethod + +// load the class into eax + movl self(%esp), %eax + +// do lookup + CacheLookup WORD_RETURN, CACHE_GET, LGetMethodMiss + +// cache hit, method triplet in %eax +// check for _objc_msgForward + LEA_STATIC_DATA %ecx, __objc_msgForward, LOCAL_SYMBOL // eats edx + cmpl method_imp(%eax), %ecx + je LGetMethodMiss // if (imp==_objc_msgForward) return nil + ret // else return method triplet address + +LGetMethodMiss: +// cache miss, return nil + xorl %eax, %eax // zero %eax + ret + +LGetMethodExit: + END_ENTRY __cache_getMethod + + +/******************************************************************** + * IMP _cache_getImp(Class cls, SEL sel) + * + * If found, returns method implementation. + * If not found, returns NULL. + ********************************************************************/ + + ENTRY __cache_getImp + +// load the class into eax + movl self(%esp), %eax + +// do lookup + CacheLookup WORD_RETURN, CACHE_GET, LGetImpMiss + +// cache hit, method triplet in %eax + movl method_imp(%eax), %eax // return method imp + ret + +LGetImpMiss: +// cache miss, return nil + xorl %eax, %eax // zero %eax + ret + +LGetImpExit: + END_ENTRY __cache_getImp + + /******************************************************************** * * id objc_msgSend(id self, SEL _cmd,...); @@ -570,14 +660,7 @@ HAVE_CALL_EXTERN_lookupMethodAndLoadCache = 1 testl %eax, %eax je LMsgSendNilSelf -#if !defined(OBJC_COLLECTING_CACHE) -// check whether context is multithreaded - EXTERN_TO_REG(__objc_multithread_mask,%ecx) - testl %ecx, %ecx - je LMsgSendMT -#endif - -// single threaded and receiver is non-nil: search the cache +// receiver is non-nil: search the cache CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward jmp *%eax // goto *imp @@ -588,32 +671,6 @@ LMsgSendCacheMiss: movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward jmp *%eax // goto *imp -#if !defined(OBJC_COLLECTING_CACHE) -// multithreaded: hold _messageLock while accessing cache -LMsgSendMT: - movl $1, %ecx // acquire _messageLock - LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL -LMsgSendLockSpin: - xchgl %ecx, (%eax) - cmpl $0, %ecx - jne LMsgSendLockSpin - movl self(%esp), %eax // restore eax - - CacheLookup WORD_RETURN, MSG_SEND, LMsgSendMTCacheMiss - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward - jmp *%eax // goto *imp - -// cache miss: go search the method lists -LMsgSendMTCacheMiss: - MethodTableLookup WORD_RETURN, MSG_SEND - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward - jmp *%eax // goto *imp -#endif - // message sent to nil object: call optional handler and return nil LMsgSendNilSelf: EXTERN_TO_REG(__objc_msgNil,%eax) @@ -643,14 +700,7 @@ LMsgSendExit: movl super(%esp), %eax -#if !defined(OBJC_COLLECTING_CACHE) -// check whether context is multithreaded - EXTERN_TO_REG_AGAIN(__objc_multithread_mask,%ecx) - testl %ecx, %ecx - je LMsgSendSuperMT -#endif - -// single threaded and receiver is non-nil: search the cache +// receiver is non-nil: search the cache CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward jmp *%eax // goto *imp @@ -661,32 +711,6 @@ LMsgSendSuperCacheMiss: movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward jmp *%eax // goto *imp -#if !defined(OBJC_COLLECTING_CACHE) -LMsgSendSuperMT: -// multithreaded: hold _messageLock while accessing cache - movl $1, %ecx // acquire _messageLock - LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL -LMsgSendSuperLockSpin: - xchgl %ecx, (%eax) - cmpl $0, %ecx - jne LMsgSendSuperLockSpin - movl super(%esp), %eax // restore eax - - CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperMTCacheMiss - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward - jmp *%eax // goto *imp - -// cache miss: go search the method lists -LMsgSendSuperMTCacheMiss: - MethodTableLookup WORD_RETURN, MSG_SENDSUPER - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSend, %edx // flag word-return for _objc_msgForward - jmp *%eax // goto *imp -#endif - LMsgSendSuperExit: END_ENTRY _objc_msgSendSuper @@ -757,14 +781,7 @@ LMsgSendvArgsOK: testl %eax, %eax je LMsgSendStretNilSelf -#if !defined(OBJC_COLLECTING_CACHE) -// check whether context is multithreaded - EXTERN_TO_REG_AGAIN(__objc_multithread_mask,%ecx) - testl %ecx, %ecx - je LMsgSendStretMT -#endif - -// single threaded and receiver is non-nil: search the cache +// receiver is non-nil: search the cache CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretCacheMiss movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward jmp *%eax // goto *imp @@ -775,32 +792,6 @@ LMsgSendStretCacheMiss: movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward jmp *%eax // goto *imp -#if !defined(OBJC_COLLECTING_CACHE) -// multithreaded: hold _messageLock while accessing cache -LMsgSendStretMT: - movl $1, %ecx // acquire _messageLock - LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL -LMsgSendStretLockSpin: - xchgl %ecx, (%eax) - cmpl $0, %ecx - jne LMsgSendStretLockSpin - movl self_stret(%esp), %eax // restore eax - - CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretMTCacheMiss - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward - jmp *%eax // goto *imp - -// cache miss: go search the method lists -LMsgSendStretMTCacheMiss: - MethodTableLookup STRUCT_RETURN, MSG_SEND - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward - jmp *%eax // goto *imp -#endif - // message sent to nil object: call optional handler and return nil LMsgSendStretNilSelf: EXTERN_TO_REG_AGAIN(__objc_msgNil,%eax) @@ -840,14 +831,7 @@ LMsgSendStretExit: movl super_stret(%esp), %eax -#if !defined(OBJC_COLLECTING_CACHE) -// check whether context is multithreaded - EXTERN_TO_REG_AGAIN(__objc_multithread_mask,%ecx) - testl %ecx, %ecx - je LMsgSendSuperStretMT -#endif - -// single threaded and receiver is non-nil: search the cache +// receiver is non-nil: search the cache CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretCacheMiss movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward jmp *%eax // goto *imp @@ -858,32 +842,6 @@ LMsgSendSuperStretCacheMiss: movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward jmp *%eax // goto *imp -#if !defined(OBJC_COLLECTING_CACHE) -LMsgSendStretSuperMT: -// multithreaded: hold _messageLock while accessing cache - movl $1, %ecx // acquire _messageLock - LEA_STATIC_DATA %eax, _messageLock, EXTERNAL_SYMBOL -LMsgSendSuperStretLockSpin: - xchgl %ecx, (%eax) - cmpl $0, %ecx - jne LMsgSendSuperStretLockSpin - movl super_stret(%esp), %eax // restore eax - - CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretMTCacheMiss - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward - jmp *%eax // goto *imp - -// cache miss: go search the method lists -LMsgSendSuperStretMTCacheMiss: - MethodTableLookup MSG_SENDSUPER - LEA_STATIC_DATA %ecx, _messageLock, EXTERNAL_SYMBOL - movl $0, (%ecx) // unlock - movl $kFwdMsgSendStret, %edx // flag struct-return for _objc_msgForward - jmp *%eax // goto *imp -#endif - LMsgSendSuperStretExit: END_ENTRY _objc_msgSendSuper_stret @@ -1043,7 +1001,7 @@ L__objc_msgForwardStret$pic_base: #endif pushl %ecx pushl (self_stret+16)(%esp) - call _objc_msgSend_stret + call _objc_msgSend movl %ebp,%esp popl %ebp ret diff --git a/runtime/Messengers.subproj/objc-msg-ppc.s b/runtime/Messengers.subproj/objc-msg-ppc.s index 005780b..f3061dc 100644 --- a/runtime/Messengers.subproj/objc-msg-ppc.s +++ b/runtime/Messengers.subproj/objc-msg-ppc.s @@ -49,7 +49,6 @@ * Created from m98k. ********************************************************************/ -#if defined(OBJC_COLLECTING_CACHE) ; _objc_entryPoints and _objc_exitPoints are used by method dispatch ; caching code to figure out whether any threads are actively ; in the cache for dispatching. The labels surround the asm code @@ -57,6 +56,8 @@ .data .globl _objc_entryPoints _objc_entryPoints: + .long __cache_getImp + .long __cache_getMethod .long _objc_msgSend .long _objc_msgSend_stret .long _objc_msgSendSuper @@ -69,6 +70,8 @@ _objc_entryPoints: .globl _objc_exitPoints _objc_exitPoints: + .long LGetImpExit + .long LGetMethodExit .long LMsgSendExit .long LMsgSendStretExit .long LMsgSendSuperExit @@ -78,7 +81,6 @@ _objc_exitPoints: .long LMsgSendSuperFewExit .long LMsgSendSuperFewStretExit .long 0 -#endif /******************************************************************** * @@ -157,7 +159,7 @@ EXTERNAL_SYMBOL = 1 #if defined(__DYNAMIC__) mflr r0 - bl 1f + bcl 20,31,1f ; 31 is cr7[so] 1: mflr $0 mtlr r0 .if $2 == EXTERNAL_SYMBOL @@ -194,7 +196,7 @@ EXTERNAL_SYMBOL = 1 .macro LEA_STATIC_DATA #if defined(__DYNAMIC__) mflr r0 - bl 1f + bcl 20,31,1f ; 31 is cr7[so] 1: mflr $0 mtlr r0 .if $2 == EXTERNAL_SYMBOL @@ -291,7 +293,7 @@ $0: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; -; CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER, cacheMissLabel, FEW_ARGS | MANY_ARGS +; CacheLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER | CACHE_GET, cacheMissLabel, FEW_ARGS | MANY_ARGS ; ; Locate the implementation for a selector in a class method cache. ; @@ -299,13 +301,20 @@ $0: ; STRUCT_RETURN (r3 is structure return address, r4 is first parameter) ; MSG_SEND (first parameter is receiver) ; MSG_SENDSUPER (first parameter is address of objc_super structure) +; CACHE_GET (first parameter is class; return method triplet) ; ; cacheMissLabel = label to branch to iff method is not cached ; ; Eats: r0, r11, r12 -; On exit: (found) imp in ctr register +; On exit: (found) MSG_SEND and MSG_SENDSUPER: return imp in r12 and ctr +; (found) CACHE_GET: return method triplet in r12 ; (not found) jumps to cacheMissLabel -; +; +; For MSG_SEND and MSG_SENDSUPER, the messenger jumps to the imp +; in ctr. The same imp in r12 is used by the method itself for its +; relative addressing. This saves the usual "jump to next line and +; fetch link register" construct inside the method. +; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Values to specify to method lookup macros whether the return type of @@ -317,6 +326,7 @@ STRUCT_RETURN = 1 ; the method is an integer or structure. MSG_SEND = 0 MSG_SENDSUPER = 1 +CACHE_GET = 2 ; Values to specify to method lookup macros whether this is a "few args" call or not ; (number of args < 5 , including self and _cmd) @@ -332,30 +342,29 @@ MANY_ARGS = 1 li r7,0 ; no probes so far! #endif - stw r8,44(r1) ; save r8 .if $3 == MANY_ARGS stw r9,48(r1) ; save r9 and r10 stw r10,52(r1) ; .endif -; locate the cache -; Locking idea: -;LGetMask_$0_$1_$2 - .if $0 == WORD_RETURN ; WORD_RETURN .if $1 == MSG_SEND ; MSG_SEND lwz r12,isa(r3) ; class = receiver->isa -.else ; MSG_SENDSUPER +.elseif $1 == MSG_SENDSUPER ; MSG_SENDSUPER lwz r12,class(r3) ; class = super->class +.else ; CACHE_GET + mr r12,r3 ; class = class .endif .else ; STRUCT_RETURN .if $1 == MSG_SEND ; MSG_SEND lwz r12,isa(r4) ; class = receiver->isa -.else ; MSG_SENDSUPER +.elseif $1 == MSG_SENDSUPER ; MSG_SENDSUPER lwz r12,class(r4) ; class = super->class +.else ; CACHE_GET + mr r12,r4 ; class = class .endif .endif @@ -365,21 +374,14 @@ MANY_ARGS = 1 #if defined(OBJC_INSTRUMENTED) mr r6,r12 ; save cache pointer #endif - lwz r11,mask(r12) ; mask = cache->mask - -; Locking idea -; lea r0,mask(r12) ; XXX eliminate this by moving the mask to first position -; lwarx r11,r0 ; mask = reserve(cache->mask) -; bgt LGetMask_$0_$1_$2 ; if (mask > 0) goto LGetMask // someone already using it -; neg r11 ; mask = -mask -; stcwx. r11,r0 ; cache->mask = mask // store positive to mark in use -; bf LGetMask_$0_$1_$2 ; go to the class and get a possibly new one again + lwz r11,mask(r12) ; mask = cache->mask addi r9,r12,buckets ; buckets = cache->buckets + slwi r11,r11,2 ; r11 = mask << 2 .if $0 == WORD_RETURN ; WORD_RETURN - and r12,r4,r11 ; index = selector & mask + and r12,r4,r11 ; bytes = sel & (mask<<2) .else ; STRUCT_RETURN - and r12,r5,r11 ; index = selector & mask + and r12,r5,r11 ; bytes = sel & (mask<<2) .endif #if defined(OBJC_INSTRUMENTED) @@ -391,17 +393,17 @@ LMiss_$0_$1_$2: addi r9,r9,1 ; slwi r9,r9,2 ; tableSize = entryCount * sizeof(entry) addi r9,r9,buckets ; offset = buckets + tableSize - add r8,r6,r9 ; cacheData = &cache->buckets[mask+1] - lwz r9,missCount(r8) ; cacheData->missCount += 1 + add r11,r6,r9 ; cacheData = &cache->buckets[mask+1] + lwz r9,missCount(r11) ; cacheData->missCount += 1 addi r9,r9,1 ; - stw r9,missCount(r8) ; - lwz r9,missProbes(r8) ; cacheData->missProbes += probeCount + stw r9,missCount(r11) ; + lwz r9,missProbes(r11) ; cacheData->missProbes += probeCount add r9,r9,r7 ; - stw r9,missProbes(r8) ; - lwz r9,maxMissProbes(r8) ; if (probeCount > cacheData->maxMissProbes) + stw r9,missProbes(r11) ; + lwz r9,maxMissProbes(r11) ; if (probeCount > cacheData->maxMissProbes) cmplw r7,r9 ; maxMissProbes = probeCount ble .+8 ; - stw r7,maxMissProbes(r8) ; + stw r7,maxMissProbes(r11) ; lwz r6,36(r1) ; restore r6 lwz r7,40(r1) ; restore r7 @@ -414,8 +416,9 @@ LLoop_$0_$1_$2: #if defined(OBJC_INSTRUMENTED) addi r7,r7,1 ; probeCount += 1 #endif - slwi r0,r12,2 ; convert word index into byte count - lwzx r10,r9,r0 ; method = cache->buckets[index] + + lwzx r10,r9,r12 ; method = buckets[bytes/4] + addi r12,r12,4 ; bytes += 4 cmplwi r10,0 ; if (method == NULL) #if defined(OBJC_INSTRUMENTED) beq LMiss_$0_$1_$2 @@ -423,28 +426,25 @@ LLoop_$0_$1_$2: beq $2 ; goto cacheMissLabel #endif - addi r12,r12,1 ; index += 1 - lwz r8,method_name(r10) ; name = method->method_name - and r12,r12,r11 ; index &= mask - lwz r10,method_imp(r10) ; imp = method->method_imp + lwz r0,method_name(r10) ; name = method->method_name + and r12,r12,r11 ; bytes &= (mask<<2) .if $0 == WORD_RETURN ; WORD_RETURN - cmplw r8,r4 ; if (name != selector) + cmplw r0,r4 ; if (name != selector) .else ; STRUCT_RETURN - cmplw r8,r5 ; if (name != selector) + cmplw r0,r5 ; if (name != selector) .endif bne LLoop_$0_$1_$2 ; goto loop -; Locking idea -; clear lock -; lwz r12,isa(r3) ; XXX or r4 or class(r3/4) - use macro -; lwz r12,cache(r12) -; neg r11 ; mask = -mask -; stwz r11,mask(r12) ; cache->mask = mask // store negative to mark free - - -; cache hit, r10 == method implementation address +; cache hit, r10 == method triplet address +.if $1 == CACHE_GET + ; return method triplet in r12 + mr r12,r10 +.else + ; return method imp in ctr and r12 + lwz r10,method_imp(r10) ; imp = method->method_imp mr r12,r10 ; copy implementation to r12 mtctr r10 ; ctr = imp +.endif #if defined(OBJC_INSTRUMENTED) ; r6 = cache, r7 = probeCount @@ -452,23 +452,22 @@ LLoop_$0_$1_$2: addi r9,r9,1 ; slwi r9,r9,2 ; tableSize = entryCount * sizeof(entry) addi r9,r9,buckets ; offset = buckets + tableSize - add r8,r6,r9 ; cacheData = &cache->buckets[mask+1] - lwz r9,hitCount(r8) ; cache->hitCount += 1 + add r11,r6,r9 ; cacheData = &cache->buckets[mask+1] + lwz r9,hitCount(r11) ; cache->hitCount += 1 addi r9,r9,1 ; - stw r9,hitCount(r8) ; - lwz r9,hitProbes(r8) ; cache->hitProbes += probeCount + stw r9,hitCount(r11) ; + lwz r9,hitProbes(r11) ; cache->hitProbes += probeCount add r9,r9,r7 ; - stw r9,hitProbes(r8) ; - lwz r9,maxHitProbes(r8) ; if (probeCount > cache->maxMissProbes) + stw r9,hitProbes(r11) ; + lwz r9,maxHitProbes(r11) ; if (probeCount > cache->maxMissProbes) cmplw r7,r9 ;maxMissProbes = probeCount ble .+8 ; - stw r7,maxHitProbes(r8) ; + stw r7,maxHitProbes(r11) ; lwz r6,36(r1) ; restore r6 lwz r7,40(r1) ; restore r7 #endif - lwz r8,44(r1) ; restore r8 .if $3 == MANY_ARGS lwz r9,48(r1) ; restore r9 and r10 lwz r10,52(r1) ; @@ -476,19 +475,56 @@ LLoop_$0_$1_$2: .endmacro +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; CacheLookup cache locking - 2001-11-12 +; The collecting cache mechanism precludes the need for a cache lock +; in objc_msgSend. The cost of the collecting cache is small: a few +; K of memory for uncollected caches, and less than 1 ms per collection. +; A large app will only run collection a few times. +; Using the code below to lock the cache almost doubles messaging time, +; costing several seconds of CPU across several minutes of operation. +; The code below probably could be improved, but almost all of the +; locking slowdown is in the sync and isync. +; +; 40 million message test times (G4 1x667): +; no lock 4.390u 0.030s 0:04.59 96.2% 0+0k 0+1io 0pf+0w +; with lock 9.120u 0.010s 0:09.83 92.8% 0+0k 0+0io 0pf+0w +; +;; LockCache mask_dest, cache +;.macro LockCache +; ; LOCKED mask is NEGATIVE +; lwarx $0, mask, $1 ; mask = reserve(cache->mask) +; cmpwi $0, 0 ; +; blt .-8 ; try again if mask < 0 +; neg r0, $0 ; +; stwcx. r0, mask, $1 ; cache->mask = -mask ($0 keeps +mask) +; bne .-20 ; try again if lost reserve +; isync ; flush prefetched instructions after locking +;.endmacro +; +;; UnlockCache (mask<<2), cache +;.macro UnlockCache +; sync ; finish previous instructions before unlocking +; srwi r0, $0, 2 ; r0 = (mask<<2) >> 2 +; stw r0, mask($1) ; cache->mask = +mask +;.endmacro +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; MethodTableLookup WORD_RETURN | STRUCT_RETURN, MSG_SEND | MSG_SENDSUPER, FEW_ARGS | MANY_ARGS ; -; Takes: WORD_RETURN (r3 is first parameter) +; Takes: WORD_RETURN (r3 is first parameter) ; STRUCT_RETURN (r3 is structure return address, r4 is first parameter) ; MSG_SEND (first parameter is receiver) ; MSG_SENDSUPER (first parameter is address of objc_super structure) ; ; Eats: r0, r11, r12 -; On exit: restores registers r8 and possibly, r9 and r10, saved by CacheLookup +; On exit: if MANY_ARGS, restores r9,r10 saved by CacheLookup ; imp in ctr ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -500,7 +536,8 @@ HAVE_CALL_EXTERN_lookupMethodAndLoadCache = 0 stw r5, 32(r1) ; stw r6, 36(r1) ; stw r7, 40(r1) ; - ; r8 and possibly, r9 and r10, were saved by CacheLookup + stw r8, 44(r1) ; + ; if MANY_ARGS, r9 and r10 were saved by CacheLookup mflr r0 ; save lr stw r0,8(r1) ; @@ -585,10 +622,10 @@ HAVE_CALL_EXTERN_lookupMethodAndLoadCache = 1 lwz r5, 32(r1) ; lwz r6, 36(r1) ; lwz r7, 40(r1) ; + lwz r8, 44(r1) ; - lwz r8, 44(r1) ; restore leftovers from CacheLookup... .if $2 == MANY_ARGS - lwz r9, 48(r1) ; + lwz r9, 48(r1) ; restore saves from CacheLookup lwz r10,52(r1) ; .endif @@ -683,6 +720,77 @@ HAVE_CALL_EXTERN_mcount = 1 #endif .endmacro + +/******************************************************************** + * Method _cache_getMethod(Class cls, SEL sel) + * + * On entry: r3 = class whose cache is to be searched + * r4 = selector to search for + * + * If found, returns method triplet pointer. + * If not found, returns NULL. + * + * NOTE: _cache_getMethod never returns any cache entry whose implementation + * is _objc_msgForward. It returns NULL instead. This prevents thread- + * safety and memory management bugs in _class_lookupMethodAndLoadCache. + * See _class_lookupMethodAndLoadCache for details. + ********************************************************************/ + + ENTRY __cache_getMethod +; do profiling if enabled + CALL_MCOUNT + +; do lookup + CacheLookup WORD_RETURN, CACHE_GET, LGetMethodMiss, MANY_ARGS + +; cache hit, method triplet in r12 +; check for _objc_msgForward + lwz r11, method_imp(r12) ; get the imp + LEA_STATIC_DATA r10, __objc_msgForward, LOCAL_SYMBOL + cmplw r11, r10 + beq LGetMethodMiss ; if (imp==_objc_msgForward) return nil + mr r3, r12 ; else return method triplet address + blr + +LGetMethodMiss: +; cache miss, return nil + li r3, 0 ; return nil + blr + +LGetMethodExit: + END_ENTRY __cache_getMethod + + +/******************************************************************** + * IMP _cache_getImp(Class cls, SEL sel) + * + * On entry: r3 = class whose cache is to be searched + * r4 = selector to search for + * + * If found, returns method implementation. + * If not found, returns NULL. + ********************************************************************/ + + ENTRY __cache_getImp +; do profiling if enabled + CALL_MCOUNT + +; do lookup + CacheLookup WORD_RETURN, CACHE_GET, LGetImpMiss, MANY_ARGS + +; cache hit, method triplet in r12 + lwz r3, method_imp(r12) ; return method imp address + blr + +LGetImpMiss: +; cache miss, return nil + li r3, 0 ; return nil + blr + +LGetImpExit: + END_ENTRY __cache_getImp + + /******************************************************************** * id objc_msgSend(id self, * SEL op, @@ -709,15 +817,7 @@ L__objc_msgNil: cmplwi r3,0 ; receiver nil? beq LMsgSendNilSelf ; if so, call handler or return nil -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendMT ; branch to the locking case -#endif - -; single threaded and receiver is non-nil: search the cache +; receiver is non-nil: search the cache CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss, MANY_ARGS li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward bctr ; goto *imp; @@ -728,23 +828,6 @@ LMsgSendCacheMiss: li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendMT: - PLOCK r11, _messageLock - CacheLookup WORD_RETURN, MSG_SEND, LMsgSendMTCacheMiss, MANY_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendMTCacheMiss: - MethodTableLookup WORD_RETURN, MSG_SEND, MANY_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; -#endif - ; message sent to nil object call: optional handler and return nil LMsgSendNilSelf: LOAD_STATIC_WORD r11, __objc_msgNil, EXTERNAL_SYMBOL @@ -789,15 +872,7 @@ LMsgSendExit: cmplwi r4,0 ; receiver nil? beq LMsgSendStretNilSelf ; if so, call handler or just return -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendStretMT ; branch to the locking case -#endif - -; single threaded and receiver is non-nil: search the cache +; receiver is non-nil: search the cache CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretCacheMiss, MANY_ARGS li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward bctr ; goto *imp; @@ -808,23 +883,6 @@ LMsgSendStretCacheMiss: li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendStretMT: - PLOCK r11, _messageLock - CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendStretMTCacheMiss, MANY_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendStretMTCacheMiss: - MethodTableLookup STRUCT_RETURN, MSG_SEND, MANY_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; -#endif - ; message sent to nil object call optional handler and return nil LMsgSendStretNilSelf: LOAD_STATIC_WORD r11, __objc_msgNil, EXTERNAL_SYMBOL @@ -863,15 +921,7 @@ LMsgSendStretExit: ; do profiling when enabled CALL_MCOUNT -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendSuperMT ; branch to the locking case -#endif - -; single threaded: search the cache +; search the cache CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperCacheMiss, MANY_ARGS lwz r3,receiver(r3) ; receiver is the first arg li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward @@ -884,25 +934,6 @@ LMsgSendSuperCacheMiss: li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendSuperMT: - PLOCK r11, _messageLock - CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperMTCacheMiss, MANY_ARGS - PUNLOCK r11, _messageLock - lwz r3,receiver(r3) ; receiver is the first arg - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendSuperMTCacheMiss: - MethodTableLookup WORD_RETURN, MSG_SENDSUPER, MANY_ARGS - PUNLOCK r11, _messageLock - lwz r3,receiver(r3) ; receiver is the first arg - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; -#endif - LMsgSendSuperExit: END_ENTRY _objc_msgSendSuper @@ -931,15 +962,7 @@ LMsgSendSuperExit: ; do profiling when enabled CALL_MCOUNT -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendSuperStretMT ; branch to the locking case -#endif - -; single threaded: search the cache +; search the cache CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretCacheMiss, MANY_ARGS lwz r4,receiver(r4) ; receiver is the first arg li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward @@ -952,25 +975,6 @@ LMsgSendSuperStretCacheMiss: li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendSuperStretMT: - PLOCK r11, _messageLock - CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperStretMTCacheMiss, MANY_ARGS - PUNLOCK r11, _messageLock - lwz r4,receiver(r4) ; receiver is the first arg - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendSuperStretMTCacheMiss: - MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER, MANY_ARGS - PUNLOCK r11, _messageLock - lwz r4,receiver(r4) ; receiver is the first arg - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; -#endif - LMsgSendSuperStretExit: END_ENTRY _objc_msgSendSuper_stret @@ -1359,15 +1363,7 @@ LMsgSendvStretSendIt: cmplwi r3,0 ; receiver nil? beq LMsgSendFewNilSelf ; if so, call handler or return nil -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendFewMT ; branch to the locking case -#endif - -; single threaded and receiver is non-nil: search the cache +; receiver is non-nil: search the cache CacheLookup WORD_RETURN, MSG_SEND, LMsgSendFewCacheMiss, FEW_ARGS li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward bctr ; goto *imp; @@ -1378,23 +1374,6 @@ LMsgSendFewCacheMiss: li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendFewMT: - PLOCK r11, _messageLock - CacheLookup WORD_RETURN, MSG_SEND, LMsgSendFewMTCacheMiss, FEW_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendFewMTCacheMiss: - MethodTableLookup WORD_RETURN, MSG_SEND, FEW_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; -#endif - ; message sent to nil object call: optional handler and return nil LMsgSendFewNilSelf: LOAD_STATIC_WORD r11, __objc_msgNil, EXTERNAL_SYMBOL @@ -1439,15 +1418,7 @@ LMsgSendFewExit: cmplwi r4,0 ; receiver nil? beq LMsgSendFewStretNilSelf ; if so, call handler or just return -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendFewStretMT ; branch to the locking case -#endif - -; single threaded and receiver is non-nil: search the cache +; receiver is non-nil: search the cache CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendFewStretCacheMiss, FEW_ARGS li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward bctr ; goto *imp; @@ -1458,23 +1429,6 @@ LMsgSendFewStretCacheMiss: li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendFewStretMT: - PLOCK r11, _messageLock - CacheLookup STRUCT_RETURN, MSG_SEND, LMsgSendFewStretMTCacheMiss, FEW_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendFewStretMTCacheMiss: - MethodTableLookup STRUCT_RETURN, MSG_SEND, FEW_ARGS - PUNLOCK r11, _messageLock - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; -#endif - ; message sent to nil object call optional handler and return nil LMsgSendFewStretNilSelf: LOAD_STATIC_WORD r11, __objc_msgNil, EXTERNAL_SYMBOL @@ -1513,15 +1467,7 @@ LMsgSendFewStretExit: ; do profiling when enabled CALL_MCOUNT -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendSuperFewMT ; branch to the locking case -#endif - -; single threaded: search the cache +; search the cache CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperFewCacheMiss, FEW_ARGS lwz r3,receiver(r3) ; receiver is the first arg li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward @@ -1534,25 +1480,6 @@ LMsgSendSuperFewCacheMiss: li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendSuperFewMT: - PLOCK r11, _messageLock - CacheLookup WORD_RETURN, MSG_SENDSUPER, LMsgSendSuperFewMTCacheMiss, FEW_ARGS - PUNLOCK r11, _messageLock - lwz r3,receiver(r3) ; receiver is the first arg - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendSuperFewMTCacheMiss: - MethodTableLookup WORD_RETURN, MSG_SENDSUPER, FEW_ARGS - PUNLOCK r11, _messageLock - lwz r3,receiver(r3) ; receiver is the first arg - li r11,kFwdMsgSend ; indicate word-return to _objc_msgForward - bctr ; goto *imp; -#endif - LMsgSendSuperFewExit: END_ENTRY _objc_msgSendSuperFew @@ -1581,15 +1508,7 @@ LMsgSendSuperFewExit: ; do profiling when enabled CALL_MCOUNT -#if !defined(OBJC_COLLECTING_CACHE) -; check whether context is multithreaded - lis r11,ha16(__objc_multithread_mask) - lwz r11,lo16(__objc_multithread_mask)(r11) - cmplwi r11,0 ; objc_multithread_mask zero? - beq LMsgSendSuperFewStretMT ; branch to the locking case -#endif - -; single threaded: search the cache +; search the cache CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperFewStretCacheMiss, FEW_ARGS lwz r4,receiver(r4) ; receiver is the first arg li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward @@ -1602,25 +1521,6 @@ LMsgSendSuperFewStretCacheMiss: li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward bctr ; goto *imp; -#if !defined(OBJC_COLLECTING_CACHE) -; multithreaded: hold _messageLock while accessing cache -LMsgSendSuperFewStretMT: - PLOCK r11, _messageLock - CacheLookup STRUCT_RETURN, MSG_SENDSUPER, LMsgSendSuperFewStretMTCacheMiss, FEW_ARGS - PUNLOCK r11, _messageLock - lwz r4,receiver(r4) ; receiver is the first arg - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; - -; cache miss: go search the method lists -LMsgSendSuperFewStretMTCacheMiss: - MethodTableLookup STRUCT_RETURN, MSG_SENDSUPER, FEW_ARGS - PUNLOCK r11, _messageLock - lwz r4,receiver(r4) ; receiver is the first arg - li r11,kFwdMsgSendStret ; indicate struct-return to _objc_msgForward - bctr ; goto *imp; -#endif - LMsgSendSuperFewStretExit: END_ENTRY _objc_msgSendSuperFew_stret diff --git a/runtime/Object.h b/runtime/Object.h index c7cb936..312d2e0 100644 --- a/runtime/Object.h +++ b/runtime/Object.h @@ -150,7 +150,6 @@ /* Abstract Protocol for Dynamic Loading */ -#if !defined(NeXT_PDO) @interface Object (DynamicLoading) //+ finishLoading:(headerType *)header; @@ -158,7 +157,6 @@ + startUnloading; @end -#endif OBJC_EXPORT id object_dispose(Object *anObject); OBJC_EXPORT id object_copy(Object *anObject, unsigned nBytes); diff --git a/runtime/Object.m b/runtime/Object.m index 08a6df4..e3552b6 100644 --- a/runtime/Object.m +++ b/runtime/Object.m @@ -26,14 +26,6 @@ Copyright 1988-1996 NeXT Software, Inc. */ -#ifdef WINNT -#include -#endif - -#ifdef NeXT_PDO // pickup BUG fix flags -#import -#endif - #import #import "objc-private.h" #import @@ -44,7 +36,6 @@ OBJC_EXPORT id (*_cvtToId)(const char *); OBJC_EXPORT id (*_poseAs)(); -#define ISMETA(cls) (((struct objc_class *)cls)->info & CLS_META) // Error Messages static const char diff --git a/runtime/OldClasses.subproj/List.h b/runtime/OldClasses.subproj/List.h index e7326f5..c9de42e 100644 --- a/runtime/OldClasses.subproj/List.h +++ b/runtime/OldClasses.subproj/List.h @@ -30,7 +30,7 @@ */ -#warning the API in this header is obsolete +#warning The API in this header is obsoleted by NSArray. #ifndef _OBJC_LIST_H_ #define _OBJC_LIST_H_ diff --git a/runtime/OldClasses.subproj/Makefile b/runtime/OldClasses.subproj/Makefile deleted file mode 100644 index 2f15e0f..0000000 --- a/runtime/OldClasses.subproj/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -# -# Generated by the Apple Project Builder. -# -# NOTE: Do NOT change this file -- Project Builder maintains it. -# -# Put all of your customizations in files called Makefile.preamble -# and Makefile.postamble (both optional), and Makefile will include them. -# - -NAME = OldClasses - -PROJECTVERSION = 2.8 -PROJECT_TYPE = Component - -HFILES = List.h - -MFILES = List.m - -OTHERSRCS = Makefile.preamble Makefile - - -MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles -CODE_GEN_STYLE = DYNAMIC -MAKEFILE = subproj.make -LIBS = -DEBUG_LIBS = $(LIBS) -PROF_LIBS = $(LIBS) - - -PUBLIC_HEADERS = List.h - - - -NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc -WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc -PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc -NEXTSTEP_JAVA_COMPILER = /usr/bin/javac -WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe -PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac - -include $(MAKEFILEDIR)/platform.make - --include Makefile.preamble - -include $(MAKEFILEDIR)/$(MAKEFILE) - --include Makefile.postamble - --include Makefile.dependencies diff --git a/runtime/OldClasses.subproj/Makefile.preamble b/runtime/OldClasses.subproj/Makefile.preamble deleted file mode 100644 index 2b5897a..0000000 --- a/runtime/OldClasses.subproj/Makefile.preamble +++ /dev/null @@ -1,7 +0,0 @@ - -OTHER_PROJECT_HEADERS = $(PUBLIC_HEADERS) $(OTHER_PRIVATE_HEADERS) - -PUBLIC_HEADER_DIR_SUFFIX = /objc -PRIVATE_HEADER_DIR_SUFFIX = /objc - - diff --git a/runtime/OldClasses.subproj/PB.project b/runtime/OldClasses.subproj/PB.project deleted file mode 100644 index da9c064..0000000 --- a/runtime/OldClasses.subproj/PB.project +++ /dev/null @@ -1,27 +0,0 @@ -{ - DYNAMIC_CODE_GEN = YES; - FILESTABLE = { - CLASSES = (); - H_FILES = (List.h); - OTHER_LINKED = (List.m); - OTHER_SOURCES = (Makefile.preamble, Makefile); - PROJECT_HEADERS = (); - PUBLIC_HEADERS = (List.h); - SUBPROJECTS = (); - }; - LANGUAGE = English; - LOCALIZABLE_FILES = {}; - MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; - NEXTSTEP_BUILDTOOL = /bin/gnumake; - NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; - NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; - PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; - PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; - PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; - PROJECTNAME = OldClasses; - PROJECTTYPE = Component; - PROJECTVERSION = 2.8; - WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; - WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; - WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; -} diff --git a/runtime/PB.project b/runtime/PB.project deleted file mode 100644 index e197c50..0000000 --- a/runtime/PB.project +++ /dev/null @@ -1,68 +0,0 @@ -{ - CURRENTLY_ACTIVE_VERSION = YES; - DEPLOY_WITH_VERSION_NAME = A; - DYNAMIC_CODE_GEN = YES; - FILESTABLE = { - CLASSES = (); - HEADERSEARCH = (); - H_FILES = ( - error.h, - hashtable2.h, - maptable.h, - "objc-api.h", - "objc-class.h", - "objc-config.h", - "objc-load.h", - "objc-private.h", - "objc-runtime.h", - objc.h, - Object.h, - Protocol.h - ); - OTHER_LINKED = ( - hashtable2.m, - maptable.m, - "objc-class.m", - "objc-errors.m", - "objc-file.m", - "objc-load.m", - "objc-moninit.c", - "objc-runtime.m", - "objc-sel.m", - Object.m, - Protocol.m - ); - OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble); - PROJECT_HEADERS = ("objc-runtime.h", "objc-class.h"); - PUBLIC_HEADERS = ( - "objc-class.h", - "objc-api.h", - "objc-load.h", - "objc-runtime.h", - objc.h, - Object.h, - Protocol.h, - error.h, - hashtable2.h - ); - SUBPROJECTS = (Messengers.subproj, OldClasses.subproj); - }; - LANGUAGE = English; - LOCALIZABLE_FILES = {}; - MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; - NEXTSTEP_BUILDTOOL = /usr/bin/gnumake; - NEXTSTEP_INSTALLDIR = /usr/lib; - NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; - NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; - PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; - PDO_UNIX_INSTALLDIR = "$(LOCAL_DEVELOPER_DIR)/Libraries"; - PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; - PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; - PROJECTNAME = runtime; - PROJECTTYPE = Library; - PROJECTVERSION = 2.8; - WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; - WINDOWS_INSTALLDIR = /.; - WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; - WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; -} diff --git a/runtime/error.h b/runtime/error.h index 3faf3d5..acbefba 100644 --- a/runtime/error.h +++ b/runtime/error.h @@ -31,7 +31,7 @@ All rights reserved. */ -#warning the API in this header is obsolete +#warning The API in this header is obsoleted by NSException et al. #ifndef _OBJC_ERROR_H_ #define _OBJC_ERROR_H_ @@ -100,7 +100,7 @@ OBJC_EXPORT void _NXRemoveHandler( NXHandler *handler ); */ OBJC_EXPORT -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && !defined(NeXT_PDO) +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) volatile /* never returns */ #endif void _NXRaiseError(int code, const void *data1, const void *data2) diff --git a/runtime/hashtable2.h b/runtime/hashtable2.h index ecb94be..0f13973 100644 --- a/runtime/hashtable2.h +++ b/runtime/hashtable2.h @@ -27,7 +27,7 @@ Copyright 1989-1996 NeXT Software, Inc. */ -#warning the API in this header is obsolete +#warning The API in this header is obsoleted by NSHashtable.h #ifndef _OBJC_LITTLE_HASHTABLE_H_ #define _OBJC_LITTLE_HASHTABLE_H_ @@ -39,7 +39,7 @@ *************************************************************************/ /* This module allows hashing of arbitrary data. Such data must be pointers or integers, and client is responsible for allocating/deallocating this data. A deallocation call-back is provided. -The objective C class HashTable is prefered when dealing with (key, values) associations because it is easier to use in that situation. +The objective C class HashTable is preferred when dealing with (key, values) associations because it is easier to use in that situation. As well-behaved scalable data structures, hash tables double in size when they start becoming full, thus guaranteeing both average constant time access and linear size. */ typedef struct { diff --git a/runtime/hashtable2.m b/runtime/hashtable2.m index 26b88e1..8cfca83 100644 --- a/runtime/hashtable2.m +++ b/runtime/hashtable2.m @@ -27,10 +27,6 @@ Created by Bertrand Serlet, Feb 89 */ -#if defined(NeXT_PDO) -#import -#endif - #import #import "objc-private.h" diff --git a/runtime/objc-api.h b/runtime/objc-api.h index 2b139e1..c911378 100644 --- a/runtime/objc-api.h +++ b/runtime/objc-api.h @@ -25,8 +25,12 @@ #if !defined(OBJC_EXPORT) +#if defined(__cplusplus) + #define OBJC_EXPORT extern "C" +#else #define OBJC_EXPORT extern #endif +#endif #if !defined(OBJC_IMPORT) #define OBJC_IMPORT extern diff --git a/runtime/objc-class.h b/runtime/objc-class.h index 7d3be02..b1b61c3 100644 --- a/runtime/objc-class.h +++ b/runtime/objc-class.h @@ -47,8 +47,8 @@ struct objc_class { struct objc_cache *cache; struct objc_protocol_list *protocols; }; -#define CLS_GETINFO(cls,infomask) ((cls)->info & infomask) -#define CLS_SETINFO(cls,infomask) ((cls)->info |= infomask) +#define CLS_GETINFO(cls,infomask) ((cls)->info & (infomask)) +#define CLS_SETINFO(cls,infomask) ((cls)->info |= (infomask)) #define CLS_CLASS 0x1L #define CLS_META 0x2L @@ -62,6 +62,51 @@ struct objc_class { // the JavaBridge constructs classes with these markers #define CLS_JAVA_HYBRID 0x200L #define CLS_JAVA_CLASS 0x400L +// thread-safe +initialize +#define CLS_INITIALIZING 0x800 + +/* + * (true as of 2001-9-24) + * Thread-safety note: changes to these flags are not atomic, so + * the only thing preventing lost updates is the timing of the changes. + * + * As long as the following are isolated from each other for any one class, + * nearly all flag updates will be safe: + * - compile-time + * - loading in one thread (not including +load) without messaging + * - initializing in one thread with messaging from that thread only + * - multi-threaded messaging with method caching + * + * The current code doesn't protect loading yet. + * + * Times when the flags may change: + * CLS_CLASS: compile-time, hand-built classes + * CLS_META: compile time, hand-built classes + * CLS_INITIALIZED: initialize + * CLS_POSING: unsafe, but posing has other thread-safety problems + * CLS_MAPPED: compile-time + * CLS_FLUSH_CACHE: messaging + * CLS_GROW_CACHE: messaging + * FLUSH_CACHE and GROW_CACHE are protected from each other by the + * cacheUpdateLock. + * CLS_NEED_BIND: load, initialize + * CLS_METHOD_ARRAY: load + * CLS_JAVA_HYBRID: hand-built classes + * CLS_JAVA_CLASS: hand-built classes, initialize + * CLS_INITIALIZING: initialize + * + * The only unsafe updates are: + * - posing (unsafe anyway) + * - hand-built classes (including JavaBridge classes) + * There is a short time between objc_addClass inserts the new class + * into the class_hash and the builder setting the right flags. + * A thread looking at the class_hash could send a message to the class + * and trigger initialization, and the changes to the initialization + * flags and the hand-adjusted flags could collide. + * Solution: don't do that. + */ + + /* * Category Template */ @@ -74,24 +119,27 @@ struct objc_category { struct objc_method_list *class_methods; struct objc_protocol_list *protocols; }; + /* * Instance Variable Template */ typedef struct objc_ivar *Ivar; -struct objc_ivar_list { - int ivar_count; +struct objc_ivar { + char *ivar_name; + char *ivar_type; + int ivar_offset; #ifdef __alpha__ int space; #endif - struct objc_ivar { - char *ivar_name; - char *ivar_type; - int ivar_offset; +}; + +struct objc_ivar_list { + int ivar_count; #ifdef __alpha__ - int space; + int space; #endif - } ivar_list[1]; /* variable length structure */ + struct objc_ivar ivar_list[1]; /* variable length structure */ }; OBJC_EXPORT Ivar object_setInstanceVariable(id, const char *name, void *); @@ -102,6 +150,12 @@ OBJC_EXPORT Ivar object_getInstanceVariable(id, const char *name, void **); */ typedef struct objc_method *Method; +struct objc_method { + SEL method_name; + char *method_types; + IMP method_imp; +}; + struct objc_method_list { struct objc_method_list *obsolete; @@ -109,11 +163,7 @@ struct objc_method_list { #ifdef __alpha__ int space; #endif - struct objc_method { - SEL method_name; - char *method_types; - IMP method_imp; - } method_list[1]; /* variable length structure */ + struct objc_method method_list[1]; /* variable length structure */ }; /* Protocol support */ @@ -160,6 +210,7 @@ typedef struct objc_cache * Cache; #define CACHE_BUCKET_NAME(B) ((B)->method_name) #define CACHE_BUCKET_IMP(B) ((B)->method_imp) #define CACHE_BUCKET_VALID(B) (B) +#define CACHE_HASH(sel, mask) (((uarith_t)(sel)>>2) & (mask)) struct objc_cache { unsigned int mask; /* total = mask + 1 */ unsigned int occupied; @@ -185,7 +236,6 @@ OBJC_EXPORT Class class_poseAs(Class imposter, Class original); OBJC_EXPORT unsigned method_getNumberOfArguments(Method); OBJC_EXPORT unsigned method_getSizeOfArguments(Method); OBJC_EXPORT unsigned method_getArgumentInfo(Method m, int arg, const char **type, int *offset); -OBJC_EXPORT const char * NSModulePathForClass (Class aClass); // usage for nextMethodList // diff --git a/runtime/objc-class.m b/runtime/objc-class.m index cfb47f2..16d474d 100644 --- a/runtime/objc-class.m +++ b/runtime/objc-class.m @@ -27,6 +27,133 @@ * Author: s. naroff **********************************************************************/ + +/*********************************************************************** + * Method cache locking (GrP 2001-1-14) + * + * For speed, objc_msgSend does not acquire any locks when it reads + * method caches. Instead, all cache changes are performed so that any + * objc_msgSend running concurrently with the cache mutator will not + * crash or hang or get an incorrect result from the cache. + * + * When cache memory becomes unused (e.g. the old cache after cache + * expansion), it is not immediately freed, because a concurrent + * objc_msgSend could still be using it. Instead, the memory is + * disconnected from the data structures and placed on a garbage list. + * The memory is now only accessible to instances of objc_msgSend that + * were running when the memory was disconnected; any further calls to + * objc_msgSend will not see the garbage memory because the other data + * structures don't point to it anymore. The collecting_in_critical + * function checks the PC of all threads and returns FALSE when all threads + * are found to be outside objc_msgSend. This means any call to objc_msgSend + * that could have had access to the garbage has finished or moved past the + * cache lookup stage, so it is safe to free the memory. + * + * All functions that modify cache data or structures must acquire the + * cacheUpdateLock to prevent interference from concurrent modifications. + * The function that frees cache garbage must acquire the cacheUpdateLock + * and use collecting_in_critical() to flush out cache readers. + * + * Cache readers (PC-checked by collecting_in_critical()) + * objc_msgSend* + * _cache_getImp + * _cache_getMethod + * + * Cache writers (hold cacheUpdateLock while reading or writing; not PC-checked) + * _cache_fill (acquires lock) + * _cache_expand (only called from cache_fill) + * _cache_create (only called from cache_expand) + * bcopy (only called from instrumented cache_expand) + * flush_caches (acquires lock) + * _cache_flush (only called from cache_fill and flush_caches) + * _cache_collect_free (only called from cache_expand and cache_flush) + * + * UNPROTECTED cache readers (NOT thread-safe; used for debug info only) + * _cache_print + * _class_printMethodCaches + * _class_printDuplicateCacheEntries + * _class_printMethodCacheStatistics + * + * _class_lookupMethodAndLoadCache is a special case. It may read a + * method triplet out of one cache and store it in another cache. This + * is unsafe if the method triplet is a forward:: entry, because the + * triplet itself could be freed unless _class_lookupMethodAndLoadCache + * were PC-checked or used a lock. Additionally, storing the method + * triplet in both caches would result in double-freeing if both caches + * were flushed or expanded. The solution is for _cache_getMethod to + * ignore all entries whose implementation is _objc_msgForward, so + * _class_lookupMethodAndLoadCache cannot look at a forward:: entry + * unsafely or place it in multiple caches. + ***********************************************************************/ + +/*********************************************************************** + * Thread-safety during class initialization (GrP 2001-9-24) + * + * Initial state: CLS_INITIALIZING and CLS_INITIALIZED both clear. + * During initialization: CLS_INITIALIZING is set + * After initialization: CLS_INITIALIZING clear and CLS_INITIALIZED set. + * CLS_INITIALIZING and CLS_INITIALIZED are never set at the same time. + * CLS_INITIALIZED is never cleared once set. + * + * Only one thread is allowed to actually initialize a class and send + * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING. + * + * Additionally, threads trying to send messages to a class must wait for + * +initialize to finish. During initialization of a class, that class's + * method cache is kept empty. objc_msgSend will revert to + * class_lookupMethodAndLoadCache, which checks CLS_INITIALIZED before + * messaging. If CLS_INITIALIZED is clear but CLS_INITIALIZING is set, + * the thread must block, unless it is the thread that started + * initializing the class in the first place. + * + * Each thread keeps a list of classes it's initializing. + * The global classInitLock is used to synchronize changes to CLS_INITIALIZED + * and CLS_INITIALIZING: the transition to CLS_INITIALIZING must be + * an atomic test-and-set with respect to itself and the transition + * to CLS_INITIALIZED. + * The global classInitWaitCond is used to block threads waiting for an + * initialization to complete. The classInitLock synchronizes + * condition checking and the condition variable. + **********************************************************************/ + +/*********************************************************************** + * +initialize deadlock case when a class is marked initializing while + * its superclass is initialized. Solved by completely initializing + * superclasses before beginning to initialize a class. + * + * OmniWeb class hierarchy: + * OBObject + * | ` OBPostLoader + * OFObject + * / \ + * OWAddressEntry OWController + * | + * OWConsoleController + * + * Thread 1 (evil testing thread): + * initialize OWAddressEntry + * super init OFObject + * super init OBObject + * [OBObject initialize] runs OBPostLoader, which inits lots of classes... + * initialize OWConsoleController + * super init OWController - wait for Thread 2 to finish OWController init + * + * Thread 2 (normal OmniWeb thread): + * initialize OWController + * super init OFObject - wait for Thread 1 to finish OFObject init + * + * deadlock! + * + * Solution: fully initialize super classes before beginning to initialize + * a subclass. Then the initializing+initialized part of the class hierarchy + * will be a contiguous subtree starting at the root, so other threads + * can't jump into the middle between two initializing classes, and we won't + * get stuck while a superclass waits for its subclass which waits for the + * superclass. + **********************************************************************/ + + + /*********************************************************************** * Imports. **********************************************************************/ @@ -73,6 +200,9 @@ size_t malloc_size (const void * ptr); // purpose // See radar 2364264 about incorrectly propogating _objc_forward entries // and double freeing them, first, before turning this on! +// (Radar 2364264 is now "inactive".) +// Double-freeing is also a potential problem when this is off. See +// note about _class_lookupMethodAndLoadCache in "Method cache locking". //#define PRELOAD_SUPERCLASS_CACHES /*********************************************************************** @@ -104,12 +234,6 @@ enum { // one entry is embedded in the cache structure itself #define TABLE_SIZE(count) ((count - 1) * sizeof(Method)) -// Class state -#define ISCLASS(cls) ((((struct objc_class *) cls)->info & CLS_CLASS) != 0) -#define ISMETA(cls) ((((struct objc_class *) cls)->info & CLS_META) != 0) -#define GETMETA(cls) (ISMETA(cls) ? ((struct objc_class *) cls) : ((struct objc_class *) cls)->isa) -#define ISINITIALIZED(cls) ((GETMETA(cls)->info & CLS_INITIALIZED) != 0) -#define MARKINITIALIZED(cls) (GETMETA(cls)->info |= CLS_INITIALIZED) /*********************************************************************** * Types internal to this module. @@ -144,22 +268,19 @@ static void addClassToOriginalClass (Class posingClass, Class originalClass); static void _objc_addOrigClass (Class origClass); static void _freedHandler (id self, SEL sel); static void _nonexistentHandler (id self, SEL sel); -static void class_initialize (Class clsDesc); -static void * objc_malloc (int byteCount); +static void class_initialize (Class cls); static Cache _cache_expand (Class cls); static int LogObjCMessageSend (BOOL isClassMethod, const char * objectsClass, const char * implementingClass, SEL selector); -static void _cache_fill (Class cls, Method smt, SEL sel); +static BOOL _cache_fill (Class cls, Method smt, SEL sel); +static void _cache_addForwardEntry(Class cls, SEL sel); static void _cache_flush (Class cls); -static Method _class_lookupMethod (Class cls, SEL sel); static int SubtypeUntil (const char * type, char end); static const char * SkipFirstType (const char * type); -#ifdef OBJC_COLLECTING_CACHE static unsigned long _get_pc_for_thread (mach_port_t thread); static int _collecting_in_critical (void); static void _garbage_make_room (void); static void _cache_collect_free (void * data, BOOL tryCollect); -#endif static void _cache_print (Cache cache); static unsigned int log2 (unsigned int x); @@ -183,23 +304,20 @@ static int _class_uncache = 1; // caches are grown every time. static int _class_slow_grow = 1; -// Locks for cache access -#ifdef OBJC_COLLECTING_CACHE -// Held when adding an entry to the cache +// Lock for cache access. +// Held when modifying a cache in place. +// Held when installing a new cache on a class. +// Held when adding to the cache garbage list. +// Held when disposing cache garbage. +// See "Method cache locking" above for notes about cache locking. static OBJC_DECLARE_LOCK(cacheUpdateLock); -// Held when freeing memory from garbage -static OBJC_DECLARE_LOCK(cacheCollectionLock); -#endif - -// Held when looking in, adding to, or freeing the cache. -#ifdef OBJC_COLLECTING_CACHE -// For speed, messageLock is not held by the method dispatch code. -// Instead the cache freeing code checks thread PCs to ensure no -// one is dispatching. messageLock is held, though, during less -// time critical operations. -#endif -OBJC_DECLARE_LOCK(messageLock); +// classInitLock protects classInitWaitCond and examination and modification +// of CLS_INITIALIZED and CLS_INITIALIZING. +OBJC_DECLARE_LOCK(classInitLock); +// classInitWaitCond is signalled when any class is done initializing. +// Threads that are waiting for a class to finish initializing wait on this. +pthread_cond_t classInitWaitCond = PTHREAD_COND_INITIALIZER; CFMutableDictionaryRef _classIMPTables = NULL; @@ -207,11 +325,9 @@ CFMutableDictionaryRef _classIMPTables = NULL; // being encached is already there. The number of times it finds a match // is tallied in cacheFillDuplicates. When traceDuplicatesVerbose is // non-zero, each duplication is logged when found in this way. -#ifdef OBJC_COLLECTING_CACHE static int traceDuplicates = 0; static int traceDuplicatesVerbose = 0; static int cacheFillDuplicates = 0; -#endif #ifdef OBJC_INSTRUMENTED // Instrumentation @@ -604,8 +720,7 @@ Ivar class_getInstanceVariable (Class aClass, * * Specifying Nil for the class "all classes." **********************************************************************/ -static void flush_caches (Class cls, - BOOL flush_meta) +static void flush_caches(Class cls, BOOL flush_meta) { int numClasses = 0, newNumClasses; struct objc_class * * classes = NULL; @@ -617,6 +732,7 @@ static void flush_caches (Class cls, #endif // Do nothing if class has no cache + // This check is safe to do without any cache locks. if (cls && !((struct objc_class *) cls)->cache) return; @@ -628,6 +744,8 @@ static void flush_caches (Class cls, } numClasses = newNumClasses; + OBJC_LOCK(&cacheUpdateLock); + // Handle nil and root instance class specially: flush all // instance and class method caches. Nice that this // loop is linear vs the N-squared loop just below. @@ -651,19 +769,20 @@ static void flush_caches (Class cls, // (the isa pointer of any meta class points to the meta class // of the root). // NOTE: When is an isa pointer of a hash tabled class ever nil? - metaClsObject = ((struct objc_class *) clsObject)->isa; - if (cls && metaClsObject && (((struct objc_class *) metaClsObject)->isa != ((struct objc_class *) metaClsObject)->isa)) + metaClsObject = clsObject->isa; + if (cls && metaClsObject && cls->isa != metaClsObject->isa) + { continue; + } #ifdef OBJC_INSTRUMENTED subclassCount += 1; #endif - // Be careful of classes that do not yet have caches - if (((struct objc_class *) clsObject)->cache) - _cache_flush (clsObject); - if (flush_meta && metaClsObject && ((struct objc_class *) metaClsObject)->cache) - _cache_flush (((struct objc_class *) clsObject)->isa); + _cache_flush (clsObject); + if (flush_meta && metaClsObject != NULL) { + _cache_flush (metaClsObject); + } } #ifdef OBJC_INSTRUMENTED LinearFlushCachesVisitedCount += classesVisited; @@ -674,6 +793,7 @@ static void flush_caches (Class cls, MaxIdealFlushCachesCount = subclassCount; #endif + OBJC_UNLOCK(&cacheUpdateLock); free(classes); return; } @@ -753,14 +873,14 @@ static void flush_caches (Class cls, MaxIdealFlushCachesCount = subclassCount; #endif - // Relinquish access to class hash table + OBJC_UNLOCK(&cacheUpdateLock); free(classes); } /*********************************************************************** * _objc_flush_caches. Flush the caches of the specified class and any * of its subclasses. If cls is a meta-class, only meta-class (i.e. - * class method) caches are flushed. If cls is an instance-class, both +* class method) caches are flushed. If cls is an instance-class, both * instance-class and meta-class caches are flushed. **********************************************************************/ void _objc_flush_caches (Class cls) @@ -940,7 +1060,6 @@ Class class_poseAs (Class imposter, Class original) { struct objc_class * clsObject; - char imposterName[256]; char * imposterNamePtr; NXHashTable * class_hash; NXHashState state; @@ -955,21 +1074,20 @@ Class class_poseAs (Class imposter, // Imposter must be an immediate subclass of the original if (((struct objc_class *)imposter)->super_class != original) { - // radar 2203635 __objc_error(imposter, _errNotSuper, ((struct objc_class *)imposter)->name, ((struct objc_class *)original)->name); } // Can't pose when you have instance variables (how could it work?) if (((struct objc_class *)imposter)->ivars) { - // radar 2203635 __objc_error(imposter, _errNewVars, ((struct objc_class *)imposter)->name, ((struct objc_class *)original)->name, ((struct objc_class *)imposter)->name); } // Build a string to use to replace the name of the original class. - strcpy (imposterName, "_%"); - strcat (imposterName, ((struct objc_class *)original)->name); - imposterNamePtr = objc_malloc (strlen (imposterName)+1); - strcpy (imposterNamePtr, imposterName); + #define imposterNamePrefix "_%" + imposterNamePtr = malloc_zone_malloc(_objc_create_zone(), strlen(((struct objc_class *)original)->name) + strlen(imposterNamePrefix) + 1); + strcpy(imposterNamePtr, imposterNamePrefix); + strcat(imposterNamePtr, ((struct objc_class *)original)->name); + #undef imposterNamePrefix // We lock the class hashtable, so we are thread safe with respect to // calls to objc_getClass (). However, the class names are not @@ -1085,83 +1203,6 @@ static void _nonexistentHandler (id self, __objc_error (self, _errNonExistentObject, SELNAME(sel), self); } -/*********************************************************************** -* class_initialize. Send the '+initialize' message on demand to any -* uninitialized class. Force initialization of superclasses first. -* -* Called only from _class_lookupMethodAndLoadCache (or itself). -* -* #ifdef OBJC_COLLECTING_CACHE -* The messageLock can be in either state. -* #else -* The messageLock is already assumed to be taken out. -* It is temporarily released while the initialize method is sent. -* #endif -**********************************************************************/ -static void class_initialize (Class clsDesc) -{ - struct objc_class * super; - - // Skip if someone else beat us to it - if (ISINITIALIZED(((struct objc_class *)clsDesc))) - return; - - // Force initialization of superclasses first - super = ((struct objc_class *)clsDesc)->super_class; - if ((super != Nil) && (!ISINITIALIZED(super))) - class_initialize (super); - - // Initializing the super class might have initialized us, - // or another thread might have initialized us during this time. - if (ISINITIALIZED(((struct objc_class *)clsDesc))) - return; - - - // bind the module in - if it came from a bundle or dynamic library - if (((struct objc_class *)clsDesc)->info & CLS_NEED_BIND) { - ((struct objc_class *)clsDesc)->info &= ~CLS_NEED_BIND; - _objc_bindModuleContainingClass(clsDesc); - } - - // by loading things we might get initialized (maybe) ((paranoia)) - if (ISINITIALIZED(((struct objc_class *)clsDesc))) - return; - - // chain on the categories and bind them if necessary - _objc_resolve_categories_for_class(clsDesc); - - // by loading things we might get initialized (maybe) ((paranoia)) - if (ISINITIALIZED(((struct objc_class *)clsDesc))) - return; - - // Mark the class initialized so it can receive the "initialize" - // message. This solution to the catch-22 is the source of a - // bug: the class is able to receive messages *from anyone* now - // that it is marked, even though initialization is not complete. - - MARKINITIALIZED(((struct objc_class *)clsDesc)); - // But the simple solution is to ask if this class itself implements - // initialize (!) and only send it then!! - -#ifndef OBJC_COLLECTING_CACHE - // Release the message lock so that messages can be sent. - OBJC_UNLOCK(&messageLock); -#endif - - // Send the initialize method. - // Of course, if this class doesn't implement initialize but - // the super class does, we send initialize to the super class - // twice, thrice... - [(id)clsDesc initialize]; - -#ifndef OBJC_COLLECTING_CACHE - // Re-acquire the lock - OBJC_LOCK(&messageLock); -#endif - - return; -} - /*********************************************************************** * _class_install_relationships. Fill in the class pointers of a class * that was loaded before some or all of the classes it needs to point to. @@ -1241,21 +1282,6 @@ Error: _objc_fatal ("please link appropriate classes in your program"); } -/*********************************************************************** -* objc_malloc. -**********************************************************************/ -static void * objc_malloc (int byteCount) -{ - void * space; - - space = malloc_zone_malloc (_objc_create_zone (), byteCount); - if (!space && byteCount) - _objc_fatal ("unable to allocate space"); - - return space; -} - - /*********************************************************************** * class_respondsToMethod. * @@ -1264,60 +1290,31 @@ static void * objc_malloc (int byteCount) BOOL class_respondsToMethod (Class cls, SEL sel) { - struct objc_class * thisCls; - arith_t index; - arith_t mask; - Method * buckets; Method meth; + IMP imp; // No one responds to zero! if (!sel) return NO; - // Synchronize access to caches - OBJC_LOCK(&messageLock); - - // Look in the cache of the specified class - mask = ((struct objc_class *)cls)->cache->mask; - buckets = ((struct objc_class *)cls)->cache->buckets; - index = ((uarith_t) sel & mask); - while (CACHE_BUCKET_VALID(buckets[index])) { - if (CACHE_BUCKET_NAME(buckets[index]) == sel) { - if (CACHE_BUCKET_IMP(buckets[index]) == &_objc_msgForward) { - OBJC_UNLOCK(&messageLock); - return NO; - } else { - OBJC_UNLOCK(&messageLock); - return YES; - } - } - - index += 1; - index &= mask; + imp = _cache_getImp(cls, sel); + if (imp) { + // Found method in cache. + // If the cache entry is forward::, the class does not respond to sel. + return (imp != &_objc_msgForward); } // Handle cache miss meth = _getMethod(cls, sel); if (meth) { - OBJC_UNLOCK(&messageLock); - _cache_fill (cls, meth, sel); + _cache_fill(cls, meth, sel); return YES; } - // Not implememted. Use _objc_msgForward. - { - Method smt; - - smt = malloc_zone_malloc (_objc_create_zone(), sizeof(struct objc_method)); - smt->method_name = sel; - smt->method_types = ""; - smt->method_imp = &_objc_msgForward; - _cache_fill (cls, smt, sel); - } + // Not implemented. Use _objc_msgForward. + _cache_addForwardEntry(cls, sel); - OBJC_UNLOCK(&messageLock); return NO; - } @@ -1326,45 +1323,22 @@ BOOL class_respondsToMethod (Class cls, * * Called from -[Object methodFor:] and +[Object instanceMethodFor:] **********************************************************************/ - +// GrP is this used anymore? IMP class_lookupMethod (Class cls, SEL sel) { - Method * buckets; - arith_t index; - arith_t mask; - IMP result; + IMP imp; // No one responds to zero! if (!sel) { - // radar 2203635 __objc_error(cls, _errBadSel, sel); } - // Synchronize access to caches - OBJC_LOCK(&messageLock); - - // Scan the cache - mask = ((struct objc_class *)cls)->cache->mask; - buckets = ((struct objc_class *)cls)->cache->buckets; - index = ((unsigned int) sel & mask); - while (CACHE_BUCKET_VALID(buckets[index])) - { - if (CACHE_BUCKET_NAME(buckets[index]) == sel) - { - result = CACHE_BUCKET_IMP(buckets[index]); - OBJC_UNLOCK(&messageLock); - return result; - } - - index += 1; - index &= mask; - } + imp = _cache_getImp(cls, sel); + if (imp) return imp; // Handle cache miss - result = _class_lookupMethodAndLoadCache (cls, sel); - OBJC_UNLOCK(&messageLock); - return result; + return _class_lookupMethodAndLoadCache (cls, sel); } /*********************************************************************** @@ -1386,49 +1360,48 @@ IMP class_lookupNamedMethodInMethodList(struct objc_method_list *mlist, return (m ? m->method_imp : NULL); } + /*********************************************************************** -* _cache_create. +* _cache_malloc. * -* Called from _cache_expand () and objc_addClass () +* Called from _cache_create() and cache_expand() **********************************************************************/ -Cache _cache_create (Class cls) +static Cache _cache_malloc(int slotCount) { - Cache new_cache; - int slotCount; - int index; - - // Select appropriate size - slotCount = (ISMETA(cls)) ? INIT_META_CACHE_SIZE : INIT_CACHE_SIZE; + Cache new_cache; + size_t size; // Allocate table (why not check for failure?) + size = sizeof(struct objc_cache) + TABLE_SIZE(slotCount); #ifdef OBJC_INSTRUMENTED - new_cache = malloc_zone_malloc (_objc_create_zone(), - sizeof(struct objc_cache) + TABLE_SIZE(slotCount) - + sizeof(CacheInstrumentation)); -#else - new_cache = malloc_zone_malloc (_objc_create_zone(), - sizeof(struct objc_cache) + TABLE_SIZE(slotCount)); + size += sizeof(CacheInstrumentation); #endif + new_cache = malloc_zone_calloc (_objc_create_zone(), size, 1); - // Invalidate all the buckets - for (index = 0; index < slotCount; index += 1) - CACHE_BUCKET_VALID(new_cache->buckets[index]) = NULL; + // [c|v]allocated memory is zeroed, so all buckets are invalidated + // and occupied == 0 and all instrumentation is zero. - // Zero the valid-entry counter - new_cache->occupied = 0; - - // Set the mask so indexing wraps at the end-of-table new_cache->mask = slotCount - 1; -#ifdef OBJC_INSTRUMENTED - { - CacheInstrumentation * cacheData; + return new_cache; +} - // Zero out the cache dynamic instrumention data - cacheData = CACHE_INSTRUMENTATION(new_cache); - bzero ((char *) cacheData, sizeof(CacheInstrumentation)); - } -#endif + +/*********************************************************************** +* _cache_create. +* +* Called from _cache_expand(). +* Cache locks: cacheUpdateLock must be held by the caller. +**********************************************************************/ +Cache _cache_create (Class cls) +{ + Cache new_cache; + int slotCount; + + // Select appropriate size + slotCount = (ISMETA(cls)) ? INIT_META_CACHE_SIZE : INIT_CACHE_SIZE; + + new_cache = _cache_malloc(slotCount); // Install the cache ((struct objc_class *)cls)->cache = new_cache; @@ -1450,11 +1423,8 @@ Cache _cache_create (Class cls) /*********************************************************************** * _cache_expand. * -* #ifdef OBJC_COLLECTING_CACHE -* The cacheUpdateLock is assumed to be taken at this point. -* #endif -* * Called from _cache_fill () +* Cache locks: cacheUpdateLock must be held by the caller. **********************************************************************/ static Cache _cache_expand (Class cls) { @@ -1500,11 +1470,7 @@ static Cache _cache_expand (Class cls) // Deallocate "forward::" entry if (CACHE_BUCKET_IMP(oldEntry) == &_objc_msgForward) { -#ifdef OBJC_COLLECTING_CACHE _cache_collect_free (oldEntry, NO); -#else - malloc_zone_free (_objc_create_zone(), oldEntry); -#endif } } @@ -1520,21 +1486,7 @@ static Cache _cache_expand (Class cls) // Double the cache size slotCount = (old_cache->mask + 1) << 1; - // Allocate a new cache table -#ifdef OBJC_INSTRUMENTED - new_cache = malloc_zone_malloc (_objc_create_zone(), - sizeof(struct objc_cache) + TABLE_SIZE(slotCount) - + sizeof(CacheInstrumentation)); -#else - new_cache = malloc_zone_malloc (_objc_create_zone(), - sizeof(struct objc_cache) + TABLE_SIZE(slotCount)); -#endif - - // Zero out the new cache - new_cache->mask = slotCount - 1; - new_cache->occupied = 0; - for (index = 0; index < slotCount; index += 1) - CACHE_BUCKET_VALID(new_cache->buckets[index]) = NULL; + new_cache = _cache_malloc(slotCount); #ifdef OBJC_INSTRUMENTED // Propagate the instrumentation data @@ -1565,7 +1517,8 @@ static Cache _cache_expand (Class cls) continue; // Hash the old entry into the new table - index2 = ((unsigned int) CACHE_BUCKET_NAME(old_cache->buckets[index]) & newMask); + index2 = CACHE_HASH(CACHE_BUCKET_NAME(old_cache->buckets[index]), + newMask); // Find an available spot, at or following the hashed spot; // Guaranteed to not infinite loop, because table has grown @@ -1598,11 +1551,7 @@ static Cache _cache_expand (Class cls) if (CACHE_BUCKET_VALID(old_cache->buckets[index]) && CACHE_BUCKET_IMP(old_cache->buckets[index]) == &_objc_msgForward) { -#ifdef OBJC_COLLECTING_CACHE _cache_collect_free (old_cache->buckets[index], NO); -#else - malloc_zone_free (_objc_create_zone(), old_cache->buckets[index]); -#endif } } } @@ -1611,11 +1560,7 @@ static Cache _cache_expand (Class cls) ((struct objc_class *)cls)->cache = new_cache; // Deallocate old cache, try freeing all the garbage -#ifdef OBJC_COLLECTING_CACHE _cache_collect_free (old_cache, YES); -#else - malloc_zone_free (_objc_create_zone(), old_cache); -#endif return new_cache; } @@ -1632,12 +1577,12 @@ static int LogObjCMessageSend (BOOL isClassMethod, // Create/open the log file if (objcMsgLogFD == (-1)) { - sprintf (buf, "/tmp/msgSends-%d", (int) getpid ()); + snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ()); objcMsgLogFD = open (buf, O_WRONLY | O_CREAT, 0666); } // Make the log entry - sprintf(buf, "%c %s %s %s\n", + snprintf(buf, sizeof(buf), "%c %s %s %s\n", isClassMethod ? '+' : '-', objectsClass, implementingClass, @@ -1685,52 +1630,42 @@ void logObjcMessageSends (ObjCLogProc logProc) fsync (objcMsgLogFD); } + /*********************************************************************** * _cache_fill. Add the specified method to the specified class' cache. +* Returns NO if the cache entry wasn't added: cache was busy, +* class is still being initialized, new entry is a duplicate. * * Called only from _class_lookupMethodAndLoadCache and -* class_respondsToMethod. +* class_respondsToMethod and _cache_addForwardEntry. * -* #ifdef OBJC_COLLECTING_CACHE -* It doesn't matter if someone has the messageLock when we enter this -* function. This function will fail to do the update if someone else -* is already updating the cache, i.e. they have the cacheUpdateLock. -* #else -* The messageLock is already assumed to be taken out. -* #endif +* Cache locks: cacheUpdateLock must not be held. **********************************************************************/ - -static void _cache_fill (Class cls, - Method smt, - SEL sel) +static BOOL _cache_fill(Class cls, Method smt, SEL sel) { - Cache cache; - Method * buckets; - - arith_t index; - arith_t mask; unsigned int newOccupied; + arith_t index; + Method *buckets; + Cache cache; + + // Never cache before +initialize is done + if (!ISINITIALIZED(cls)) { + return NO; + } // Keep tally of cache additions totalCacheFills += 1; -#ifdef OBJC_COLLECTING_CACHE - // Make sure only one thread is updating the cache at a time, but don't - // wait for concurrent updater to finish, because it might be a while, or - // a deadlock! Instead, just leave the method out of the cache until - // next time. This is nasty given that cacheUpdateLock is per task! - if (!OBJC_TRYLOCK(&cacheUpdateLock)) - return; + OBJC_LOCK(&cacheUpdateLock); - // Set up invariants for cache traversals - cache = ((struct objc_class *)cls)->cache; - mask = cache->mask; - buckets = cache->buckets; + cache = ((struct objc_class *)cls)->cache; // Check for duplicate entries, if we're in the mode if (traceDuplicates) { int index2; + arith_t mask = cache->mask; + buckets = cache->buckets; // Scan the cache for (index2 = 0; index2 < mask + 1; index2 += 1) @@ -1753,44 +1688,26 @@ static void _cache_fill (Class cls, } } - // Do nothing if entry is already placed. This re-check is needed - // only in the OBJC_COLLECTING_CACHE code, because the probe is - // done un-sync'd. - index = ((unsigned int) sel & mask); - while (CACHE_BUCKET_VALID(buckets[index])) - { - if (CACHE_BUCKET_NAME(buckets[index]) == sel) - { - OBJC_UNLOCK(&cacheUpdateLock); - return; - } - - index += 1; - index &= mask; + // Make sure the entry wasn't added to the cache by some other thread + // before we grabbed the cacheUpdateLock. + // Don't use _cache_getMethod() because _cache_getMethod() doesn't + // return forward:: entries. + if (_cache_getImp(cls, sel)) { + OBJC_UNLOCK(&cacheUpdateLock); + return NO; // entry is already cached, didn't add new one } -#else // not OBJC_COLLECTING_CACHE - cache = ((struct objc_class *)cls)->cache; - mask = cache->mask; -#endif - // Use the cache as-is if it is less than 3/4 full newOccupied = cache->occupied + 1; - if ((newOccupied * 4) <= (mask + 1) * 3) + if ((newOccupied * 4) <= (cache->mask + 1) * 3) { + // Cache is less than 3/4 full. cache->occupied = newOccupied; - - // Cache is getting full - else - { - // Flush the cache - if ((((struct objc_class * )cls)->info & CLS_FLUSH_CACHE) != 0) + } else { + // Cache is too full. Flush it or expand it. + if ((((struct objc_class * )cls)->info & CLS_FLUSH_CACHE) != 0) { _cache_flush (cls); - - // Expand the cache - else - { + } else { cache = _cache_expand (cls); - mask = cache->mask; } // Account for the addition @@ -1809,7 +1726,7 @@ static void _cache_fill (Class cls, // are two kinds of entries, so there have to be two ways // to slide them. buckets = cache->buckets; - index = ((unsigned int) sel & mask); + index = CACHE_HASH(sel, cache->mask); for (;;) { // Slide existing entries down by one @@ -1830,19 +1747,43 @@ static void _cache_fill (Class cls, // Move on to next slot index += 1; - index &= mask; + index &= cache->mask; } -#ifdef OBJC_COLLECTING_CACHE OBJC_UNLOCK(&cacheUpdateLock); -#endif + + return YES; // successfully added new cache entry } + +/*********************************************************************** +* _cache_addForwardEntry +* Add a forward:: entry for the given selector to cls's method cache. +* Does nothing if the cache addition fails for any reason. +* Called from class_respondsToMethod and _class_lookupMethodAndLoadCache. +* Cache locks: cacheUpdateLock must not be held. +**********************************************************************/ +static void _cache_addForwardEntry(Class cls, SEL sel) +{ + Method smt; + + smt = malloc_zone_malloc(_objc_create_zone(), sizeof(struct objc_method)); + smt->method_name = sel; + smt->method_types = ""; + smt->method_imp = &_objc_msgForward; + if (! _cache_fill(cls, smt, sel)) { + // Entry not added to cache. Don't leak the method struct. + malloc_zone_free(_objc_create_zone(), smt); + } +} + + /*********************************************************************** * _cache_flush. Invalidate all valid entries in the given class' cache, * and clear the CLS_FLUSH_CACHE in the cls->info. * -* Called from flush_caches (). +* Called from flush_caches() and _cache_fill() +* Cache locks: cacheUpdateLock must be held by the caller. **********************************************************************/ static void _cache_flush (Class cls) { @@ -1851,7 +1792,7 @@ static void _cache_flush (Class cls) // Locate cache. Ignore unused cache. cache = ((struct objc_class *)cls)->cache; - if (cache == &emptyCache) + if (cache == NULL || cache == &emptyCache) return; #ifdef OBJC_INSTRUMENTED @@ -1879,11 +1820,7 @@ static void _cache_flush (Class cls) // Deallocate "forward::" entry if (oldEntry && oldEntry->method_imp == &_objc_msgForward) -#ifdef OBJC_COLLECTING_CACHE _cache_collect_free (oldEntry, NO); -#else - malloc_zone_free (_objc_create_zone(), oldEntry); -#endif } // Clear the valid-entry counter @@ -1916,6 +1853,267 @@ Class _objc_getNonexistentClass (void) return (Class) &nonexistentObjectClass; } + +/*********************************************************************** +* struct _objc_initializing_classes +* Per-thread list of classes currently being initialized by that thread. +* During initialization, that thread is allowed to send messages to that +* class, but other threads have to wait. +* The list is a simple array of metaclasses (the metaclass stores +* the initialization state). +**********************************************************************/ +typedef struct _objc_initializing_classes { + int classesAllocated; + struct objc_class** metaclasses; +} _objc_initializing_classes; + + +/*********************************************************************** +* _fetchInitializingClassList +* Return the list of classes being initialized by this thread. +* If create == YES, create the list when no classes are being initialized by this thread. +* If create == NO, return NULL when no classes are being initialized by this thread. +**********************************************************************/ +static _objc_initializing_classes *_fetchInitializingClassList(BOOL create) +{ + _objc_pthread_data *data; + _objc_initializing_classes *list; + struct objc_class **classes; + + data = pthread_getspecific(_objc_pthread_key); + if (data == NULL) { + if (!create) { + return NULL; + } else { + data = calloc(1, sizeof(_objc_pthread_data)); + pthread_setspecific(_objc_pthread_key, data); + } + } + + list = data->initializingClasses; + if (list == NULL) { + if (!create) { + return NULL; + } else { + list = calloc(1, sizeof(_objc_initializing_classes)); + data->initializingClasses = list; + } + } + + classes = list->metaclasses; + if (classes == NULL) { + // If _objc_initializing_classes exists, allocate metaclass array, + // even if create == NO. + // Allow 4 simultaneous class inits on this thread before realloc. + list->classesAllocated = 4; + classes = calloc(list->classesAllocated, sizeof(struct objc_class *)); + list->metaclasses = classes; + } + return list; +} + + +/*********************************************************************** +* _destroyInitializingClassList +* Deallocate memory used by the given initialization list. +* Any part of the list may be NULL. +* Called from _objc_pthread_destroyspecific(). +**********************************************************************/ +void _destroyInitializingClassList(_objc_initializing_classes *list) +{ + if (list != NULL) { + if (list->metaclasses != NULL) { + free(list->metaclasses); + } + free(list); + } +} + + +/*********************************************************************** +* _thisThreadIsInitializingClass +* Return TRUE if this thread is currently initializing the given class. +**********************************************************************/ +static BOOL _thisThreadIsInitializingClass(struct objc_class *cls) +{ + int i; + + _objc_initializing_classes *list = _fetchInitializingClassList(NO); + if (list) { + cls = GETMETA(cls); + for (i = 0; i < list->classesAllocated; i++) { + if (cls == list->metaclasses[i]) return YES; + } + } + + // no list or not found in list + return NO; +} + + +/*********************************************************************** +* _setThisThreadIsInitializingClass +* Record that this thread is currently initializing the given class. +* This thread will be allowed to send messages to the class, but +* other threads will have to wait. +**********************************************************************/ +static void _setThisThreadIsInitializingClass(struct objc_class *cls) +{ + int i; + _objc_initializing_classes *list = _fetchInitializingClassList(YES); + cls = GETMETA(cls); + + // paranoia: explicitly disallow duplicates + for (i = 0; i < list->classesAllocated; i++) { + if (cls == list->metaclasses[i]) { + _objc_fatal("thread is already initializing this class!"); + return; // already the initializer + } + } + + for (i = 0; i < list->classesAllocated; i++) { + if (0 == list->metaclasses[i]) { + list->metaclasses[i] = cls; + return; + } + } + + // class list is full - reallocate + list->classesAllocated = list->classesAllocated * 2 + 1; + list->metaclasses = realloc(list->metaclasses, list->classesAllocated * sizeof(struct objc_class *)); + // zero out the new entries + list->metaclasses[i++] = cls; + for ( ; i < list->classesAllocated; i++) { + list->metaclasses[i] = NULL; + } +} + + +/*********************************************************************** +* _setThisThreadIsNotInitializingClass +* Record that this thread is no longer initializing the given class. +**********************************************************************/ +static void _setThisThreadIsNotInitializingClass(struct objc_class *cls) +{ + int i; + + _objc_initializing_classes *list = _fetchInitializingClassList(NO); + if (list) { + cls = GETMETA(cls); + for (i = 0; i < list->classesAllocated; i++) { + if (cls == list->metaclasses[i]) { + list->metaclasses[i] = NULL; + return; + } + } + } + + // no list or not found in list + _objc_fatal("thread is not initializing this class!"); +} + + +/*********************************************************************** +* class_initialize. Send the '+initialize' message on demand to any +* uninitialized class. Force initialization of superclasses first. +* +* Called only from _class_lookupMethodAndLoadCache (or itself). +**********************************************************************/ +static void class_initialize(struct objc_class *cls) +{ + long *infoP = &GETMETA(cls)->info; + BOOL reallyInitialize = NO; + + // Get the real class from the metaclass. The superclass chain + // hangs off the real class only. + if (ISMETA(cls)) { + if (strncmp(cls->name, "_%", 2) == 0) { + // Posee's meta's name is smashed and isn't in the class_hash, + // so objc_getClass doesn't work. + char *baseName = strchr(cls->name, '%'); // get posee's real name + cls = objc_getClass(baseName); + } else { + cls = objc_getClass(cls->name); + } + } + + // Make sure super is done initializing BEFORE beginning to initialize cls. + // See note about deadlock above. + if (cls->super_class && !ISINITIALIZED(cls->super_class)) { + class_initialize(cls->super_class); + } + + // Try to atomically set CLS_INITIALIZING. + pthread_mutex_lock(&classInitLock); + if (!ISINITIALIZED(cls) && !ISINITIALIZING(cls)) { + *infoP |= CLS_INITIALIZING; + reallyInitialize = YES; + } + pthread_mutex_unlock(&classInitLock); + + if (reallyInitialize) { + // We successfully set the CLS_INITIALIZING bit. Initialize the class. + + // Record that we're initializing this class so we can message it. + _setThisThreadIsInitializingClass(cls); + + // bind the module in - if it came from a bundle or dynamic library + _objc_bindClassIfNeeded(cls); + + // chain on the categories and bind them if necessary + _objc_resolve_categories_for_class(cls); + + // Send the +initialize message. + // Note that +initialize is sent to the superclass (again) if + // this class doesn't implement +initialize. 2157218 + [(id)cls initialize]; + + // Done initializing. Update the info bits and notify waiting threads. + pthread_mutex_lock(&classInitLock); + *infoP = (*infoP | CLS_INITIALIZED) & ~CLS_INITIALIZING; + pthread_cond_broadcast(&classInitWaitCond); + pthread_mutex_unlock(&classInitLock); + _setThisThreadIsNotInitializingClass(cls); + return; + } + + else if (ISINITIALIZING(cls)) { + // We couldn't set INITIALIZING because INITIALIZING was already set. + // If this thread set it earlier, continue normally. + // If some other thread set it, block until initialize is done. + // It's ok if INITIALIZING changes to INITIALIZED while we're here, + // because we safely check for INITIALIZED inside the lock + // before blocking. + if (_thisThreadIsInitializingClass(cls)) { + return; + } else { + pthread_mutex_lock(&classInitLock); + while (!ISINITIALIZED(cls)) { + pthread_cond_wait(&classInitWaitCond, &classInitLock); + } + pthread_mutex_unlock(&classInitLock); + return; + } + } + + else if (ISINITIALIZED(cls)) { + // Set CLS_INITIALIZING failed because someone else already + // initialized the class. Continue normally. + // NOTE this check must come AFTER the ISINITIALIZING case. + // Otherwise: Another thread is initializing this class. ISINITIALIZED + // is false. Skip this clause. Then the other thread finishes + // initialization and sets INITIALIZING=no and INITIALIZED=yes. + // Skip the ISINITIALIZING clause. Die horribly. + return; + } + + else { + // We shouldn't be here. + _objc_fatal("thread-safe class init in objc runtime is buggy!"); + } +} + + /*********************************************************************** * _class_lookupMethodAndLoadCache. * @@ -1925,9 +2123,8 @@ IMP _class_lookupMethodAndLoadCache (Class cls, SEL sel) { struct objc_class * curClass; - Method smt; - BOOL calledSingleThreaded; - IMP methodPC; + Method meth; + IMP methodPC = NULL; trace(0xb300, 0, 0, 0); @@ -1939,119 +2136,80 @@ IMP _class_lookupMethodAndLoadCache (Class cls, if (cls == &nonexistentObjectClass) return (IMP) _nonexistentHandler; -#ifndef OBJC_COLLECTING_CACHE - // Control can get here via the single-threaded message dispatcher, - // but class_initialize can cause application to go multithreaded. Notice - // whether this is the case, so we can leave the messageLock unlocked - // on the way out, just as the single-threaded message dispatcher - // expects. Note that the messageLock locking in classinitialize is - // appropriate in this case, because there are more than one thread now. - calledSingleThreaded = (_objc_multithread_mask != 0); -#endif - trace(0xb301, 0, 0, 0); - // Lazy initialization. This unlocks and relocks messageLock, - // so cache information we might already have becomes invalid. - if (!ISINITIALIZED(cls)) - class_initialize (objc_getClass (((struct objc_class *)cls)->name)); + if (!ISINITIALIZED(cls)) { + class_initialize ((struct objc_class *)cls); + // If sel == initialize, class_initialize will send +initialize and + // then the messenger will send +initialize again after this + // procedure finishes. Of course, if this is not being called + // from the messenger then it won't happen. 2778172 + } trace(0xb302, 0, 0, 0); // Outer loop - search the caches and method lists of the // class and its super-classes - methodPC = NULL; for (curClass = cls; curClass; curClass = ((struct objc_class * )curClass)->super_class) { - Method * buckets; - arith_t idx; - arith_t mask; - arith_t methodCount; - struct objc_method_list *mlist; - void *iterator = 0; #ifdef PRELOAD_SUPERCLASS_CACHES - struct objc_class * curClass2; + struct objc_class *curClass2; #endif trace(0xb303, 0, 0, 0); - mask = curClass->cache->mask; - buckets = curClass->cache->buckets; - - // Minor loop #1 - check cache of given class - for (idx = ((uarith_t) sel & mask); - CACHE_BUCKET_VALID(buckets[idx]); - idx = (++idx & mask)) - { - // Skip entries until selector matches - if (CACHE_BUCKET_NAME(buckets[idx]) != sel) - continue; - - // Found the method. Add it to the cache(s) - // unless it was found in the cache of the - // class originally being messaged. - // - // NOTE: The method is usually not found - // the original class' cache, because - // objc_msgSend () has already looked. - // BUT, if sending this method resulted in - // a +initialize on the class, and +initialize - // sends the same method, the method will - // indeed now be in the cache. Calling - // _cache_fill with a buckets[idx] from the - // cache being filled results in a crash - // if the cache has to grow, because the - // buckets[idx] address is no longer valid. - if (curClass != cls) - { + // Beware of thread-unsafety and double-freeing of forward:: + // entries here! See note in "Method cache locking" above. + // The upshot is that _cache_getMethod() will return NULL + // instead of returning a forward:: entry. + meth = _cache_getMethod(curClass, sel); + if (meth) { + // Found the method in this class or a superclass. + // Cache the method in this class, unless we just found it in + // this class's cache. + if (curClass != cls) { #ifdef PRELOAD_SUPERCLASS_CACHES for (curClass2 = cls; curClass2 != curClass; curClass2 = curClass2->super_class) - _cache_fill (curClass2, buckets[idx], sel); - _cache_fill (curClass, buckets[idx], sel); + _cache_fill (curClass2, meth, sel); + _cache_fill (curClass, meth, sel); #else - _cache_fill (cls, buckets[idx], sel); + _cache_fill (cls, meth, sel); #endif } - // Return the implementation address - methodPC = CACHE_BUCKET_IMP(buckets[idx]); + methodPC = meth->method_imp; break; } trace(0xb304, (int)methodPC, 0, 0); - // Done if that found it - if (methodPC) - break; - - smt = _findMethodInClass(curClass, sel); + // Cache scan failed. Search method list. - if (smt) { + meth = _findMethodInClass(curClass, sel); + if (meth) { // If logging is enabled, log the message send and let // the logger decide whether to encache the method. if ((objcMsgLogEnabled == 0) || - (objcMsgLogProc (CLS_GETINFO(((struct objc_class * )curClass),CLS_META) ? YES : NO, + (objcMsgLogProc (CLS_GETINFO(((struct objc_class * )curClass), + CLS_META) ? YES : NO, ((struct objc_class *)cls)->name, curClass->name, sel))) { // Cache the method implementation #ifdef PRELOAD_SUPERCLASS_CACHES for (curClass2 = cls; curClass2 != curClass; curClass2 = curClass2->super_class) - _cache_fill (curClass2, smt, sel); - _cache_fill (curClass, smt, sel); + _cache_fill (curClass2, meth, sel); + _cache_fill (curClass, meth, sel); #else - _cache_fill (cls, smt, sel); + _cache_fill (cls, meth, sel); #endif } - // Return the implementation - methodPC = smt->method_imp; + + methodPC = meth->method_imp; + break; } trace(0xb305, (int)methodPC, 0, 0); - - // Done if that found it - if (methodPC) - break; } trace(0xb306, (int)methodPC, 0, 0); @@ -2059,25 +2217,16 @@ IMP _class_lookupMethodAndLoadCache (Class cls, if (methodPC == NULL) { // Class and superclasses do not respond -- use forwarding - smt = malloc_zone_malloc (_objc_create_zone(), sizeof(struct objc_method)); - smt->method_name = sel; - smt->method_types = ""; - smt->method_imp = &_objc_msgForward; - _cache_fill (cls, smt, sel); + _cache_addForwardEntry(cls, sel); methodPC = &_objc_msgForward; } -#ifndef OBJC_COLLECTING_CACHE - // Unlock the lock - if (calledSingleThreaded) - OBJC_UNLOCK(&messageLock); -#endif - trace(0xb30f, (int)methodPC, 0, 0); return methodPC; } + /*********************************************************************** * SubtypeUntil. * @@ -2170,6 +2319,9 @@ unsigned method_getNumberOfArguments (Method method) // Traverse argument type typedesc = SkipFirstType (typedesc); + // Skip GNU runtime's register parameter hint + if (*typedesc == '+') typedesc++; + // Traverse (possibly negative) argument offset if (*typedesc == '-') typedesc += 1; @@ -2231,6 +2383,9 @@ unsigned method_getSizeOfArguments (Method method) // skip the '@' marking the Id field typedesc = SkipFirstType (typedesc); + // Skip GNU runtime's register parameter hint + if (*typedesc == '+') typedesc++; + // pick up the offset for the Id field foundBaseOffset = 0; while ((*typedesc >= '0') && (*typedesc <= '9')) @@ -2303,6 +2458,9 @@ unsigned method_getArgumentInfo (Method method, if (nargs == 0) { + // Skip GNU runtime's register parameter hint + if (*typedesc == '+') typedesc++; + // Skip negative sign in offset if (*typedesc == '-') { @@ -2321,6 +2479,9 @@ unsigned method_getArgumentInfo (Method method, else { + // Skip GNU runtime's register parameter hint + if (*typedesc == '+') typedesc++; + // Skip (possibly negative) argument offset if (*typedesc == '-') typedesc += 1; @@ -2349,6 +2510,9 @@ unsigned method_getArgumentInfo (Method method, else { + // Skip GNU register parameter hint + if (*typedesc == '+') typedesc++; + // Pick up (possibly negative) argument offset if (*typedesc == '-') { @@ -2408,7 +2572,6 @@ void * _objc_create_zone (void) /*********************************************************************** * cache collection. **********************************************************************/ -#ifdef OBJC_COLLECTING_CACHE static unsigned long _get_pc_for_thread (mach_port_t thread) #ifdef hppa @@ -2454,6 +2617,10 @@ static unsigned long _get_pc_for_thread (mach_port_t thread) /*********************************************************************** * _collecting_in_critical. +* Returns TRUE if some thread is currently executing a cache-reading +* function. Collection of cache garbage is not allowed when a cache- +* reading function is in progress because it might still be using +* the garbage memory. **********************************************************************/ OBJC_EXPORT unsigned long objc_entryPoints[]; OBJC_EXPORT unsigned long objc_exitPoints[]; @@ -2465,22 +2632,23 @@ static int _collecting_in_critical (void) unsigned count; kern_return_t ret; int result; + mach_port_t mythread = pthread_mach_thread_np(pthread_self()); // Get a list of all the threads in the current task ret = task_threads (mach_task_self (), &threads, &number); if (ret != KERN_SUCCESS) { - _objc_inform ("objc: task_thread failed\n"); + _objc_inform ("task_thread failed (result %d)\n", ret); exit (1); } // Check whether any thread is in the cache lookup code - result = 0; - for (count = 0; !result && (count < number); count += 1) + result = FALSE; + for (count = 0; count < number; count++) { - int region; - unsigned long pc; + int region; + unsigned long pc; // Don't bother checking ourselves if (threads[count] == mythread) @@ -2490,13 +2658,18 @@ static int _collecting_in_critical (void) pc = _get_pc_for_thread (threads[count]); // Check whether it is in the cache lookup code - for (region = 0; !result && (objc_entryPoints[region] != 0); region += 1) + for (region = 0; objc_entryPoints[region] != 0; region++) { if ((pc >= objc_entryPoints[region]) && - (pc <= objc_exitPoints[region])) - result = 1; + (pc <= objc_exitPoints[region])) + { + result = TRUE; + goto done; + } } } + + done: // Deallocate the port rights for the threads for (count = 0; count < number; count++) { mach_port_deallocate(mach_task_self (), threads[count]); @@ -2562,6 +2735,7 @@ static void _garbage_make_room (void) /*********************************************************************** * _cache_collect_free. Add the specified malloc'd memory to the list * of them to free at some later point. +* Cache locks: cacheUpdateLock must be held by the caller. **********************************************************************/ static void _cache_collect_free (void * data, BOOL tryCollect) @@ -2573,9 +2747,6 @@ static void _cache_collect_free (void * data, report_garbage = getenv ("OBJC_REPORT_GARBAGE"); } - // Synchronize - OBJC_LOCK(&cacheCollectionLock); - // Insert new element in garbage list // Note that we do this even if we end up free'ing everything _garbage_make_room (); @@ -2589,45 +2760,37 @@ static void _cache_collect_free (void * data, // Done if caller says not to empty or the garbage is not full if (!tryCollect || (garbage_byte_size < garbage_threshold)) { - OBJC_UNLOCK(&cacheCollectionLock); if (tryCollect && report_garbage) - _objc_inform ("below threshold\n"); + _objc_inform ("couldn't collect cache garbage: below threshold\n"); return; } - // Synchronize garbage collection with messageLock holders - if (OBJC_TRYLOCK(&messageLock)) - { - // Synchronize garbage collection with cache lookers - if (!_collecting_in_critical ()) - { - // Log our progress - if (tryCollect && report_garbage) - _objc_inform ("collecting!\n"); - - // Dispose all refs now in the garbage - while (garbage_count) - free (garbage_refs[--garbage_count]); - - // Clear the total size indicator - garbage_byte_size = 0; + // tryCollect is guaranteed to be true after this point + + // Synchronize garbage collection with objc_msgSend and other cache readers + if (!_collecting_in_critical ()) { + // No cache readers in progress - garbage is now deletable + + // Log our progress + if (report_garbage) + _objc_inform ("collecting!\n"); + + // Dispose all refs now in the garbage + while (garbage_count) + free (garbage_refs[--garbage_count]); + + // Clear the total size indicator + garbage_byte_size = 0; + } + else { + // objc_msgSend (or other cache reader) is currently looking in the + // cache and might still be using some garbage. + if (report_garbage) { + _objc_inform ("couldn't collect cache garbage: objc_msgSend in progress\n"); } - - // Someone is actively looking in the cache - else if (tryCollect && report_garbage) - _objc_inform ("in critical region\n"); - - OBJC_UNLOCK(&messageLock); } - - // Someone already holds messageLock - else if (tryCollect && report_garbage) - _objc_inform ("messageLock taken\n"); - - OBJC_UNLOCK(&cacheCollectionLock); } -#endif // OBJC_COLLECTING_CACHE /*********************************************************************** @@ -2943,8 +3106,10 @@ void _class_printMethodCacheStatistics (void) negativeEntryCount += 1; // Calculate search distance (chain length) for this method - hash = (uarith_t) CACHE_BUCKET_NAME(method); - methodChain = ((index - hash) & mask); + // The chain may wrap around to the beginning of the table. + hash = CACHE_HASH(CACHE_BUCKET_NAME(method), mask); + if (index >= hash) methodChain = index - hash; + else methodChain = (mask+1) + index - hash; // Tally chains of this length if (methodChain < MAX_CHAIN_SIZE) diff --git a/runtime/objc-config.h b/runtime/objc-config.h index 9b0b334..82c8975 100644 --- a/runtime/objc-config.h +++ b/runtime/objc-config.h @@ -32,26 +32,11 @@ // because objc-class.h is public and objc-config.h is not. //#define OBJC_INSTRUMENTED -// OBJC_COLLECTING_CACHE controls whether the method dispatching caches -// are lockless during dispatch. This is a BIG speed win, but can be -// implemented only when a thread can figure out the PCs of all the other -// threads in the task. - -#if defined(hppa) || defined (__i386__) || defined (i386) || defined (m68k) || defined (__ppc__) || defined(ppc) - #if !defined(NeXT_PDO) - #define OBJC_COLLECTING_CACHE - #endif -#endif - // Turn on support for class refs #define OBJC_CLASS_REFS #define __S(x) x -#if defined(NeXT_PDO) - #define GENERIC_OBJC_FILE -#endif - // Get the nice macros for subroutine calling, etc. // Not available on all architectures. Not needed // (by us) on some configurations. @@ -59,6 +44,6 @@ #import #elif defined (__ppc__) || defined(ppc) #import -#elif (!defined(hppa) && !defined(sparc)) || !defined(NeXT_PDO) +#else #error We need asm_help.h for this architecture #endif diff --git a/runtime/objc-errors.m b/runtime/objc-errors.m index 3b5d34b..e45722a 100644 --- a/runtime/objc-errors.m +++ b/runtime/objc-errors.m @@ -55,7 +55,7 @@ void _objc_syslog(const char *format, ...) char bigBuffer[4*1024]; va_start(ap, format); - vsprintf(bigBuffer, format, ap); + vsnprintf(bigBuffer, sizeof(bigBuffer), format, ap); va_end(ap); @@ -91,7 +91,7 @@ volatile void _objc_error(id self, const char *fmt, va_list ap) { char bigBuffer[4*1024]; - vsprintf (bigBuffer, fmt, ap); + vsnprintf (bigBuffer, sizeof(bigBuffer), fmt, ap); _objc_syslog ("objc: %s: %s", object_getClassName (self), bigBuffer); abort(); /* generates a core file */ @@ -117,7 +117,7 @@ void _objc_inform(const char *fmt, ...) char bigBuffer[4*1024]; va_start (ap,fmt); - vsprintf (bigBuffer, fmt, ap); + vsnprintf (bigBuffer, sizeof(bigBuffer), fmt, ap); _objc_syslog ("objc: %s", bigBuffer); va_end (ap); } diff --git a/runtime/objc-private.h b/runtime/objc-private.h index a178b19..9e90500 100644 --- a/runtime/objc-private.h +++ b/runtime/objc-private.h @@ -64,12 +64,8 @@ * * had been: typedef void *objc_header; */ -#if defined(NeXT_PDO) - typedef void headerType; -#else - #import - typedef struct mach_header headerType; -#endif +#import +typedef struct mach_header headerType; #import @@ -80,17 +76,10 @@ typedef struct _NXConstantStringTemplate { unsigned int _length; } NXConstantStringTemplate; -#if defined(NeXT_PDO) - #define OBJC_CONSTANT_STRING_PTR NXConstantStringTemplate** - #define OBJC_CONSTANT_STRING_DEREF - #define OBJC_PROTOCOL_PTR ProtocolTemplate** - #define OBJC_PROTOCOL_DEREF -> -#elif defined(__MACH__) - #define OBJC_CONSTANT_STRING_PTR NXConstantStringTemplate* - #define OBJC_CONSTANT_STRING_DEREF & - #define OBJC_PROTOCOL_PTR ProtocolTemplate* - #define OBJC_PROTOCOL_DEREF . -#endif +#define OBJC_CONSTANT_STRING_PTR NXConstantStringTemplate* +#define OBJC_CONSTANT_STRING_DEREF & +#define OBJC_PROTOCOL_PTR ProtocolTemplate* +#define OBJC_PROTOCOL_DEREF . // both OBJC_EXPORT headerType ** _getObjcHeaders(); @@ -101,6 +90,7 @@ OBJC_EXPORT const char * _getObjcHeaderName(headerType *head); // internal routines for delaying binding void _objc_resolve_categories_for_class (struct objc_class * cls); +void _objc_bindClassIfNeeded(struct objc_class *cls); void _objc_bindModuleContainingClass(struct objc_class * cls); // someday a logging facility @@ -198,8 +188,10 @@ OBJC_EXPORT SEL * _getObjcMessageRefs(headerType *head, int *nmess); OBJC_EXPORT void _objc_insertMethods( struct objc_method_list *mlist, struct objc_method_list ***list ); OBJC_EXPORT void _objc_removeMethods( struct objc_method_list *mlist, struct objc_method_list ***list ); + OBJC_EXPORT IMP _cache_getImp(Class cls, SEL sel); + OBJC_EXPORT Method _cache_getMethod(Class cls, SEL sel); + /* message dispatcher */ - OBJC_EXPORT Cache _cache_create(Class); OBJC_EXPORT IMP _class_lookupMethodAndLoadCache(Class, SEL); OBJC_EXPORT id _objc_msgForward (id self, SEL sel, ...); @@ -219,9 +211,6 @@ OBJC_EXPORT SEL * _getObjcMessageRefs(headerType *head, int *nmess); #define MUTEX_TYPE pthread_mutex_t* #define OBJC_DECLARE_LOCK(MTX) pthread_mutex_t MTX = PTHREAD_MUTEX_INITIALIZER OBJC_EXPORT pthread_mutex_t classLock; - OBJC_EXPORT pthread_mutex_t messageLock; - - OBJC_EXPORT int _objc_multithread_mask; // _objc_msgNil is actually (unsigned dummy, id, SEL) for i386; // currently not implemented for any sparc or hppa platforms @@ -236,27 +225,9 @@ OBJC_EXPORT SEL * _getObjcMessageRefs(headerType *head, int *nmess); return (s1 == s2); } - #if defined(OBJC_COLLECTING_CACHE) - #define OBJC_LOCK(MUTEX) mutex_lock (MUTEX) - #define OBJC_UNLOCK(MUTEX) mutex_unlock (MUTEX) - #define OBJC_TRYLOCK(MUTEX) mutex_try_lock (MUTEX) - #else // not OBJC_COLLECTING_CACHE - #define OBJC_LOCK(MUTEX) \ - do \ - { \ - if (!_objc_multithread_mask) \ - mutex_lock (MUTEX); \ - } \ - while (0) - - #define OBJC_UNLOCK(MUTEX) \ - do \ - { \ - if (!_objc_multithread_mask) \ - mutex_unlock (MUTEX); \ - } \ - while (0) - #endif /* OBJC_COLLECTING_CACHE */ + #define OBJC_LOCK(MUTEX) mutex_lock (MUTEX) + #define OBJC_UNLOCK(MUTEX) mutex_unlock (MUTEX) + #define OBJC_TRYLOCK(MUTEX) mutex_try_lock (MUTEX) #if !defined(SEG_OBJC) #define SEG_OBJC "__OBJC" /* objective-C runtime segment */ @@ -286,5 +257,25 @@ static __inline__ unsigned int _objc_strhash(const unsigned char *s) { return hash; } + +// objc per-thread storage +OBJC_EXPORT pthread_key_t _objc_pthread_key; +typedef struct { + struct _objc_initializing_classes *initializingClasses; // for +initialize + + // If you add new fields here, don't forget to update + // _objc_pthread_destroyspecific() + +} _objc_pthread_data; + + +// Class state +#define ISCLASS(cls) ((((struct objc_class *) cls)->info & CLS_CLASS) != 0) +#define ISMETA(cls) ((((struct objc_class *) cls)->info & CLS_META) != 0) +#define GETMETA(cls) (ISMETA(cls) ? ((struct objc_class *) cls) : ((struct objc_class *) cls)->isa) +#define ISINITIALIZED(cls) ((((volatile long)GETMETA(cls)->info) & CLS_INITIALIZED) != 0) +#define ISINITIALIZING(cls) ((((volatile long)GETMETA(cls)->info) & CLS_INITIALIZING) != 0) + + #endif /* _OBJC_PRIVATE_H_ */ diff --git a/runtime/objc-runtime.h b/runtime/objc-runtime.h index 1b94c3b..18b6774 100644 --- a/runtime/objc-runtime.h +++ b/runtime/objc-runtime.h @@ -29,9 +29,7 @@ #ifndef _OBJC_RUNTIME_H_ #define _OBJC_RUNTIME_H_ -#ifndef NeXT_PDO #import -#endif #import #import @@ -42,10 +40,6 @@ struct objc_symtab { SEL *refs; unsigned short cls_def_cnt; unsigned short cat_def_cnt; -#ifdef NeXT_PDO - arith_t obj_defs; - arith_t proto_defs; -#endif void *defs[1]; /* variable size */ }; @@ -58,16 +52,10 @@ struct objc_module { Symtab symtab; }; -#ifdef __cplusplus -extern "Objective-C" { -#endif struct objc_super { id receiver; Class class; }; -#ifdef __cplusplus -} -#endif /* kernel operations */ diff --git a/runtime/objc-runtime.m b/runtime/objc-runtime.m index 86036ef..69f5f1c 100644 --- a/runtime/objc-runtime.m +++ b/runtime/objc-runtime.m @@ -87,10 +87,6 @@ typedef struct _PendingClass * Exports. **********************************************************************/ -// Mask which specifies whether we are multi-threaded or not. -// A value of (-1) means single-threaded, 0 means multi-threaded. -int _objc_multithread_mask = (-1); - // Function to call when message sent to nil object. void (*_objc_msgNil)(id, SEL) = NULL; @@ -106,6 +102,9 @@ OBJC_DECLARE_LOCK (classLock); // Condition for logging load progress static int LaunchingDebug = -1; +// objc's key for pthread_getspecific +pthread_key_t _objc_pthread_key; + /*********************************************************************** * Function prototypes internal to this module. **********************************************************************/ @@ -128,7 +127,6 @@ static void map_selrefs (SEL * sels, unsigned int cnt); static void map_method_descs (struct objc_method_description_list * methods); static void _objc_fixup_protocol_objects_for_image (header_info * hi); static void _objc_bindModuleContainingCategory(Category cat); -static const char * libraryNameForMachHeader (const headerType * themh); static void _objc_fixup_selector_refs (const header_info * hi); static void _objc_call_loads_for_image (header_info * header); static void _objc_checkForPendingClassReferences (struct objc_class * cls); @@ -151,6 +149,9 @@ static NXHashTablePrototype classHashPrototype = NXNoEffectFree, 0 }; +// Exported copy of class_hash variable (hook for debugging tools) +NXHashTable *_objc_debug_class_hash = NULL; + // Function pointer objc_getClass calls through when class is not found static int (*objc_classHandler) (const char *) = _objc_defaultClassHandler; @@ -169,14 +170,14 @@ void objc_dump_class_hash (void) { NXHashTable * table; unsigned count; - struct objc_class * * data; + struct objc_class * data; NXHashState state; table = class_hash; count = 0; state = NXInitHashState (table); while (NXNextHashState (table, &state, (void **) &data)) - printf ("class %d: %s\n", ++count, (*data)->name); + printf ("class %d: %s\n", ++count, data->name); } /*********************************************************************** @@ -226,6 +227,7 @@ void _objc_init_class_hash (void) 1024, nil, _objc_create_zone ()); + _objc_debug_class_hash = class_hash; } /*********************************************************************** @@ -291,6 +293,7 @@ void objc_setClassHandler (int (*userSuppliedHandler) (const char *)) * If the objc_classHandler returns a non-zero value, try once more to * find the class. Default objc_classHandler always returns zero. * objc_setClassHandler is how someone can install a non-default routine. +* Warning: doesn't work if aClassName is the name of a posed-for class's isa! **********************************************************************/ id objc_getClass (const char * aClassName) { @@ -340,6 +343,7 @@ id objc_lookUpClass (const char * aClassName) /*********************************************************************** * objc_getMetaClass. Return the id of the meta class the named class. +* Warning: doesn't work if aClassName is the name of a posed-for class's isa! **********************************************************************/ id objc_getMetaClass (const char * aClassName) { @@ -1073,7 +1077,7 @@ static void _objc_fixup_protocol_objects_for_image (header_info * hi) OBJC_PROTOCOL_PTR protos; unsigned int index; - // Locate protocals in the image + // Locate protocols in the image protos = (OBJC_PROTOCOL_PTR) _getObjcProtocols ((headerType *) hi->mhdr, &size); if (!protos) return; @@ -1120,7 +1124,6 @@ void _objc_bindModuleContainingList() { ***********************************************************************/ static void _objc_bind_symbol(const char *name) { - int i; static header_info *lastHeader = NULL; header_info *hInfo; const headerType *imageHeader = lastHeader ? lastHeader->mhdr : NULL; @@ -1224,6 +1227,24 @@ void _objc_bindModuleContainingClass (struct objc_class * cls) { } +/*********************************************************************** +* _objc_bindClassIfNeeded. +* If the given class is still marked as needs-bind, bind the module +* containing it. +* Called during _objc_call_loads_for_image just before sending +load, +* and during class_initialize just before sending +initialize. +**********************************************************************/ +void _objc_bindClassIfNeeded(struct objc_class *cls) +{ + // Clear NEED_BIND *after* binding to prevent race + // This assumes that simultaneous binding of one module by two threads is ok. + if (cls->info & CLS_NEED_BIND) { + _objc_bindModuleContainingClass(cls); + cls->info &= ~CLS_NEED_BIND; + } +} + + /*********************************************************************** * _objc_addHeader. * @@ -1273,7 +1294,7 @@ static header_info * _objc_addHeader(const headerType *header, unsigned long vma * * If we have it we're in trouble **************************************************************************/ -void _objc_fatalHeader(const headerType *header) +static void _objc_fatalHeader(const headerType *header) { header_info *hInfo; @@ -1284,29 +1305,6 @@ void _objc_fatalHeader(const headerType *header) } } -/*********************************************************************** -* libraryNameForMachHeader. -**********************************************************************/ -static const char * libraryNameForMachHeader (const headerType * themh) -{ - unsigned long index; - unsigned long imageCount; - headerType * mh; - - // Search images for matching type - imageCount = _dyld_image_count (); - for (index = 0; index < imageCount ; index += 1) - { - // Return name of image with matching type - mh = _dyld_get_image_header (index); - if (mh == themh) - return _dyld_get_image_name (index); - } - - // Not found - return 0; -} - /*********************************************************************** * _objc_fixup_selector_refs. Register all of the selectors in each * image, and fix them all up. @@ -1314,11 +1312,8 @@ static const char * libraryNameForMachHeader (const headerType * themh) **********************************************************************/ static void _objc_fixup_selector_refs (const header_info * hi) { - unsigned int midx; unsigned int size; - OBJC_PROTOCOL_PTR protos; Module mods; - unsigned int index; SEL * messages_refs; mods = (Module) ((unsigned long) hi->mod_ptr + hi->image_slide); @@ -1370,10 +1365,7 @@ static void _objc_call_loads_for_image (header_info * header) // +initialize and cache fill on class that is not even loaded yet load_method = class_lookupNamedMethodInMethodList (*mlistp, "load"); if (load_method) { - if (cls->info & CLS_NEED_BIND) { - cls->info &= ~CLS_NEED_BIND; - _objc_bindModuleContainingClass(cls); - } + _objc_bindClassIfNeeded(cls); (*load_method) ((id) cls, @selector(load)); } } @@ -1421,11 +1413,30 @@ static void objc_setConfiguration() { **********************************************************************/ void objc_setMultithreaded (BOOL flag) { - if (flag == YES) - _objc_multithread_mask = 0; - else - _objc_multithread_mask = (-1); + // Nothing here. Thread synchronization in the runtime is always active. } + + + +/*********************************************************************** +* _objc_pthread_destroyspecific +* Destructor for objc's per-thread data. +* arg shouldn't be NULL, but we check anyway. +**********************************************************************/ +extern void _destroyInitializingClassList(struct _objc_initializing_classes *list); +void _objc_pthread_destroyspecific(void *arg) +{ + _objc_pthread_data *data = (_objc_pthread_data *)arg; + if (data != NULL) { + _destroyInitializingClassList(data->initializingClasses); + + // add further cleanup here... + + free(data); + } +} + + /*********************************************************************** * _objcInit. * Library initializer called by dyld & from crt0 @@ -1448,6 +1459,8 @@ void _objcInit(void) { // make sure CF is initialized before we go further; // someday this can be removed, as it'll probably be automatic __CFInitialize(); + + pthread_key_create(&_objc_pthread_key, _objc_pthread_destroyspecific); // Create the class lookup table _objc_init_class_hash (); @@ -1545,7 +1558,6 @@ static void _objc_map_image(headerType *mh, unsigned long vmaddr_slide) { static int dumpClasses = -1; header_info *hInfo; - unsigned int size; if ( dumpClasses == -1 ) { if ( getenv("OBJC_DUMP_CLASSES") ) dumpClasses = 1; diff --git a/runtime/objc-sel.m b/runtime/objc-sel.m index 59c476b..c5db8c8 100644 --- a/runtime/objc-sel.m +++ b/runtime/objc-sel.m @@ -47,7 +47,7 @@ static SEL _objc_search_builtins(const char *key) { #if defined(DUMP_SELECTORS) if (NULL != key) printf("\t\"%s\",\n", key); #endif - /* The builtin table contains only sels starting with '[A-z]' */ + /* The builtin table contains only sels starting with '[A-z]', including '_' */ if (!key) return (SEL)0; if ('\0' == *key) return (SEL)_objc_empty_selector; if (*key < 'A' || 'z' < *key) return (SEL)0; @@ -69,7 +69,7 @@ static CFMutableSetRef _objc_selectors = NULL; static Boolean _objc_equal_selector(const void *v1, const void *v2) { if (v1 == v2) return TRUE; - if ((!v1 && v2) || (v1 && !v2)) return FALSE; + if ((v1 == NULL) || (v2 == NULL)) return FALSE; return ((*(char *)v1 == *(char *)v2) && (0 == _objc_strcmp(v1, v2))) ? TRUE : FALSE; }