commit 323e3dfaebf536d1d96cd072849d25c8c38f118f Author: theraven Date: Tue Sep 8 16:18:59 2009 +0000 Added new runtime library, based on GCC 4.4 libobjc, libobjc_tr and Objective2.framework. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..aaa67da --- /dev/null +++ b/COPYING @@ -0,0 +1,28 @@ +This document governs the GNUstep Objective-C runtime. This is based on the +GCC Objective-C runtime, which is governed by version 3 of the GNU General +Public License and on the Étoilé Objective-C 2 framework and Objective-C +runtime, which are MIT licensed. + +As a result of this dual inheritance, some portions of this library are more +permissively licensed than the whole. When viewed in aggregate, this library +is licensed under the GNU General Public Licence, version 3 or later (at your +option). You may find the relevant license texts in full in the following +files in this distribution: + +COPYING3 contains the GNU general public license version 3. +COPYING.RUNTIME contains the GCC runtime exemption to the GNU GPL. + +Some individual files retain their original MIT license. These are: + +blocks_runtime.m +lock.h +mutation.m +runtime.c +sync.m +objc/blocks_runtime.h +objc/runtime.h + +These files may be used for any purpose, including binary-only redistribution, +according to the terms of the MIT license, as long as their copyright notices +remain intact. You may find the full text of the license for these files in +COPYING.MIT diff --git a/COPYING.MIT b/COPYING.MIT new file mode 100644 index 0000000..8647a56 --- /dev/null +++ b/COPYING.MIT @@ -0,0 +1,20 @@ +Copyright (c) 2009 David Chisnall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/COPYING.RUNTIME b/COPYING.RUNTIME new file mode 100644 index 0000000..e1b3c69 --- /dev/null +++ b/COPYING.RUNTIME @@ -0,0 +1,73 @@ +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + diff --git a/COPYING3 b/COPYING3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..1fa631b --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,64 @@ +include $(GNUSTEP_MAKEFILES)/common.make + +LIBRARY_NAME = libobjc + +libobjc_VERSION = 4 + +libobjc_OBJC_FILES = \ + blocks_runtime.m\ + linking.m\ + mutation.m\ + NXConstStr.m\ + Object.m\ + Protocol.m\ + sync.m + + +libobjc_C_FILES = \ + archive.c\ + class.c\ + encoding.c\ + exception.c\ + gc.c\ + hash.c\ + init.c\ + misc.c\ + nil_method.c\ + objects.c\ + protocol.c\ + runtime.c\ + sarray.c\ + selector.c\ + sendmsg.c\ + thr.c + +# Deprecated functions are only deprecated for external use, not for us because +# we are special, precious, little flowers. +libobjc_CPPFLAGS += -D__OBJC_RUNTIME_INTERNAL__=1 +# Note to Riccardo. Please do not 'fix' C99isms in this. The new ABI is only +# useful on compilers that support C99 (currently only clang), so there is no +# benefit from supporting platforms with no C99 compiler. +libobjc_CFLAGS += -Werror -std=c99 -g -fexceptions +libobjc_OBJCFLAGS += -g +libobjc_LDFLAGS += -g + +ifeq ($(findstring no, $(debug)),) +before-all:: + @echo + @echo + @echo WARNING: You are building in debug mode. This will generate a LOT of console \ + output for every Objective-C program you run. If this is not what you \ + want, please compile with $(MAKE) debug=no + @echo + @echo +endif + +ifneq ($(findstring gcc, $(CC)),) +libobjc_CFLAGS += -fgnu89-inline +endif + +ifneq ($(findstring mingw, $(GNUSTEP_HOST_OS)),) +libobjc_C_FILES += libobjc_entry.c +endif + +include $(GNUSTEP_MAKEFILES)/library.make diff --git a/NXConstStr.m b/NXConstStr.m new file mode 100644 index 0000000..55492e1 --- /dev/null +++ b/NXConstStr.m @@ -0,0 +1,45 @@ +/* Implementation of the NXConstantString class for Objective-C. + Copyright (C) 1995, 2009 Free Software Foundation, Inc. + Contributed by Pieter J. Schoenmakers + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "objc/NXConstStr.h" +#include "objc/objc-api.h" + +@implementation NXConstantString ++ (void)load +{ + __CLS_INFO((Class)self) &= ~_CLS_PLANE_AWARE; +} + +-(const char *) cString +{ + return (c_string); +} /* -cString */ + +-(unsigned int) length +{ + return (len); +} /* -length */ + +@end diff --git a/Object.m b/Object.m new file mode 100644 index 0000000..e828141 --- /dev/null +++ b/Object.m @@ -0,0 +1,389 @@ +/* The implementation of class Object for Objective-C. + Copyright (C) 1993, 1994, 1995, 1997, 2002, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include +#include +#include "objc/Object.h" +#include "objc/Protocol.h" +#include "objc/objc-api.h" + +#define MAX_CLASS_NAME_LEN 256 + +@implementation Object + ++ (void)load +{ + __CLS_INFO((Class)self) &= ~_CLS_PLANE_AWARE; +} ++ initialize +{ + // Object subclasses are not plane aware either. + __CLS_INFO((Class)self) &= ~_CLS_PLANE_AWARE; + return self; +} + +- init +{ + return self; +} + ++ new +{ + return [[self alloc] init]; +} + ++ alloc +{ + return class_create_instance(self); +} + +- free +{ + return object_dispose(self); +} + +- copy +{ + return [[self shallowCopy] deepen]; +} + +- shallowCopy +{ + return object_copy(self); +} + +- deepen +{ + return self; +} + +- deepCopy +{ + return [self copy]; +} + +- (Class)class +{ + return object_get_class(self); +} + +- (Class)superClass +{ + return object_get_super_class(self); +} + +- (MetaClass)metaClass +{ + return object_get_meta_class(self); +} + +- (const char *)name +{ + return object_get_class_name(self); +} + +- self +{ + return self; +} + +- (unsigned int)hash +{ + return (size_t)self; +} + +- (BOOL)isEqual:anObject +{ + return self==anObject; +} + +- (int)compare:(id)anotherObject; +{ + if ([self isEqual:anotherObject]) + return 0; + // Ordering objects by their address is pretty useless, + // so subclasses should override this is some useful way. + else if ((id)self > anotherObject) + return 1; + else + return -1; +} + +- (BOOL)isMetaClass +{ + return NO; +} + +- (BOOL)isClass +{ + return object_is_class(self); +} + +- (BOOL)isInstance +{ + return object_is_instance(self); +} + +- (BOOL)isKindOf:(Class)aClassObject +{ + Class class; + + for (class = self->isa; class!=Nil; class = class_get_super_class(class)) + if (class==aClassObject) + return YES; + return NO; +} + +- (BOOL)isMemberOf:(Class)aClassObject +{ + return self->isa==aClassObject; +} + +- (BOOL)isKindOfClassNamed:(const char *)aClassName +{ + Class class; + + if (aClassName!=NULL) + for (class = self->isa; class!=Nil; class = class_get_super_class(class)) + if (!strcmp(class_get_class_name(class), aClassName)) + return YES; + return NO; +} + +- (BOOL)isMemberOfClassNamed:(const char *)aClassName +{ + return ((aClassName!=NULL) + &&!strcmp(class_get_class_name(self->isa), aClassName)); +} + ++ (BOOL)instancesRespondTo:(SEL)aSel +{ + return class_get_instance_method(self, aSel)!=METHOD_NULL; +} + +- (BOOL)respondsTo:(SEL)aSel +{ + return ((object_is_instance(self) + ?class_get_instance_method(self->isa, aSel) + :class_get_class_method(self->isa, aSel))!=METHOD_NULL); +} + ++ (IMP)instanceMethodFor:(SEL)aSel +{ + return method_get_imp(class_get_instance_method(self, aSel)); +} + +// Indicates if the receiving class or instance conforms to the given protocol +// not usually overridden by subclasses +// +// Modified 9/5/94 to always search the class object's protocol list, rather +// than the meta class. + ++ (BOOL) conformsTo: (Protocol*)aProtocol +{ + size_t i; + struct objc_protocol_list* proto_list; + id parent; + + for (proto_list = ((Class)self)->protocols; + proto_list; proto_list = proto_list->next) + { + for (i=0; i < proto_list->count; i++) + { + if ([proto_list->list[i] conformsTo: aProtocol]) + return YES; + } + } + + if ((parent = [self superClass])) + return [parent conformsTo: aProtocol]; + else + return NO; +} + +- (BOOL) conformsTo: (Protocol*)aProtocol +{ + return [[self class] conformsTo:aProtocol]; +} + +- (IMP)methodFor:(SEL)aSel +{ + return (method_get_imp(object_is_instance(self) + ?class_get_instance_method(self->isa, aSel) + :class_get_class_method(self->isa, aSel))); +} + ++ (struct objc_method_description *)descriptionForInstanceMethod:(SEL)aSel +{ + return ((struct objc_method_description *) + class_get_instance_method(self, aSel)); +} + +- (struct objc_method_description *)descriptionForMethod:(SEL)aSel +{ + return ((struct objc_method_description *) + (object_is_instance(self) + ?class_get_instance_method(self->isa, aSel) + :class_get_class_method(self->isa, aSel))); +} + +- perform:(SEL)aSel +{ + IMP msg = objc_msg_lookup(self, aSel); + if (!msg) + return [self error:"invalid selector passed to %s", sel_get_name(_cmd)]; + return (*msg)(self, aSel); +} + +- perform:(SEL)aSel with:anObject +{ + IMP msg = objc_msg_lookup(self, aSel); + if (!msg) + return [self error:"invalid selector passed to %s", sel_get_name(_cmd)]; + return (*msg)(self, aSel, anObject); +} + +- perform:(SEL)aSel with:anObject1 with:anObject2 +{ + IMP msg = objc_msg_lookup(self, aSel); + if (!msg) + return [self error:"invalid selector passed to %s", sel_get_name(_cmd)]; + return (*msg)(self, aSel, anObject1, anObject2); +} + +- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame +{ + (void) argFrame; /* UNUSED */ + return (retval_t)[self doesNotRecognize: aSel]; +} + +- (retval_t)performv:(SEL)aSel :(arglist_t)argFrame +{ + return objc_msg_sendv(self, aSel, argFrame); +} + ++ poseAs:(Class)aClassObject +{ + return class_pose_as(self, aClassObject); +} + +- (Class)transmuteClassTo:(Class)aClassObject +{ + if (object_is_instance(self)) + if (class_is_class(aClassObject)) + if (class_get_instance_size(aClassObject)==class_get_instance_size(isa)) + if ([self isKindOf:aClassObject]) + { + Class old_isa = isa; + isa = aClassObject; + return old_isa; + } + return nil; +} + +- subclassResponsibility:(SEL)aSel +{ + return [self error:"subclass should override %s", sel_get_name(aSel)]; +} + +- notImplemented:(SEL)aSel +{ + return [self error:"method %s not implemented", sel_get_name(aSel)]; +} + +- shouldNotImplement:(SEL)aSel +{ + return [self error:"%s should not implement %s", + object_get_class_name(self), sel_get_name(aSel)]; +} + +- doesNotRecognize:(SEL)aSel +{ + return [self error:"%s does not recognize %s", + object_get_class_name(self), sel_get_name(aSel)]; +} + +- error:(const char *)aString, ... +{ +#define FMT "error: %s (%s)\n%s\n" + char fmt[(strlen((char*)FMT)+strlen((char*)object_get_class_name(self)) + +((aString!=NULL)?strlen((char*)aString):0)+8)]; + va_list ap; + + sprintf(fmt, FMT, object_get_class_name(self), + object_is_instance(self)?"instance":"class", + (aString!=NULL)?aString:""); + va_start(ap, aString); + objc_verror(self, OBJC_ERR_UNKNOWN, fmt, ap); + va_end(ap); + return nil; +#undef FMT +} + ++ (int)version +{ + return class_get_version(self); +} + ++ setVersion:(int)aVersion +{ + class_set_version(self, aVersion); + return self; +} + ++ (int)streamVersion: (TypedStream*)aStream +{ + if (aStream->mode == OBJC_READONLY) + return objc_get_stream_class_version (aStream, self); + else + return class_get_version (self); +} + +// These are used to write or read the instance variables +// declared in this particular part of the object. Subclasses +// should extend these, by calling [super read/write: aStream] +// before doing their own archiving. These methods are private, in +// the sense that they should only be called from subclasses. + +- read: (TypedStream*)aStream +{ + (void) aStream; /* UNUSED */ + // [super read: aStream]; + return self; +} + +- write: (TypedStream*)aStream +{ + (void) aStream; /* UNUSED */ + // [super write: aStream]; + return self; +} + +- awake +{ + // [super awake]; + return self; +} + +@end diff --git a/Protocol.m b/Protocol.m new file mode 100644 index 0000000..23606a9 --- /dev/null +++ b/Protocol.m @@ -0,0 +1,198 @@ +/* This file contains the implementation of class Protocol. + Copyright (C) 1993, 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "objc/Protocol.h" +#include "objc/objc-api.h" + +/* Method description list */ +struct objc_method_description_list { + int count; + struct objc_method_description list[1]; +}; + +@interface Protocol2 : Protocol +{ + struct objc_method_description_list *optional_instance_methods, *optional_class_methods; + struct objc_property_list *properties, *optional_properties; +} +@end + +@implementation Protocol ++ (void)load +{ + __CLS_INFO((Class)self) &= ~_CLS_PLANE_AWARE; +} + +/* Obtaining attributes intrinsic to the protocol */ + +- (const char *)name +{ + return protocol_name; +} + +/* Testing protocol conformance */ + +- (BOOL) conformsTo: (Protocol *)aProtocolObject +{ + size_t i; + struct objc_protocol_list* proto_list; + + if (aProtocolObject == nil) + return NO; + + if (!strcmp(aProtocolObject->protocol_name, self->protocol_name)) + return YES; + + for (proto_list = protocol_list; proto_list; proto_list = proto_list->next) + { + for (i=0; i < proto_list->count; i++) + { + if ([proto_list->list[i] conformsTo: aProtocolObject]) + return YES; + } + } + + return NO; +} + +/* Looking up information specific to a protocol */ + +- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel +{ + int i; + struct objc_protocol_list* proto_list; + const char* name = sel_get_name (aSel); + struct objc_method_description *result; + + if (instance_methods) + for (i = 0; i < instance_methods->count; i++) + { + if (!strcmp ((char*)instance_methods->list[i].name, name)) + return &(instance_methods->list[i]); + } + + for (proto_list = protocol_list; proto_list; proto_list = proto_list->next) + { + size_t j; + for (j=0; j < proto_list->count; j++) + { + if ((result = [proto_list->list[j] + descriptionForInstanceMethod: aSel])) + return result; + } + } + + return NULL; +} + +- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel; +{ + int i; + struct objc_protocol_list* proto_list; + const char* name = sel_get_name (aSel); + struct objc_method_description *result; + + if (class_methods) + for (i = 0; i < class_methods->count; i++) + { + if (!strcmp ((char*)class_methods->list[i].name, name)) + return &(class_methods->list[i]); + } + + for (proto_list = protocol_list; proto_list; proto_list = proto_list->next) + { + size_t j; + for (j=0; j < proto_list->count; j++) + { + if ((result = [proto_list->list[j] + descriptionForClassMethod: aSel])) + return result; + } + } + + return NULL; +} + +- (unsigned) hash +{ + /* Compute a hash of the protocol_name; use the same hash algorithm + * that we use for class names; protocol names and class names are + * somewhat similar types of string spaces. + */ + int hash = 0, index; + + for (index = 0; protocol_name[index] != '\0'; index++) + { + hash = (hash << 4) ^ (hash >> 28) ^ protocol_name[index]; + } + + hash = (hash ^ (hash >> 10) ^ (hash >> 20)); + + return hash; +} + +/* + * Equality between formal protocols is only formal (nothing to do + * with actually checking the list of methods they have!). Two formal + * Protocols are equal if and only if they have the same name. + * + * Please note (for comparisons with other implementations) that + * checking the names is equivalent to checking that Protocol A + * conforms to Protocol B and Protocol B conforms to Protocol A, + * because this happens iff they have the same name. If they have + * different names, A conforms to B if and only if A includes B, but + * the situation where A includes B and B includes A is a circular + * dependency between Protocols which is forbidden by the compiler, so + * A conforms to B and B conforms to A with A and B having different + * names is an impossible case. + */ +- (BOOL) isEqual: (id)obj +{ + if (obj == self) + return YES; + + if ([obj isKindOf: [Protocol class]]) + { + if (strcmp (protocol_name, ((Protocol *)obj)->protocol_name) == 0) + return YES; + } + + return NO; +} +@end + +/** + * Objective-C 2 protocol objects are opaque. + */ +@implementation Protocol2 @end + +/** + * This class exists for the sole reason that the legacy GNU ABI did not + * provide a way of registering protocols with the runtime. With the new ABI, + * every protocol in a compilation unit that is not referenced should be added + * in a category on this class. This ensures that the runtime sees every + * protocol at least once and can perform uniquing. + */ +@interface __ObjC_Protocol_Holder_Ugly_Hack : Object @end +@implementation __ObjC_Protocol_Holder_Ugly_Hack @end diff --git a/README b/README new file mode 100644 index 0000000..cad0b2a --- /dev/null +++ b/README @@ -0,0 +1,158 @@ +GNUstep Objective-C Runtime +=========================== + +The GNUstep Objective-C runtime is based on the GCC runtime. It supports both +a legacy and a modern ABI, allowing code compiled with old versions of GCC to +be supported without requiring recompilation. The modern ABI adds the +following features: + +- Non-fragile instance variables. +- Protocol uniquing. +- Object planes support. +- Declared property introspection. + +Both ABIs support the following feature above and beyond the GNU runtime: + +- The modern Objective-C runtime APIs, introduced with OS X 10.5. +- Blocks (closures). + +Non-Fragile Instance Variables +------------------------------ + +When a class is compiled to support non-fragile instance variables, the +instance_size field in the class is set to 0 - the size of the instance +variables declared on that class (excluding those inherited. For example, an +NSObject subclass declaring an int ivar would have its instance_size set to 0 - +sizeof(int). The offsets of each instance variable in the class's ivar_list +field are then set to the offset from the start of the superclass's ivars. + +When the class is loaded, the runtime library uses the size of the superclass +to calculate the correct size for this new class and the correct offsets. Each +instance variable should have two other variables exported as global symbols. +Consider the following class: + +@interface NewClass : SuperClass { + int anIvar; +} +@end + +This would have its instance_size initialized to 0-sizeof(int), and anIvar's +offset initialized to 0. It should also export the following two symbols: + +int __objc_ivar_offset_value_NewClass.anIvar; +int *__objc_ivar_offset_NewClass.anIvar; + +The latter should point to the former or to the ivar_offset field in the ivar +metadata. The former should be pointed to by the only element in the +ivar_offsets array in the class structure. + +In other compilation units referring to this ivar, the latter symbol should be +exported as a weak symbol pointing to an internal symbol containing the +compiler's guess at the ivar offset. The ivar will then work as a fragile ivar +when NewClass is compiled with the old ABI. If NewClass is compiled with the +new ABI, then the linker will replace the weak symbol with the version in the +class's compilation unit and references which use this offset will function +correctly. + +If the compiler can guarantee that NewClass is compiled with the new ABI, for +example if it is declared in the same compilation unit, by finding the symbol +during a link-time optimization phase, or as a result of a command-line +argument, then it may use the __objc_ivar_offset_value_NewClass.anIvar symbol +as the ivar offset. This eliminates the need for one load for every ivar +access. + +Protocols +--------- + +The runtime now provides a __ObjC_Protocol_Holder_Ugly_Hack class. All +protocols that are referenced but not defined should be registered as +categories on this class. This ensures that every protocol is registered with +the runtime. + +In the near future, the runtime will ensure that protocols can be looked up by +name at run time and that empty protocol definitions have their fields updated +to match the defined version. + +Protocols have been extended to provide space for introspection on properties +and optional methods. These fields only exist on protocols compiled with a +compiler that supports Objective-C 2. To differentiate the two, the isa +pointer for new protocols will be set to the Protocol2 class. + +Fast Proxies and Cacheable Lookups +---------------------------------- + +The new runtime provides two mechanisms for faster lookup. The older +Vobjc_msg_lookup() function, which returns an IMP, is still supported, however +it is no longer recommended. The new lookup functions is: + +Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender) + +The receiver is passed by pointer, and so may be modified during the lookup +process. The runtime itself will never modify the receiver. The following +hook is provided to allow fast proxy support: + +id (*objc_proxy_lookup)(id receiver, SEL op); + +This function takes an object and selector as arguments and returns a new +objects. The lookup will then be re-run and the final message should be sent to +the new object. + +The returned Slot_t from the new lookup function is a pointer to a structure +which contains both an IMP and a version (among other things). The version is +incremented every time the method is overridden, allowing this to be cached by +the caller. User code wishing to perform IMP caching may use the old mechanism +if it can guarantee that the IMP will not change between calls, or the newer +mechanism. Note that a modern compiler should insert caching automatically, +ideally with the aid of run-time profiling results. To support this, a new hook +has been added: + +Slot_t objc_msg_forward3(id receiver, SEL op); + +This is identical to objc_msg_forward2(), but returns a pointer to a slot, +instead of an IMP. The slot should have its version set to 0, to prevent +caching. + +Object Planes +------------- + +Object planes provide interception points for messages between groups of +related objects. They can be thought of as similar to processes, with mediated +inter-plane communication. A typical use-case for an object plane is to +automatically queue messages sent to a thread, or to record every message sent +to model objects. Planes can dramatically reduce the number of proxy objects +required for this kind of activity. + +The GNUstep runtime adds a flag to class objects indicating that their +instances are present in the global plane. All constant strings, protocols, +and classes are in the global plane, and may therefore be sent and may receive +messages bypassing the normal plane interception mechanism. + +The runtime library does not provide direct support for planes, it merely +provides the core components required to implement support for planes in +another framework. Two objects are regarded as being in the same plane when +they words immediately before their isa pointers are the same. In this case, +the runtime's usual dispatch mechanisms will be used. In all other cases, the +runtime will delegate message lookup to another library via the following hook: + +Slot_t (*objc_plane_lookup)(id *receiver, SEL op, id sender); + +From the perspective of the runtime, the plane identifier is opaque. In +GNUstep, it is a pointer to an NSZone structure. + +Threading +--------- + +The old threading layer is gone. It was buggy, badly supported, and +inadequately tested. The library now always runs in thread-safe mode. The +same functions for locking the runtime mutex are still supported, but their use +any mutex not exported by the runtime library is explicitly not supported. The +(private) lock.h header is now used to abstract the details of different +threading systems sufficiently for the runtime. This provides mechanisms for +locking, unlocking, creating, and destroying mutex objects. + +Objective-C 2 Features +---------------------- + +The runtime now provides implementations of the functions required for the +@synchronized directive, for property accessors, and for fast enumeration. The +public runtime function interfaces now match those of OS X. diff --git a/archive.c b/archive.c new file mode 100644 index 0000000..dc2830e --- /dev/null +++ b/archive.c @@ -0,0 +1,1665 @@ + /* GNU Objective C Runtime archiving + Copyright (C) 1993, 1995, 1996, 1997, 2002, 2004, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "objc/typedstream.h" +#include "objc/encoding.h" +#include "objc/runtime-legacy.h" +#include + +extern int fflush (FILE *); + +#define ROUND(V, A) \ + ({ __typeof (V) __v = (V); __typeof (A) __a = (A); \ + __a * ((__v + __a - 1)/__a); }) + +#define PTR2LONG(P) (((char *) (P))-(char *) 0) +#define LONG2PTR(L) (((char *) 0) + (L)) + +/* Declare some functions... */ + +static int +objc_read_class (struct objc_typed_stream *stream, Class *class); + +int objc_sizeof_type (const char *type); + +static int +objc_write_use_common (struct objc_typed_stream *stream, unsigned long key); + +static int +objc_write_register_common (struct objc_typed_stream *stream, + unsigned long key); + +static int +objc_write_class (struct objc_typed_stream *stream, + struct objc_class *class); + +const char *objc_skip_type (const char *type); + +static void __objc_finish_write_root_object (struct objc_typed_stream *); +static void __objc_finish_read_root_object (struct objc_typed_stream *); + +static inline int +__objc_code_unsigned_char (unsigned char *buf, unsigned char val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + buf[0] = _B_NINT|0x01; + buf[1] = val; + return 2; + } +} + +int +objc_write_unsigned_char (struct objc_typed_stream *stream, + unsigned char value) +{ + unsigned char buf[sizeof (unsigned char) + 1]; + int len = __objc_code_unsigned_char (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_char (unsigned char *buf, signed char val) +{ + if (val >= 0) + return __objc_code_unsigned_char (buf, val); + else + { + buf[0] = _B_NINT|_B_SIGN|0x01; + buf[1] = -val; + return 2; + } +} + +int +objc_write_char (struct objc_typed_stream *stream, signed char value) +{ + unsigned char buf[sizeof (char) + 1]; + int len = __objc_code_char (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_unsigned_short (unsigned char *buf, unsigned short val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + int c, b; + + buf[0] = _B_NINT; + + for (c = sizeof (short); c != 0; c -= 1) + if (((val >> (8*(c - 1)))%0x100) != 0) + break; + + buf[0] |= c; + + for (b = 1; c != 0; c--, b++) + { + buf[b] = (val >> (8*(c - 1)))%0x100; + } + + return b; + } +} + +int +objc_write_unsigned_short (struct objc_typed_stream *stream, + unsigned short value) +{ + unsigned char buf[sizeof (unsigned short) + 1]; + int len = __objc_code_unsigned_short (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_short (unsigned char *buf, short val) +{ + int sign = (val < 0); + int size = __objc_code_unsigned_short (buf, sign ? -val : val); + if (sign) + buf[0] |= _B_SIGN; + return size; +} + +int +objc_write_short (struct objc_typed_stream *stream, short value) +{ + unsigned char buf[sizeof (short) + 1]; + int len = __objc_code_short (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + + +static inline int +__objc_code_unsigned_int (unsigned char *buf, unsigned int val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + int c, b; + + buf[0] = _B_NINT; + + for (c = sizeof (int); c != 0; c -= 1) + if (((val >> (8*(c - 1)))%0x100) != 0) + break; + + buf[0] |= c; + + for (b = 1; c != 0; c--, b++) + { + buf[b] = (val >> (8*(c-1)))%0x100; + } + + return b; + } +} + +int +objc_write_unsigned_int (struct objc_typed_stream *stream, unsigned int value) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len = __objc_code_unsigned_int (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_int (unsigned char *buf, int val) +{ + int sign = (val < 0); + int size = __objc_code_unsigned_int (buf, sign ? -val : val); + if (sign) + buf[0] |= _B_SIGN; + return size; +} + +int +objc_write_int (struct objc_typed_stream *stream, int value) +{ + unsigned char buf[sizeof (int) + 1]; + int len = __objc_code_int (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_unsigned_long (unsigned char *buf, unsigned long val) +{ + if ((val&_B_VALUE) == val) + { + buf[0] = val|_B_SINT; + return 1; + } + else + { + int c, b; + + buf[0] = _B_NINT; + + for (c = sizeof (long); c != 0; c -= 1) + if (((val >> (8*(c - 1)))%0x100) != 0) + break; + + buf[0] |= c; + + for (b = 1; c != 0; c--, b++) + { + buf[b] = (val >> (8*(c - 1)))%0x100; + } + + return b; + } +} + +int +objc_write_unsigned_long (struct objc_typed_stream *stream, + unsigned long value) +{ + unsigned char buf[sizeof (unsigned long) + 1]; + int len = __objc_code_unsigned_long (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + +static inline int +__objc_code_long (unsigned char *buf, long val) +{ + int sign = (val < 0); + int size = __objc_code_unsigned_long (buf, sign ? -val : val); + if (sign) + buf[0] |= _B_SIGN; + return size; +} + +int +objc_write_long (struct objc_typed_stream *stream, long value) +{ + unsigned char buf[sizeof (long) + 1]; + int len = __objc_code_long (buf, value); + return (*stream->write) (stream->physical, (char*)buf, len); +} + + +int +objc_write_string (struct objc_typed_stream *stream, + const unsigned char *string, unsigned int nbytes) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len = __objc_code_unsigned_int (buf, nbytes); + + if ((buf[0]&_B_CODE) == _B_SINT) + buf[0] = (buf[0]&_B_VALUE)|_B_SSTR; + + else /* _B_NINT */ + buf[0] = (buf[0]&_B_VALUE)|_B_NSTR; + + if ((*stream->write) (stream->physical, (char*)buf, len) != 0) + return (*stream->write) (stream->physical, (char*)string, nbytes); + else + return 0; +} + +int +objc_write_string_atomic (struct objc_typed_stream *stream, + unsigned char *string, unsigned int nbytes) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, string)))) + return objc_write_use_common (stream, key); + else + { + int length; + objc_hash_add (&stream->stream_table, + LONG2PTR(key=PTR2LONG(string)), string); + if ((length = objc_write_register_common (stream, key))) + return objc_write_string (stream, string, nbytes); + return length; + } +} + +static int +objc_write_register_common (struct objc_typed_stream *stream, + unsigned long key) +{ + unsigned char buf[sizeof (unsigned long)+2]; + int len = __objc_code_unsigned_long (buf + 1, key); + if (len == 1) + { + buf[0] = _B_RCOMM|0x01; + buf[1] &= _B_VALUE; + return (*stream->write) (stream->physical, (char*)buf, len + 1); + } + else + { + buf[1] = (buf[1]&_B_VALUE)|_B_RCOMM; + return (*stream->write) (stream->physical, (char*)buf + 1, len); + } +} + +static int +objc_write_use_common (struct objc_typed_stream *stream, unsigned long key) +{ + unsigned char buf[sizeof (unsigned long)+2]; + int len = __objc_code_unsigned_long (buf + 1, key); + if (len == 1) + { + buf[0] = _B_UCOMM|0x01; + buf[1] &= _B_VALUE; + return (*stream->write) (stream->physical, (char*)buf, 2); + } + else + { + buf[1] = (buf[1]&_B_VALUE)|_B_UCOMM; + return (*stream->write) (stream->physical, (char*)buf + 1, len); + } +} + +static inline int +__objc_write_extension (struct objc_typed_stream *stream, unsigned char code) +{ + if (code <= _B_VALUE) + { + unsigned char buf = code|_B_EXT; + return (*stream->write) (stream->physical, (char*)&buf, 1); + } + else + { + objc_error (nil, OBJC_ERR_BAD_OPCODE, + "__objc_write_extension: bad opcode %c\n", code); + return -1; + } +} + +inline int +__objc_write_object (struct objc_typed_stream *stream, id object) +{ + unsigned char buf = '\0'; + SEL write_sel = sel_get_any_uid ("write:"); + if (object) + { + __objc_write_extension (stream, _BX_OBJECT); + objc_write_class (stream, object->class_pointer); + (*objc_msg_lookup (object, write_sel)) (object, write_sel, stream); + return (*stream->write) (stream->physical, (char*)&buf, 1); + } + else + return objc_write_use_common (stream, 0); +} + +int +objc_write_object_reference (struct objc_typed_stream *stream, id object) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->object_table, object)))) + return objc_write_use_common (stream, key); + + __objc_write_extension (stream, _BX_OBJREF); + return objc_write_unsigned_long (stream, PTR2LONG (object)); +} + +int +objc_write_root_object (struct objc_typed_stream *stream, id object) +{ + int len = 0; + if (stream->writing_root_p) + objc_error (nil, OBJC_ERR_RECURSE_ROOT, + "objc_write_root_object called recursively"); + else + { + stream->writing_root_p = 1; + __objc_write_extension (stream, _BX_OBJROOT); + if ((len = objc_write_object (stream, object))) + __objc_finish_write_root_object (stream); + stream->writing_root_p = 0; + } + return len; +} + +int +objc_write_object (struct objc_typed_stream *stream, id object) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->object_table, object)))) + return objc_write_use_common (stream, key); + + else if (object == nil) + return objc_write_use_common (stream, 0); + + else + { + int length; + objc_hash_add (&stream->object_table, + LONG2PTR(key=PTR2LONG(object)), object); + if ((length = objc_write_register_common (stream, key))) + return __objc_write_object (stream, object); + return length; + } +} + +inline int +__objc_write_class (struct objc_typed_stream *stream, struct objc_class *class) +{ + __objc_write_extension (stream, _BX_CLASS); + objc_write_string_atomic (stream, (unsigned char *) class->name, + strlen ((char *) class->name)); + return objc_write_unsigned_long (stream, class->version); +} + + +static int +objc_write_class (struct objc_typed_stream *stream, + struct objc_class *class) +{ + unsigned long key; + if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, class)))) + return objc_write_use_common (stream, key); + else + { + int length; + objc_hash_add (&stream->stream_table, + LONG2PTR(key = PTR2LONG(class)), class); + if ((length = objc_write_register_common (stream, key))) + return __objc_write_class (stream, class); + return length; + } +} + + +inline int +__objc_write_selector (struct objc_typed_stream *stream, SEL selector) +{ + const char *sel_name; + __objc_write_extension (stream, _BX_SEL); + /* to handle NULL selectors */ + if ((SEL)0 == selector) + return objc_write_string (stream, (unsigned char*)"", 0); + sel_name = sel_get_name (selector); + return objc_write_string (stream, (unsigned char*)sel_name, strlen ((char*)sel_name)); +} + +int +objc_write_selector (struct objc_typed_stream *stream, SEL selector) +{ + const char *sel_name; + unsigned long key; + + /* to handle NULL selectors */ + if ((SEL)0 == selector) + return __objc_write_selector (stream, selector); + + sel_name = sel_get_name (selector); + if ((key = PTR2LONG(objc_hash_value_for_key (stream->stream_table, + sel_name)))) + return objc_write_use_common (stream, key); + else + { + int length; + objc_hash_add (&stream->stream_table, + LONG2PTR(key = PTR2LONG(sel_name)), (char *) sel_name); + if ((length = objc_write_register_common (stream, key))) + return __objc_write_selector (stream, selector); + return length; + } +} + + + +/* +** Read operations +*/ + +inline int +objc_read_char (struct objc_typed_stream *stream, char *val) +{ + unsigned char buf; + int len; + len = (*stream->read) (stream->physical, (char*)&buf, 1); + if (len != 0) + { + if ((buf & _B_CODE) == _B_SINT) + (*val) = (buf & _B_VALUE); + + else if ((buf & _B_NUMBER) == 1) + { + len = (*stream->read) (stream->physical, val, 1); + if (buf&_B_SIGN) + (*val) = -1 * (*val); + } + + else + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected 8bit signed int, got %dbit int", + (int) (buf&_B_NUMBER)*8); + } + return len; +} + + +inline int +objc_read_unsigned_char (struct objc_typed_stream *stream, unsigned char *val) +{ + unsigned char buf; + int len; + if ((len = (*stream->read) (stream->physical, (char*)&buf, 1))) + { + if ((buf & _B_CODE) == _B_SINT) + (*val) = (buf & _B_VALUE); + + else if ((buf & _B_NUMBER) == 1) + len = (*stream->read) (stream->physical, (char*)val, 1); + + else + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected 8bit unsigned int, got %dbit int", + (int) (buf&_B_NUMBER)*8); + } + return len; +} + +inline int +objc_read_short (struct objc_typed_stream *stream, short *value) +{ + unsigned char buf[sizeof (short) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (short)) + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected short, got bigger (%dbits)", nbytes*8); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + if (buf[0] & _B_SIGN) + (*value) = -(*value); + } + } + return len; +} + +inline int +objc_read_unsigned_short (struct objc_typed_stream *stream, + unsigned short *value) +{ + unsigned char buf[sizeof (unsigned short) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (short)) + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected short, got int or bigger"); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + } + } + return len; +} + + +inline int +objc_read_int (struct objc_typed_stream *stream, int *value) +{ + unsigned char buf[sizeof (int) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (int)) + objc_error (nil, OBJC_ERR_BAD_DATA, "expected int, got bigger"); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + if (buf[0] & _B_SIGN) + (*value) = -(*value); + } + } + return len; +} + +inline int +objc_read_long (struct objc_typed_stream *stream, long *value) +{ + unsigned char buf[sizeof (long) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + { + int pos = 1; + int nbytes = buf[0] & _B_NUMBER; + if (nbytes > (int) sizeof (long)) + objc_error (nil, OBJC_ERR_BAD_DATA, "expected long, got bigger"); + len = (*stream->read) (stream->physical, (char*)buf + 1, nbytes); + (*value) = 0; + while (pos <= nbytes) + (*value) = ((*value)*0x100) + buf[pos++]; + if (buf[0] & _B_SIGN) + (*value) = -(*value); + } + } + return len; +} + +inline int +__objc_read_nbyte_uint (struct objc_typed_stream *stream, + unsigned int nbytes, unsigned int *val) +{ + int len; + unsigned int pos = 0; + unsigned char buf[sizeof (unsigned int) + 1]; + + if (nbytes > sizeof (int)) + objc_error (nil, OBJC_ERR_BAD_DATA, "expected int, got bigger"); + + len = (*stream->read) (stream->physical, (char*)buf, nbytes); + (*val) = 0; + while (pos < nbytes) + (*val) = ((*val)*0x100) + buf[pos++]; + return len; +} + + +inline int +objc_read_unsigned_int (struct objc_typed_stream *stream, + unsigned int *value) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + len = __objc_read_nbyte_uint (stream, (buf[0] & _B_VALUE), value); + + } + return len; +} + +int +__objc_read_nbyte_ulong (struct objc_typed_stream *stream, + unsigned int nbytes, unsigned long *val) +{ + int len; + unsigned int pos = 0; + unsigned char buf[sizeof (unsigned long) + 1]; + + if (nbytes > sizeof (long)) + objc_error (nil, OBJC_ERR_BAD_DATA, "expected long, got bigger"); + + len = (*stream->read) (stream->physical, (char*)buf, nbytes); + (*val) = 0; + while (pos < nbytes) + (*val) = ((*val)*0x100) + buf[pos++]; + return len; +} + + +inline int +objc_read_unsigned_long (struct objc_typed_stream *stream, + unsigned long *value) +{ + unsigned char buf[sizeof (unsigned long) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + if ((buf[0] & _B_CODE) == _B_SINT) + (*value) = (buf[0] & _B_VALUE); + + else + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), value); + + } + return len; +} + +inline int +objc_read_string (struct objc_typed_stream *stream, + char **string) +{ + unsigned char buf[sizeof (unsigned int) + 1]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + switch (buf[0]&_B_CODE) { + case _B_SSTR: + { + int length = buf[0]&_B_VALUE; + (*string) = (char*)objc_malloc (length + 1); + if (key) + objc_hash_add (&stream->stream_table, LONG2PTR(key), *string); + len = (*stream->read) (stream->physical, *string, length); + (*string)[length] = '\0'; + } + break; + + case _B_UCOMM: + { + char *tmp; + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + tmp = objc_hash_value_for_key (stream->stream_table, LONG2PTR (key)); + *string = objc_malloc (strlen (tmp) + 1); + strcpy (*string, tmp); + } + break; + + case _B_NSTR: + { + unsigned int nbytes = buf[0]&_B_VALUE; + len = __objc_read_nbyte_uint (stream, nbytes, &nbytes); + if (len) { + (*string) = (char*)objc_malloc (nbytes + 1); + if (key) + objc_hash_add (&stream->stream_table, LONG2PTR(key), *string); + len = (*stream->read) (stream->physical, *string, nbytes); + (*string)[nbytes] = '\0'; + } + } + break; + + default: + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected string, got opcode %c\n", (buf[0]&_B_CODE)); + } + } + + return len; +} + + +int +objc_read_object (struct objc_typed_stream *stream, id *object) +{ + unsigned char buf[sizeof (unsigned int)]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + SEL read_sel = sel_get_any_uid ("read:"); + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register common */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + if (buf[0] == (_B_EXT | _BX_OBJECT)) + { + Class class; + + /* get class */ + len = objc_read_class (stream, &class); + + /* create instance */ + (*object) = class_create_instance (class); + + /* register? */ + if (key) + objc_hash_add (&stream->object_table, LONG2PTR(key), *object); + + /* send -read: */ + if (__objc_responds_to (*object, read_sel)) + (*get_imp (class, read_sel)) (*object, read_sel, stream); + + /* check null-byte */ + len = (*stream->read) (stream->physical, (char*)buf, 1); + if (buf[0] != '\0') + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected null-byte, got opcode %c", buf[0]); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + (*object) = objc_hash_value_for_key (stream->object_table, + LONG2PTR(key)); + } + + else if (buf[0] == (_B_EXT | _BX_OBJREF)) /* a forward reference */ + { + struct objc_list *other; + len = objc_read_unsigned_long (stream, &key); + other + = (struct objc_list *) objc_hash_value_for_key (stream->object_refs, + LONG2PTR(key)); + objc_hash_add (&stream->object_refs, LONG2PTR(key), + (void *)list_cons (object, other)); + } + + else if (buf[0] == (_B_EXT | _BX_OBJROOT)) /* a root object */ + { + if (key) + objc_error (nil, OBJC_ERR_BAD_KEY, + "cannot register root object..."); + len = objc_read_object (stream, object); + __objc_finish_read_root_object (stream); + } + + else + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected object, got opcode %c", buf[0]); + } + return len; +} + +static int +objc_read_class (struct objc_typed_stream *stream, Class *class) +{ + unsigned char buf[sizeof (unsigned int)]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + if (buf[0] == (_B_EXT | _BX_CLASS)) + { + char temp[1] = ""; + char *class_name = temp; + unsigned long version; + + /* get class */ + len = objc_read_string (stream, &class_name); + (*class) = objc_get_class (class_name); + objc_free (class_name); + + /* register */ + if (key) + objc_hash_add (&stream->stream_table, LONG2PTR(key), *class); + + objc_read_unsigned_long (stream, &version); + objc_hash_add (&stream->class_table, + (*class)->name, (void *) ((size_t) version)); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + *class = objc_hash_value_for_key (stream->stream_table, + LONG2PTR(key)); + if (! *class) + objc_error (nil, OBJC_ERR_BAD_CLASS, + "cannot find class for key %lu", key); + } + + else + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected class, got opcode %c", buf[0]); + } + return len; +} + +int +objc_read_selector (struct objc_typed_stream *stream, SEL* selector) +{ + unsigned char buf[sizeof (unsigned int)]; + int len; + if ((len = (*stream->read) (stream->physical, (char*)buf, 1))) + { + unsigned long key = 0; + + if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ + { + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + len = (*stream->read) (stream->physical, (char*)buf, 1); + } + + if (buf[0] == (_B_EXT|_BX_SEL)) /* selector! */ + { + char temp[1] = ""; + char *selector_name = temp; + + /* get selector */ + len = objc_read_string (stream, &selector_name); + /* To handle NULL selectors */ + if (0 == strlen (selector_name)) + { + (*selector) = (SEL)0; + return 0; + } + else + (*selector) = sel_get_any_uid (selector_name); + objc_free (selector_name); + + /* register */ + if (key) + objc_hash_add (&stream->stream_table, + LONG2PTR(key), (void *) *selector); + } + + else if ((buf[0]&_B_CODE) == _B_UCOMM) + { + if (key) + objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); + len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); + (*selector) = objc_hash_value_for_key (stream->stream_table, + LONG2PTR(key)); + } + + else + objc_error (nil, OBJC_ERR_BAD_DATA, + "expected selector, got opcode %c", buf[0]); + } + return len; +} + +/* +** USER LEVEL FUNCTIONS +*/ + +/* +** Write one object, encoded in TYPE and pointed to by DATA to the +** typed stream STREAM. +*/ + +int +objc_write_type (TypedStream *stream, const char *type, const void *data) +{ + switch (*type) { + case _C_ID: + return objc_write_object (stream, *(id *) data); + break; + + case _C_CLASS: + return objc_write_class (stream, *(Class *) data); + break; + + case _C_SEL: + return objc_write_selector (stream, *(SEL *) data); + break; + + case _C_CHR: + return objc_write_char (stream, *(signed char *) data); + break; + + case _C_UCHR: + return objc_write_unsigned_char (stream, *(unsigned char *) data); + break; + + case _C_SHT: + return objc_write_short (stream, *(short *) data); + break; + + case _C_USHT: + return objc_write_unsigned_short (stream, *(unsigned short *) data); + break; + + case _C_INT: + return objc_write_int (stream, *(int *) data); + break; + + case _C_UINT: + return objc_write_unsigned_int (stream, *(unsigned int *) data); + break; + + case _C_LNG: + return objc_write_long (stream, *(long *) data); + break; + + case _C_ULNG: + return objc_write_unsigned_long (stream, *(unsigned long *) data); + break; + + case _C_CHARPTR: + return objc_write_string (stream, + *(unsigned char **) data, strlen (*(char **) data)); + break; + + case _C_ATOM: + return objc_write_string_atomic (stream, *(unsigned char **) data, + strlen (*(char **) data)); + break; + + case _C_ARY_B: + { + int len = atoi (type + 1); + while (isdigit ((unsigned char) *++type)) + ; + return objc_write_array (stream, type, len, data); + } + break; + + case _C_STRUCT_B: + { + int acc_size = 0; + int align; + while (*type != _C_STRUCT_E && *type++ != '=') + ; /* skip "=" */ + while (*type != _C_STRUCT_E) + { + align = objc_alignof_type (type); /* padd to alignment */ + acc_size = ROUND (acc_size, align); + objc_write_type (stream, type, ((char *) data) + acc_size); + acc_size += objc_sizeof_type (type); /* add component size */ + type = objc_skip_typespec (type); /* skip component */ + } + return 1; + } + + default: + { + objc_error (nil, OBJC_ERR_BAD_TYPE, + "objc_write_type: cannot parse typespec: %s\n", type); + return 0; + } + } +} + +/* +** Read one object, encoded in TYPE and pointed to by DATA to the +** typed stream STREAM. DATA specifies the address of the types to +** read. Expected type is checked against the type actually present +** on the stream. +*/ + +int +objc_read_type(TypedStream *stream, const char *type, void *data) +{ + char c; + switch (c = *type) { + case _C_ID: + return objc_read_object (stream, (id*)data); + break; + + case _C_CLASS: + return objc_read_class (stream, (Class*)data); + break; + + case _C_SEL: + return objc_read_selector (stream, (SEL*)data); + break; + + case _C_CHR: + return objc_read_char (stream, (char*)data); + break; + + case _C_UCHR: + return objc_read_unsigned_char (stream, (unsigned char*)data); + break; + + case _C_SHT: + return objc_read_short (stream, (short*)data); + break; + + case _C_USHT: + return objc_read_unsigned_short (stream, (unsigned short*)data); + break; + + case _C_INT: + return objc_read_int (stream, (int*)data); + break; + + case _C_UINT: + return objc_read_unsigned_int (stream, (unsigned int*)data); + break; + + case _C_LNG: + return objc_read_long (stream, (long*)data); + break; + + case _C_ULNG: + return objc_read_unsigned_long (stream, (unsigned long*)data); + break; + + case _C_CHARPTR: + case _C_ATOM: + return objc_read_string (stream, (char**)data); + break; + + case _C_ARY_B: + { + int len = atoi (type + 1); + while (isdigit ((unsigned char) *++type)) + ; + return objc_read_array (stream, type, len, data); + } + break; + + case _C_STRUCT_B: + { + int acc_size = 0; + int align; + while (*type != _C_STRUCT_E && *type++ != '=') + ; /* skip "=" */ + while (*type != _C_STRUCT_E) + { + align = objc_alignof_type (type); /* padd to alignment */ + acc_size = ROUND (acc_size, align); + objc_read_type (stream, type, ((char*)data)+acc_size); + acc_size += objc_sizeof_type (type); /* add component size */ + type = objc_skip_typespec (type); /* skip component */ + } + return 1; + } + + default: + { + objc_error (nil, OBJC_ERR_BAD_TYPE, + "objc_read_type: cannot parse typespec: %s\n", type); + return 0; + } + } +} + +/* +** Write the object specified by the template TYPE to STREAM. Last +** arguments specify addresses of values to be written. It might +** seem surprising to specify values by address, but this is extremely +** convenient for copy-paste with objc_read_types calls. A more +** down-to-the-earth cause for this passing of addresses is that values +** of arbitrary size is not well supported in ANSI C for functions with +** variable number of arguments. +*/ + +int +objc_write_types (TypedStream *stream, const char *type, ...) +{ + va_list args; + const char *c; + int res = 0; + + va_start(args, type); + + for (c = type; *c; c = objc_skip_typespec (c)) + { + switch (*c) { + case _C_ID: + res = objc_write_object (stream, *va_arg (args, id*)); + break; + + case _C_CLASS: + res = objc_write_class (stream, *va_arg (args, Class*)); + break; + + case _C_SEL: + res = objc_write_selector (stream, *va_arg (args, SEL*)); + break; + + case _C_CHR: + res = objc_write_char (stream, *va_arg (args, char*)); + break; + + case _C_UCHR: + res = objc_write_unsigned_char (stream, + *va_arg (args, unsigned char*)); + break; + + case _C_SHT: + res = objc_write_short (stream, *va_arg (args, short*)); + break; + + case _C_USHT: + res = objc_write_unsigned_short (stream, + *va_arg (args, unsigned short*)); + break; + + case _C_INT: + res = objc_write_int(stream, *va_arg (args, int*)); + break; + + case _C_UINT: + res = objc_write_unsigned_int(stream, *va_arg (args, unsigned int*)); + break; + + case _C_LNG: + res = objc_write_long(stream, *va_arg (args, long*)); + break; + + case _C_ULNG: + res = objc_write_unsigned_long(stream, *va_arg (args, unsigned long*)); + break; + + case _C_CHARPTR: + { + unsigned char **str = va_arg (args, unsigned char **); + res = objc_write_string (stream, *str, strlen ((char*)*str)); + } + break; + + case _C_ATOM: + { + unsigned char **str = va_arg (args, unsigned char **); + res = objc_write_string_atomic (stream, *str, strlen ((char*)*str)); + } + break; + + case _C_ARY_B: + { + int len = atoi (c + 1); + const char *t = c; + while (isdigit ((unsigned char) *++t)) + ; + res = objc_write_array (stream, t, len, va_arg (args, void *)); + t = objc_skip_typespec (t); + if (*t != _C_ARY_E) + objc_error (nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t); + } + break; + + default: + objc_error (nil, OBJC_ERR_BAD_TYPE, + "objc_write_types: cannot parse typespec: %s\n", type); + } + } + va_end(args); + return res; +} + + +/* +** Last arguments specify addresses of values to be read. Expected +** type is checked against the type actually present on the stream. +*/ + +int +objc_read_types(TypedStream *stream, const char *type, ...) +{ + va_list args; + const char *c; + int res = 0; + + va_start (args, type); + + for (c = type; *c; c = objc_skip_typespec(c)) + { + switch (*c) { + case _C_ID: + res = objc_read_object(stream, va_arg (args, id*)); + break; + + case _C_CLASS: + res = objc_read_class(stream, va_arg (args, Class*)); + break; + + case _C_SEL: + res = objc_read_selector(stream, va_arg (args, SEL*)); + break; + + case _C_CHR: + res = objc_read_char(stream, va_arg (args, char*)); + break; + + case _C_UCHR: + res = objc_read_unsigned_char(stream, va_arg (args, unsigned char*)); + break; + + case _C_SHT: + res = objc_read_short(stream, va_arg (args, short*)); + break; + + case _C_USHT: + res = objc_read_unsigned_short(stream, va_arg (args, unsigned short*)); + break; + + case _C_INT: + res = objc_read_int(stream, va_arg (args, int*)); + break; + + case _C_UINT: + res = objc_read_unsigned_int(stream, va_arg (args, unsigned int*)); + break; + + case _C_LNG: + res = objc_read_long(stream, va_arg (args, long*)); + break; + + case _C_ULNG: + res = objc_read_unsigned_long(stream, va_arg (args, unsigned long*)); + break; + + case _C_CHARPTR: + case _C_ATOM: + { + char **str = va_arg (args, char **); + res = objc_read_string (stream, str); + } + break; + + case _C_ARY_B: + { + int len = atoi (c + 1); + const char *t = c; + while (isdigit ((unsigned char) *++t)) + ; + res = objc_read_array (stream, t, len, va_arg (args, void *)); + t = objc_skip_typespec (t); + if (*t != _C_ARY_E) + objc_error (nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t); + } + break; + + default: + objc_error (nil, OBJC_ERR_BAD_TYPE, + "objc_read_types: cannot parse typespec: %s\n", type); + } + } + va_end (args); + return res; +} + +/* +** Write an array of COUNT elements of TYPE from the memory address DATA. +** This is equivalent of objc_write_type (stream, "[N]", data) +*/ + +int +objc_write_array (TypedStream *stream, const char *type, + int count, const void *data) +{ + int off = objc_sizeof_type(type); + const char *where = data; + + while (count-- > 0) + { + objc_write_type(stream, type, where); + where += off; + } + return 1; +} + +/* +** Read an array of COUNT elements of TYPE into the memory address +** DATA. The memory pointed to by data is supposed to be allocated +** by the callee. This is equivalent of +** objc_read_type (stream, "[N]", data) +*/ + +int +objc_read_array (TypedStream *stream, const char *type, + int count, void *data) +{ + int off = objc_sizeof_type(type); + char *where = (char*)data; + + while (count-- > 0) + { + objc_read_type(stream, type, where); + where += off; + } + return 1; +} + +static int +__objc_fread (FILE *file, char *data, int len) +{ + return fread(data, len, 1, file); +} + +static int +__objc_fwrite (FILE *file, char *data, int len) +{ + return fwrite(data, len, 1, file); +} + +static int +__objc_feof (FILE *file) +{ + return feof(file); +} + +static int +__objc_no_write (FILE *file __attribute__ ((__unused__)), + const char *data __attribute__ ((__unused__)), + int len __attribute__ ((__unused__))) +{ + objc_error (nil, OBJC_ERR_NO_WRITE, "TypedStream not open for writing"); + return 0; +} + +static int +__objc_no_read (FILE *file __attribute__ ((__unused__)), + const char *data __attribute__ ((__unused__)), + int len __attribute__ ((__unused__))) +{ + objc_error (nil, OBJC_ERR_NO_READ, "TypedStream not open for reading"); + return 0; +} + +static int +__objc_read_typed_stream_signature (TypedStream *stream) +{ + char buffer[80]; + int pos = 0; + do + (*stream->read) (stream->physical, buffer+pos, 1); + while (buffer[pos++] != '\0') + ; + sscanf (buffer, "GNU TypedStream %d", &stream->version); + if (stream->version != OBJC_TYPED_STREAM_VERSION) + objc_error (nil, OBJC_ERR_STREAM_VERSION, + "cannot handle TypedStream version %d", stream->version); + return 1; +} + +static int +__objc_write_typed_stream_signature (TypedStream *stream) +{ + char buffer[80]; + sprintf(buffer, "GNU TypedStream %d", OBJC_TYPED_STREAM_VERSION); + stream->version = OBJC_TYPED_STREAM_VERSION; + (*stream->write) (stream->physical, buffer, strlen (buffer) + 1); + return 1; +} + +static void __objc_finish_write_root_object(struct objc_typed_stream *stream) +{ + objc_hash_delete (stream->object_table); + stream->object_table = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); +} + +static void __objc_finish_read_root_object(struct objc_typed_stream *stream) +{ + node_ptr node; + SEL awake_sel = sel_get_any_uid ("awake"); + cache_ptr free_list = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + + /* resolve object forward references */ + for (node = objc_hash_next (stream->object_refs, NULL); node; + node = objc_hash_next (stream->object_refs, node)) + { + struct objc_list *reflist = node->value; + const void *key = node->key; + id object = objc_hash_value_for_key (stream->object_table, key); + while (reflist) + { + *((id*) reflist->head) = object; + if (objc_hash_value_for_key (free_list,reflist) == NULL) + objc_hash_add (&free_list,reflist,reflist); + + reflist = reflist->tail; + } + } + + /* apply __objc_free to all objects stored in free_list */ + for (node = objc_hash_next (free_list, NULL); node; + node = objc_hash_next (free_list, node)) + objc_free ((void *) node->key); + + objc_hash_delete (free_list); + + /* empty object reference table */ + objc_hash_delete (stream->object_refs); + stream->object_refs = objc_hash_new (8, (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + + /* call -awake for all objects read */ + if (awake_sel) + { + for (node = objc_hash_next (stream->object_table, NULL); node; + node = objc_hash_next (stream->object_table, node)) + { + id object = node->value; + if (__objc_responds_to (object, awake_sel)) + (*objc_msg_lookup (object, awake_sel)) (object, awake_sel); + } + } + + /* empty object table */ + objc_hash_delete (stream->object_table); + stream->object_table = objc_hash_new(64, + (hash_func_type)objc_hash_ptr, + (compare_func_type)objc_compare_ptrs); +} + +/* +** Open the stream PHYSICAL in MODE +*/ + +TypedStream * +objc_open_typed_stream (FILE *physical, int mode) +{ + TypedStream *s = (TypedStream *) objc_malloc (sizeof (TypedStream)); + + s->mode = mode; + s->physical = physical; + s->stream_table = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + s->object_table = objc_hash_new (64, + (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + s->eof = (objc_typed_eof_func) __objc_feof; + s->flush = (objc_typed_flush_func) fflush; + s->writing_root_p = 0; + if (mode == OBJC_READONLY) + { + s->class_table + = objc_hash_new (8, (hash_func_type) objc_hash_string, + (compare_func_type) objc_compare_strings); + s->object_refs = objc_hash_new (8, (hash_func_type) objc_hash_ptr, + (compare_func_type) objc_compare_ptrs); + s->read = (objc_typed_read_func) __objc_fread; + s->write = (objc_typed_write_func) __objc_no_write; + __objc_read_typed_stream_signature (s); + } + else if (mode == OBJC_WRITEONLY) + { + s->class_table = 0; + s->object_refs = 0; + s->read = (objc_typed_read_func) __objc_no_read; + s->write = (objc_typed_write_func) __objc_fwrite; + __objc_write_typed_stream_signature (s); + } + else + { + objc_close_typed_stream (s); + return NULL; + } + s->type = OBJC_FILE_STREAM; + return s; +} + +/* +** Open the file named by FILE_NAME in MODE +*/ + +TypedStream* +objc_open_typed_stream_for_file (const char *file_name, int mode) +{ + FILE *file = NULL; + TypedStream *s; + + if (mode == OBJC_READONLY) + file = fopen (file_name, "r"); + else + file = fopen (file_name, "w"); + + if (file) + { + s = objc_open_typed_stream (file, mode); + if (s) + s->type |= OBJC_MANAGED_STREAM; + return s; + } + else + return NULL; +} + +/* +** Close STREAM freeing the structure it self. If it was opened with +** objc_open_typed_stream_for_file, the file will also be closed. +*/ + +void +objc_close_typed_stream (TypedStream *stream) +{ + if (stream->mode == OBJC_READONLY) + { + __objc_finish_read_root_object (stream); /* Just in case... */ + objc_hash_delete (stream->class_table); + objc_hash_delete (stream->object_refs); + } + + objc_hash_delete (stream->stream_table); + objc_hash_delete (stream->object_table); + + if (stream->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM)) + fclose ((FILE *)stream->physical); + + objc_free(stream); +} + +BOOL +objc_end_of_typed_stream (TypedStream *stream) +{ + return (*stream->eof) (stream->physical); +} + +void +objc_flush_typed_stream (TypedStream *stream) +{ + (*stream->flush) (stream->physical); +} + +long +objc_get_stream_class_version (TypedStream *stream, Class class) +{ + if (stream->class_table) + return PTR2LONG(objc_hash_value_for_key (stream->class_table, + class->name)); + else + return class_get_version (class); +} + diff --git a/blocks_runtime.m b/blocks_runtime.m new file mode 100644 index 0000000..018f467 --- /dev/null +++ b/blocks_runtime.m @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2009 Remy Demarest + * Portions Copyright (c) 2009 David Chisnall + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#import "objc/blocks_runtime.h" +#import "objc/runtime.h" +#include +#include +#include +#include + +// FIXME: Before we finalise the ABI for blocks, I wish to modify clang to emit +// type information for the block function. + +/* Makes the compiler happy even without Foundation */ +@interface Dummy +- (id)retain; +- (void)release; +@end + +// Descriptor attributes +enum { + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29), // interim until complete world build is accomplished +}; + +// _Block_object_assign() and _Block_object_dispose() flag helpers. +enum { + BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ... + BLOCK_FIELD_IS_BLOCK = 7, // a block variable + BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable + + BLOCK_FIELD_IS_WEAK = 16, // declared __weak + + BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers +}; + +// Helper structure +struct psy_block_literal { + void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock + int flags; + int reserved; + void (*invoke)(void *, ...); + struct { + unsigned long int reserved; // NULL + unsigned long int size; // sizeof(struct Block_literal_1) + // optional helper functions + void (*copy_helper)(void *dst, void *src); + void (*dispose_helper)(void *src); + } *descriptor; +}; + +// Helper structure +struct psy_block_byref_obj { + void *isa; // uninitialized + struct psy_block_byref_obj *forwarding; + int flags; //refcount; + int size; + void (*byref_keep)(struct psy_block_byref_obj *dst, struct psy_block_byref_obj *src); + void (*byref_dispose)(struct psy_block_byref_obj *); +}; + +/* Certain field types require runtime assistance when being copied to the + * heap. The following function is used to copy fields of types: blocks, + * pointers to byref structures, and objects (including + * __attribute__((NSObject)) pointers. BLOCK_FIELD_IS_WEAK is orthogonal to + * the other choices which are mutually exclusive. Only in a Block copy helper + * will one see BLOCK_FIELD_IS_BYREF. + */ +void _Block_object_assign(void *destAddr, void *object, const int flags) +{ + //printf("Copying %x to %x with flags %x\n", object, destAddr, flags); + // FIXME: Needs to be implemented + if(flags & BLOCK_FIELD_IS_WEAK) + { + } + else + { + if(flags & BLOCK_FIELD_IS_BYREF) + { + struct psy_block_byref_obj *src = object; + struct psy_block_byref_obj **dst = destAddr; + + /* I followed Apple's specs saying byref's "flags" field should + * represent the refcount but it still contains real flag, so this + * is a little hack... + */ + if((src->flags & ~BLOCK_HAS_COPY_DISPOSE) == 0) + { + *dst = malloc(src->size); + memcpy(*dst, src, src->size); + if (src->forwarding == src) + { + (*dst)->forwarding = *dst; + } + if(src->size >= sizeof(struct psy_block_byref_obj)) + { + src->byref_keep(*dst, src); + } + } + else *dst = src; + + (*dst)->flags++; + } + else if((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) + { + struct psy_block_literal *src = object; + struct psy_block_literal **dst = destAddr; + + *dst = Block_copy(src); + } + else if((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_OBJECT) + { + id src = object; + id *dst = destAddr; + *dst = [src retain]; + } + } +} + +/* Similarly a compiler generated dispose helper needs to call back for each + * field of the byref data structure. (Currently the implementation only packs + * one field into the byref structure but in principle there could be more). + * The same flags used in the copy helper should be used for each call + * generated to this function: + */ +void _Block_object_dispose(void *object, const int flags) +{ + // FIXME: Needs to be implemented + if(flags & BLOCK_FIELD_IS_WEAK) + { + } + else + { + if(flags & BLOCK_FIELD_IS_BYREF) + { + struct psy_block_byref_obj *src = object; + + src->flags--; + if((src->flags & ~BLOCK_HAS_COPY_DISPOSE) == 0) + { + if(src->size >= sizeof(struct psy_block_byref_obj)) + src->byref_dispose(src); + + free(src); + } + } + else if((flags & ~BLOCK_BYREF_CALLER) == BLOCK_FIELD_IS_BLOCK) + { + struct psy_block_literal *src = object; + Block_release(src); + } + else if((flags & ~BLOCK_BYREF_CALLER) == BLOCK_FIELD_IS_OBJECT) + { + id src = object; + [src release]; + } + } +} + +struct StackBlockClass { + void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock + int flags; + int reserved; + void (*invoke)(void *, ...); + struct { + unsigned long int reserved; // NULL + unsigned long int size; // sizeof(struct Block_literal_1) + // optional helper functions + void (*copy_helper)(void *dst, void *src); + void (*dispose_helper)(void *src); + } *descriptor; +}; + + +// Copy a block to the heap if it's still on the stack or increments its retain count. +// The block is considered on the stack if self->descriptor->reserved == 0. +void *Block_copy(void *src) +{ + struct StackBlockClass *self = src; + struct StackBlockClass *ret = self; + + extern void _NSConcreteStackBlock __attribute__((weak)); + + // If the block is Global, there's no need to copy it on the heap. + if(self->isa == &_NSConcreteStackBlock && self->flags & BLOCK_HAS_DESCRIPTOR) + { + if(self->reserved == 0) + { + ret = malloc(self->descriptor->size); + memcpy(ret, self, self->descriptor->size); + if(self->flags & BLOCK_HAS_COPY_DISPOSE) + self->descriptor->copy_helper(ret, self); + memcpy(self, ret, self->descriptor->size); + } + ret->reserved++; + } + return ret; +} + +// Release a block and frees the memory when the retain count hits zero. +void Block_release(void *src) +{ + struct StackBlockClass *self = src; + + extern void _NSConcreteStackBlock __attribute__((weak)); + + if(self->isa == &_NSConcreteStackBlock && // A Global block doesn't need to be released + self->flags & BLOCK_HAS_DESCRIPTOR && // Should always be true... + self->reserved > 0) // If false, then it's not allocated on the heap, we won't release auto memory ! + { + self->reserved--; + if(self->reserved == 0) + { + if(self->flags & BLOCK_HAS_COPY_DISPOSE) + self->descriptor->dispose_helper(self); + free(self); + } + } +} diff --git a/class.c b/class.c new file mode 100644 index 0000000..615d29c --- /dev/null +++ b/class.c @@ -0,0 +1,710 @@ +/* GNU Objective C Runtime class related functions + Copyright (C) 1993, 1995, 1996, 1997, 2001, 2002, 2009 + Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup and Dennis Glatting. + + Lock-free class table code designed and written from scratch by + Nicola Pero, 2001. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* + The code in this file critically affects class method invocation + speed. This long preamble comment explains why, and the issues + involved. + + + One of the traditional weaknesses of the GNU Objective-C runtime is + that class method invocations are slow. The reason is that when you + write + + array = [NSArray new]; + + this gets basically compiled into the equivalent of + + array = [(objc_get_class ("NSArray")) new]; + + objc_get_class returns the class pointer corresponding to the string + `NSArray'; and because of the lookup, the operation is more + complicated and slow than a simple instance method invocation. + + Most high performance Objective-C code (using the GNU Objc runtime) + I had the opportunity to read (or write) work around this problem by + caching the class pointer: + + Class arrayClass = [NSArray class]; + + ... later on ... + + array = [arrayClass new]; + array = [arrayClass new]; + array = [arrayClass new]; + + In this case, you always perform a class lookup (the first one), but + then all the [arrayClass new] methods run exactly as fast as an + instance method invocation. It helps if you have many class method + invocations to the same class. + + The long-term solution to this problem would be to modify the + compiler to output tables of class pointers corresponding to all the + class method invocations, and to add code to the runtime to update + these tables - that should in the end allow class method invocations + to perform precisely as fast as instance method invocations, because + no class lookup would be involved. I think the Apple Objective-C + runtime uses this technique. Doing this involves synchronized + modifications in the runtime and in the compiler. + + As a first medicine to the problem, I [NP] have redesigned and + rewritten the way the runtime is performing class lookup. This + doesn't give as much speed as the other (definitive) approach, but + at least a class method invocation now takes approximately 4.5 times + an instance method invocation on my machine (it would take approx 12 + times before the rewriting), which is a lot better. + + One of the main reason the new class lookup is so faster is because + I implemented it in a way that can safely run multithreaded without + using locks - a so-called `lock-free' data structure. The atomic + operation is pointer assignment. The reason why in this problem + lock-free data structures work so well is that you never remove + classes from the table - and the difficult thing with lock-free data + structures is freeing data when is removed from the structures. */ + +#include "objc/runtime-legacy.h" /* the kitchen sink */ +#include "objc/sarray.h" + +#include "objc/objc.h" +#include "objc/objc-api.h" +#include "objc/thr.h" + +/* We use a table which maps a class name to the corresponding class + * pointer. The first part of this file defines this table, and + * functions to do basic operations on the table. The second part of + * the file implements some higher level Objective-C functionality for + * classes by using the functions provided in the first part to manage + * the table. */ + +/** + ** Class Table Internals + **/ + +/* A node holding a class */ +typedef struct class_node +{ + struct class_node *next; /* Pointer to next entry on the list. + NULL indicates end of list. */ + + const char *name; /* The class name string */ + int length; /* The class name string length */ + Class pointer; /* The Class pointer */ + +} *class_node_ptr; + +/* A table containing classes is a class_node_ptr (pointing to the + first entry in the table - if it is NULL, then the table is + empty). */ + +/* We have 1024 tables. Each table contains all class names which + have the same hash (which is a number between 0 and 1023). To look + up a class_name, we compute its hash, and get the corresponding + table. Once we have the table, we simply compare strings directly + till we find the one which we want (using the length first). The + number of tables is quite big on purpose (a normal big application + has less than 1000 classes), so that you shouldn't normally get any + collisions, and get away with a single comparison (which we can't + avoid since we need to know that you have got the right thing). */ +#define CLASS_TABLE_SIZE 1024 +#define CLASS_TABLE_MASK 1023 + +#include "lock.h" + +static class_node_ptr class_table_array[CLASS_TABLE_SIZE]; + +/* The table writing mutex - we lock on writing to avoid conflicts + between different writers, but we read without locks. That is + possible because we assume pointer assignment to be an atomic + operation. */ +static mutex_t __class_table_lock; + +/* CLASS_TABLE_HASH is how we compute the hash of a class name. It is + a macro - *not* a function - arguments *are* modified directly. + + INDEX should be a variable holding an int; + HASH should be a variable holding an int; + CLASS_NAME should be a variable holding a (char *) to the class_name. + + After the macro is executed, INDEX contains the length of the + string, and HASH the computed hash of the string; CLASS_NAME is + untouched. */ + +#define CLASS_TABLE_HASH(INDEX, HASH, CLASS_NAME) \ + HASH = 0; \ + for (INDEX = 0; CLASS_NAME[INDEX] != '\0'; INDEX++) \ + { \ + HASH = (HASH << 4) ^ (HASH >> 28) ^ CLASS_NAME[INDEX]; \ + } \ + \ + HASH = (HASH ^ (HASH >> 10) ^ (HASH >> 20)) & CLASS_TABLE_MASK; + +/* Setup the table. */ +static void +class_table_setup (void) +{ + /* Start - nothing in the table. */ + memset (class_table_array, 0, sizeof (class_node_ptr) * CLASS_TABLE_SIZE); + + /* The table writing mutex. */ + INIT_LOCK(__class_table_lock); +} + + +/* Insert a class in the table (used when a new class is registered). */ +static void +class_table_insert (const char *class_name, Class class_pointer) +{ + int hash, length; + class_node_ptr new_node; + + /* Find out the class name's hash and length. */ + CLASS_TABLE_HASH (length, hash, class_name); + + /* Prepare the new node holding the class. */ + new_node = objc_malloc (sizeof (struct class_node)); + new_node->name = class_name; + new_node->length = length; + new_node->pointer = class_pointer; + + /* Lock the table for modifications. */ + LOCK(&__class_table_lock); + + /* Insert the new node in the table at the beginning of the table at + class_table_array[hash]. */ + new_node->next = class_table_array[hash]; + class_table_array[hash] = new_node; + + UNLOCK(&__class_table_lock); +} + +/* Replace a class in the table (used only by poseAs:). */ +static void +class_table_replace (Class old_class_pointer, Class new_class_pointer) +{ + int hash; + class_node_ptr node; + + LOCK(&__class_table_lock); + + hash = 0; + node = class_table_array[hash]; + + while (hash < CLASS_TABLE_SIZE) + { + if (node == NULL) + { + hash++; + if (hash < CLASS_TABLE_SIZE) + { + node = class_table_array[hash]; + } + } + else + { + Class class1 = node->pointer; + + if (class1 == old_class_pointer) + { + node->pointer = new_class_pointer; + } + node = node->next; + } + } + + UNLOCK(&__class_table_lock); +} + + +/* Get a class from the table. This does not need mutex protection. + Currently, this function is called each time you call a static + method, this is why it must be very fast. */ +static inline Class +class_table_get_safe (const char *class_name) +{ + class_node_ptr node; + int length, hash; + + /* Compute length and hash. */ + CLASS_TABLE_HASH (length, hash, class_name); + + node = class_table_array[hash]; + + if (node != NULL) + { + do + { + if (node->length == length) + { + /* Compare the class names. */ + int i; + + if (strcmp(node->name, class_name) == 0) + { + return node->pointer; + } +#if 0 + for (i = 0; i < length; i++) + { + if ((node->name)[i] != class_name[i]) + { + break; + } + } + + if (i == length) + { + /* They are equal! */ + return node->pointer; + } +#endif + } + } + while ((node = node->next) != NULL); + } + + return Nil; +} + +/* Enumerate over the class table. */ +struct class_table_enumerator +{ + int hash; + class_node_ptr node; +}; + + +static Class +class_table_next (struct class_table_enumerator **e) +{ + struct class_table_enumerator *enumerator = *e; + class_node_ptr next; + + if (enumerator == NULL) + { + *e = objc_malloc (sizeof (struct class_table_enumerator)); + enumerator = *e; + enumerator->hash = 0; + enumerator->node = NULL; + + next = class_table_array[enumerator->hash]; + } + else + { + next = enumerator->node->next; + } + + if (next != NULL) + { + enumerator->node = next; + return enumerator->node->pointer; + } + else + { + enumerator->hash++; + + while (enumerator->hash < CLASS_TABLE_SIZE) + { + next = class_table_array[enumerator->hash]; + if (next != NULL) + { + enumerator->node = next; + return enumerator->node->pointer; + } + enumerator->hash++; + } + + /* Ok - table finished - done. */ + objc_free (enumerator); + return Nil; + } +} + +#if 0 /* DEBUGGING FUNCTIONS */ +/* Debugging function - print the class table. */ +void +class_table_print (void) +{ + int i; + + for (i = 0; i < CLASS_TABLE_SIZE; i++) + { + class_node_ptr node; + + printf ("%d:\n", i); + node = class_table_array[i]; + + while (node != NULL) + { + printf ("\t%s\n", node->name); + node = node->next; + } + } +} + +/* Debugging function - print an histogram of number of classes in + function of hash key values. Useful to evaluate the hash function + in real cases. */ +void +class_table_print_histogram (void) +{ + int i, j; + int counter = 0; + + for (i = 0; i < CLASS_TABLE_SIZE; i++) + { + class_node_ptr node; + + node = class_table_array[i]; + + while (node != NULL) + { + counter++; + node = node->next; + } + if (((i + 1) % 50) == 0) + { + printf ("%4d:", i + 1); + for (j = 0; j < counter; j++) + { + printf ("X"); + } + printf ("\n"); + counter = 0; + } + } + printf ("%4d:", i + 1); + for (j = 0; j < counter; j++) + { + printf ("X"); + } + printf ("\n"); +} +#endif /* DEBUGGING FUNCTIONS */ + +/** + ** Objective-C runtime functions + **/ + +/* From now on, the only access to the class table data structure + should be via the class_table_* functions. */ + +/* This is a hook which is called by objc_get_class and + objc_lookup_class if the runtime is not able to find the class. + This may e.g. try to load in the class using dynamic loading. */ +Class (*_objc_lookup_class) (const char *name) = 0; /* !T:SAFE */ + + +/* True when class links has been resolved. */ +BOOL __objc_class_links_resolved = NO; /* !T:UNUSED */ + + +void +__objc_init_class_tables (void) +{ + /* Allocate the class hash table. */ + + if (__class_table_lock) + return; + + LOCK(__objc_runtime_mutex); + + class_table_setup (); + + UNLOCK(__objc_runtime_mutex); +} + +/* This function adds a class to the class hash table, and assigns the + class a number, unless it's already known. */ +void +__objc_add_class_to_hash (Class class) +{ + Class h_class; + + LOCK(__objc_runtime_mutex); + + /* Make sure the table is there. */ + //assert (__class_table_lock); + + /* Make sure it's not a meta class. */ + assert (CLS_ISCLASS (class)); + + /* Check to see if the class is already in the hash table. */ + h_class = class_table_get_safe (class->name); + if (! h_class) + { + /* The class isn't in the hash table. Add the class and assign a class + number. */ + static unsigned int class_number = 1; + + CLS_SETNUMBER (class, class_number); + CLS_SETNUMBER (class->class_pointer, class_number); + + ++class_number; + class_table_insert (class->name, class); + } + + UNLOCK(__objc_runtime_mutex); +} + +/* Get the class object for the class named NAME. If NAME does not + identify a known class, the hook _objc_lookup_class is called. If + this fails, nil is returned. */ +Class +objc_lookup_class (const char *name) +{ + Class class; + + class = class_table_get_safe (name); + + if (class) + return class; + + if (_objc_lookup_class) + return (*_objc_lookup_class) (name); + else + return 0; +} + +/* Get the class object for the class named NAME. If NAME does not + identify a known class, the hook _objc_lookup_class is called. If + this fails, an error message is issued and the system aborts. */ +Class +objc_get_class (const char *name) +{ + Class class; + + class = class_table_get_safe (name); + + if (class) + return class; + + if (_objc_lookup_class) + class = (*_objc_lookup_class) (name); + + if (class) + return class; + + objc_error (nil, OBJC_ERR_BAD_CLASS, + "objc runtime: cannot find class %s\n", name); + return 0; +} + +MetaClass +objc_get_meta_class (const char *name) +{ + return objc_get_class (name)->class_pointer; +} + +/* This function provides a way to enumerate all the classes in the + executable. Pass *ENUM_STATE == NULL to start the enumeration. The + function will return 0 when there are no more classes. + For example: + id class; + void *es = NULL; + while ((class = objc_next_class (&es))) + ... do something with class; +*/ +Class +objc_next_class (void **enum_state) +{ + Class class; + + LOCK(__objc_runtime_mutex); + + /* Make sure the table is there. */ + //assert (__class_table_lock); + + class = class_table_next ((struct class_table_enumerator **) enum_state); + + UNLOCK(__objc_runtime_mutex); + + return class; +} + +/* Resolve super/subclass links for all classes. The only thing we + can be sure of is that the class_pointer for class objects point to + the right meta class objects. */ +void +__objc_resolve_class_links (void) +{ + struct class_table_enumerator *es = NULL; + Class object_class = objc_get_class ("Object"); + Class class1; + + assert (object_class); + + LOCK(__objc_runtime_mutex); + + /* Assign subclass links. */ + while ((class1 = class_table_next (&es))) + { + /* Make sure we have what we think we have. */ + assert (CLS_ISCLASS (class1)); + assert (CLS_ISMETA (class1->class_pointer)); + + /* The class_pointer of all meta classes point to Object's meta + class. */ + class1->class_pointer->class_pointer = object_class->class_pointer; + + if (! CLS_ISRESOLV (class1)) + { + CLS_SETRESOLV (class1); + CLS_SETRESOLV (class1->class_pointer); + + if (class1->super_class) + { + Class a_super_class + = objc_get_class ((char *) class1->super_class); + + assert (a_super_class); + + DEBUG_PRINTF ("making class connections for: %s\n", + class1->name); + + /* Assign subclass links for superclass. */ + class1->sibling_class = a_super_class->subclass_list; + a_super_class->subclass_list = class1; + + /* Assign subclass links for meta class of superclass. */ + if (a_super_class->class_pointer) + { + class1->class_pointer->sibling_class + = a_super_class->class_pointer->subclass_list; + a_super_class->class_pointer->subclass_list + = class1->class_pointer; + } + } + else /* A root class, make its meta object be a subclass of + Object. */ + { + class1->class_pointer->sibling_class + = object_class->subclass_list; + object_class->subclass_list = class1->class_pointer; + } + } + } + + /* Assign superclass links. */ + es = NULL; + while ((class1 = class_table_next (&es))) + { + Class sub_class; + for (sub_class = class1->subclass_list; sub_class; + sub_class = sub_class->sibling_class) + { + sub_class->super_class = class1; + if (CLS_ISCLASS (sub_class)) + sub_class->class_pointer->super_class = class1->class_pointer; + } + } + + UNLOCK(__objc_runtime_mutex); +} + + + +#define CLASSOF(c) ((c)->class_pointer) + +Class +class_pose_as (Class impostor, Class super_class) +{ + if (! CLS_ISRESOLV (impostor)) + __objc_resolve_class_links (); + + /* Preconditions */ + assert (impostor); + assert (super_class); + assert (impostor->super_class == super_class); + assert (CLS_ISCLASS (impostor)); + assert (CLS_ISCLASS (super_class)); + assert (impostor->instance_size == super_class->instance_size); + + { + Class *subclass = &(super_class->subclass_list); + + /* Move subclasses of super_class to impostor. */ + while (*subclass) + { + Class nextSub = (*subclass)->sibling_class; + + if (*subclass != impostor) + { + Class sub = *subclass; + + /* Classes */ + sub->sibling_class = impostor->subclass_list; + sub->super_class = impostor; + impostor->subclass_list = sub; + + /* It will happen that SUB is not a class object if it is + the top of the meta class hierarchy chain (root + meta-class objects inherit their class object). If + that is the case... don't mess with the meta-meta + class. */ + if (CLS_ISCLASS (sub)) + { + /* Meta classes */ + CLASSOF (sub)->sibling_class = + CLASSOF (impostor)->subclass_list; + CLASSOF (sub)->super_class = CLASSOF (impostor); + CLASSOF (impostor)->subclass_list = CLASSOF (sub); + } + } + + *subclass = nextSub; + } + + /* Set subclasses of superclass to be impostor only. */ + super_class->subclass_list = impostor; + CLASSOF (super_class)->subclass_list = CLASSOF (impostor); + + /* Set impostor to have no sibling classes. */ + impostor->sibling_class = 0; + CLASSOF (impostor)->sibling_class = 0; + } + + /* Check relationship of impostor and super_class is kept. */ + assert (impostor->super_class == super_class); + assert (CLASSOF (impostor)->super_class == CLASSOF (super_class)); + + /* This is how to update the lookup table. Regardless of what the + keys of the hashtable is, change all values that are superclass + into impostor. */ + + LOCK(__objc_runtime_mutex); + + class_table_replace (super_class, impostor); + + UNLOCK(__objc_runtime_mutex); + + /* Next, we update the dispatch tables... */ + __objc_update_dispatch_table_for_class (CLASSOF (impostor)); + __objc_update_dispatch_table_for_class (impostor); + + return impostor; +} diff --git a/encoding.c b/encoding.c new file mode 100644 index 0000000..9d2446d --- /dev/null +++ b/encoding.c @@ -0,0 +1,1080 @@ +/* Encoding of types for Objective C. + Copyright (C) 1993, 1995, 1996, 1997, 1998, 2000, 2002, 2004, 2009 + Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + Bitfield support by Ovidiu Predescu + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* FIXME: This file has no business including tm.h. */ + +#include "objc/objc-api.h" +#include "objc/encoding.h" +#include + +#undef MAX +#define MAX(X, Y) \ + ({ __typeof (X) __x = (X), __y = (Y); \ + (__x > __y ? __x : __y); }) + +#undef MIN +#define MIN(X, Y) \ + ({ __typeof (X) __x = (X), __y = (Y); \ + (__x < __y ? __x : __y); }) + +#undef ROUND +#define ROUND(V, A) \ + ({ __typeof (V) __v = (V); __typeof (A) __a = (A); \ + __a * ((__v+__a - 1)/__a); }) + + +/* Various hacks for objc_layout_record. These are used by the target + macros. */ + +#define TREE_CODE(TYPE) *(TYPE) +#define TREE_TYPE(TREE) (TREE) + +#define RECORD_TYPE _C_STRUCT_B +#define UNION_TYPE _C_UNION_B +#define QUAL_UNION_TYPE _C_UNION_B +#define ARRAY_TYPE _C_ARY_B + +#define REAL_TYPE _C_DBL + +#define VECTOR_TYPE _C_VECTOR + +#define TYPE_FIELDS(TYPE) ({const char *_field = (TYPE)+1; \ + while (*_field != _C_STRUCT_E && *_field != _C_STRUCT_B \ + && *_field != _C_UNION_B && *_field++ != '=') \ + /* do nothing */; \ + _field;}) + +#define DECL_MODE(TYPE) *(TYPE) +#define TYPE_MODE(TYPE) *(TYPE) + +#define DFmode _C_DBL + +#define strip_array_types(TYPE) ({const char *_field = (TYPE); \ + while (*_field == _C_ARY_B)\ + {\ + while (isdigit ((unsigned char)*++_field))\ + ;\ + }\ + _field;}) + +/* Some ports (eg ARM) allow the structure size boundary to be + selected at compile-time. We override the normal definition with + one that has a constant value for this compilation. */ +#ifndef BITS_PER_UNIT +#define BITS_PER_UNIT 8 +#endif +#undef STRUCTURE_SIZE_BOUNDARY +#define STRUCTURE_SIZE_BOUNDARY (BITS_PER_UNIT * sizeof (struct{char a;})) + +/* Some ROUND_TYPE_ALIGN macros use TARGET_foo, and consequently + target_flags. Define a dummy entry here to so we don't die. + We have to rename it because target_flags may already have been + declared extern. */ +#define target_flags not_target_flags +static int __attribute__ ((__unused__)) not_target_flags = 0; + +/* Some ROUND_TYPE_ALIGN use ALTIVEC_VECTOR_MODE (rs6000 darwin). + Define a dummy ALTIVEC_VECTOR_MODE so it will not die. */ +#undef ALTIVEC_VECTOR_MODE +#define ALTIVEC_VECTOR_MODE(MODE) (0) + + +/* FIXME: while this file has no business including tm.h, this + definitely has no business defining this macro but it + is only way around without really rewritting this file, + should look after the branch of 3.4 to fix this. */ +#define rs6000_special_round_type_align(STRUCT, COMPUTED, SPECIFIED) \ + ({ const char *_fields = TYPE_FIELDS (STRUCT); \ + ((_fields != 0 \ + && TYPE_MODE (strip_array_types (TREE_TYPE (_fields))) == DFmode) \ + ? MAX (MAX (COMPUTED, SPECIFIED), 64) \ + : MAX (COMPUTED, SPECIFIED));}) +/* FIXME: The word 'fixme' is insufficient to explain the wrong-ness + of this next macro definition. */ +#define darwin_rs6000_special_round_type_align(S,C,S2) \ + rs6000_special_round_type_align(S,C,S2) + +/* + return the size of an object specified by type +*/ + +int +objc_sizeof_type (const char *type) +{ + /* Skip the variable name if any */ + if (*type == '"') + { + for (type++; *type++ != '"';) + /* do nothing */; + } + + switch (*type) { + case _C_BOOL: + return sizeof (_Bool); + break; + + case _C_ID: + return sizeof (id); + break; + + case _C_CLASS: + return sizeof (Class); + break; + + case _C_SEL: + return sizeof (SEL); + break; + + case _C_CHR: + return sizeof (char); + break; + + case _C_UCHR: + return sizeof (unsigned char); + break; + + case _C_SHT: + return sizeof (short); + break; + + case _C_USHT: + return sizeof (unsigned short); + break; + + case _C_INT: + return sizeof (int); + break; + + case _C_UINT: + return sizeof (unsigned int); + break; + + case _C_LNG: + return sizeof (long); + break; + + case _C_ULNG: + return sizeof (unsigned long); + break; + + case _C_LNG_LNG: + return sizeof (long long); + break; + + case _C_ULNG_LNG: + return sizeof (unsigned long long); + break; + + case _C_FLT: + return sizeof (float); + break; + + case _C_DBL: + return sizeof (double); + break; + + case _C_VOID: + return sizeof (void); + break; + + case _C_PTR: + case _C_ATOM: + case _C_CHARPTR: + return sizeof (char *); + break; + + case _C_ARY_B: + { + int len = atoi (type + 1); + while (isdigit ((unsigned char)*++type)) + ; + return len * objc_aligned_size (type); + } + break; + + case _C_BFLD: + { + /* The new encoding of bitfields is: b 'position' 'type' 'size' */ + int position, size; + int startByte, endByte; + + position = atoi (type + 1); + while (isdigit ((unsigned char)*++type)) + ; + size = atoi (type + 1); + + startByte = position / BITS_PER_UNIT; + endByte = (position + size) / BITS_PER_UNIT; + return endByte - startByte; + } + + case _C_UNION_B: + case _C_STRUCT_B: + { + struct objc_struct_layout layout; + unsigned int size; + + objc_layout_structure (type, &layout); + while (objc_layout_structure_next_member (&layout)) + /* do nothing */ ; + objc_layout_finish_structure (&layout, &size, NULL); + + return size; + } + + case _C_COMPLEX: + { + type++; /* Skip after the 'j'. */ + switch (*type) + { + case _C_CHR: + return sizeof (_Complex char); + break; + + case _C_UCHR: + return sizeof (_Complex unsigned char); + break; + + case _C_SHT: + return sizeof (_Complex short); + break; + + case _C_USHT: + return sizeof (_Complex unsigned short); + break; + + case _C_INT: + return sizeof (_Complex int); + break; + + case _C_UINT: + return sizeof (_Complex unsigned int); + break; + + case _C_LNG: + return sizeof (_Complex long); + break; + + case _C_ULNG: + return sizeof (_Complex unsigned long); + break; + + case _C_LNG_LNG: + return sizeof (_Complex long long); + break; + + case _C_ULNG_LNG: + return sizeof (_Complex unsigned long long); + break; + + case _C_FLT: + return sizeof (_Complex float); + break; + + case _C_DBL: + return sizeof (_Complex double); + break; + + default: + { + objc_error (nil, OBJC_ERR_BAD_TYPE, "unknown complex type %s\n", + type); + return 0; + } + } + } + + default: + { + objc_error (nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type); + return 0; + } + } +} + + +/* + Return the alignment of an object specified by type +*/ + +int +objc_alignof_type (const char *type) +{ + /* Skip the variable name if any */ + if (*type == '"') + { + for (type++; *type++ != '"';) + /* do nothing */; + } + switch (*type) { + case _C_BOOL: + return __alignof__ (_Bool); + break; + + case _C_ID: + return __alignof__ (id); + break; + + case _C_CLASS: + return __alignof__ (Class); + break; + + case _C_SEL: + return __alignof__ (SEL); + break; + + case _C_CHR: + return __alignof__ (char); + break; + + case _C_UCHR: + return __alignof__ (unsigned char); + break; + + case _C_SHT: + return __alignof__ (short); + break; + + case _C_USHT: + return __alignof__ (unsigned short); + break; + + case _C_INT: + return __alignof__ (int); + break; + + case _C_UINT: + return __alignof__ (unsigned int); + break; + + case _C_LNG: + return __alignof__ (long); + break; + + case _C_ULNG: + return __alignof__ (unsigned long); + break; + + case _C_LNG_LNG: + return __alignof__ (long long); + break; + + case _C_ULNG_LNG: + return __alignof__ (unsigned long long); + break; + + case _C_FLT: + return __alignof__ (float); + break; + + case _C_DBL: + return __alignof__ (double); + break; + + case _C_PTR: + case _C_ATOM: + case _C_CHARPTR: + return __alignof__ (char *); + break; + + case _C_ARY_B: + while (isdigit ((unsigned char)*++type)) + /* do nothing */; + return objc_alignof_type (type); + + case _C_STRUCT_B: + case _C_UNION_B: + { + struct objc_struct_layout layout; + unsigned int align; + + objc_layout_structure (type, &layout); + while (objc_layout_structure_next_member (&layout)) + /* do nothing */; + objc_layout_finish_structure (&layout, NULL, &align); + + return align; + } + + + case _C_COMPLEX: + { + type++; /* Skip after the 'j'. */ + switch (*type) + { + case _C_CHR: + return __alignof__ (_Complex char); + break; + + case _C_UCHR: + return __alignof__ (_Complex unsigned char); + break; + + case _C_SHT: + return __alignof__ (_Complex short); + break; + + case _C_USHT: + return __alignof__ (_Complex unsigned short); + break; + + case _C_INT: + return __alignof__ (_Complex int); + break; + + case _C_UINT: + return __alignof__ (_Complex unsigned int); + break; + + case _C_LNG: + return __alignof__ (_Complex long); + break; + + case _C_ULNG: + return __alignof__ (_Complex unsigned long); + break; + + case _C_LNG_LNG: + return __alignof__ (_Complex long long); + break; + + case _C_ULNG_LNG: + return __alignof__ (_Complex unsigned long long); + break; + + case _C_FLT: + return __alignof__ (_Complex float); + break; + + case _C_DBL: + return __alignof__ (_Complex double); + break; + + default: + { + objc_error (nil, OBJC_ERR_BAD_TYPE, "unknown complex type %s\n", + type); + return 0; + } + } + } + + default: + { + objc_error (nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type); + return 0; + } + } +} + +/* + The aligned size if the size rounded up to the nearest alignment. +*/ + +int +objc_aligned_size (const char *type) +{ + int size, align; + + /* Skip the variable name */ + if (*type == '"') + { + for (type++; *type++ != '"';) + /* do nothing */; + } + + size = objc_sizeof_type (type); + align = objc_alignof_type (type); + + return ROUND (size, align); +} + +/* + The size rounded up to the nearest integral of the wordsize, taken + to be the size of a void *. +*/ + +int +objc_promoted_size (const char *type) +{ + int size, wordsize; + + /* Skip the variable name */ + if (*type == '"') + { + for (type++; *type++ != '"';) + /* do nothing */; + } + + size = objc_sizeof_type (type); + wordsize = sizeof (void *); + + return ROUND (size, wordsize); +} + +/* + Skip type qualifiers. These may eventually precede typespecs + occurring in method prototype encodings. +*/ + +inline const char * +objc_skip_type_qualifiers (const char *type) +{ + while (*type == _C_CONST + || *type == _C_IN + || *type == _C_INOUT + || *type == _C_OUT + || *type == _C_BYCOPY + || *type == _C_BYREF + || *type == _C_ONEWAY + || *type == _C_GCINVISIBLE) + { + type += 1; + } + return type; +} + + +/* + Skip one typespec element. If the typespec is prepended by type + qualifiers, these are skipped as well. +*/ + +const char * +objc_skip_typespec (const char *type) +{ + /* Skip the variable name if any */ + if (*type == '"') + { + for (type++; *type++ != '"';) + /* do nothing */; + } + + type = objc_skip_type_qualifiers (type); + + switch (*type) { + + case _C_ID: + /* An id may be annotated by the actual type if it is known + with the @"ClassName" syntax */ + + if (*++type != '"') + return type; + else + { + while (*++type != '"') + /* do nothing */; + return type + 1; + } + + /* The following are one character type codes */ + case _C_CLASS: + case _C_SEL: + case _C_CHR: + case _C_UCHR: + case _C_CHARPTR: + case _C_ATOM: + case _C_SHT: + case _C_USHT: + case _C_INT: + case _C_UINT: + case _C_LNG: + case _C_BOOL: + case _C_ULNG: + case _C_LNG_LNG: + case _C_ULNG_LNG: + case _C_FLT: + case _C_DBL: + case _C_VOID: + case _C_UNDEF: + return ++type; + break; + + case _C_COMPLEX: + return type + 2; + break; + + case _C_ARY_B: + /* skip digits, typespec and closing ']' */ + + while (isdigit ((unsigned char)*++type)) + ; + type = objc_skip_typespec (type); + if (*type == _C_ARY_E) + return ++type; + else + { + objc_error (nil, OBJC_ERR_BAD_TYPE, "bad array type %s\n", type); + return 0; + } + + case _C_BFLD: + /* The new encoding of bitfields is: b 'position' 'type' 'size' */ + while (isdigit ((unsigned char)*++type)) + ; /* skip position */ + while (isdigit ((unsigned char)*++type)) + ; /* skip type and size */ + return type; + + case _C_STRUCT_B: + /* skip name, and elements until closing '}' */ + + while (*type != _C_STRUCT_E && *type++ != '=') + ; + while (*type != _C_STRUCT_E) + { + type = objc_skip_typespec (type); + } + return ++type; + + case _C_UNION_B: + /* skip name, and elements until closing ')' */ + + while (*type != _C_UNION_E && *type++ != '=') + ; + while (*type != _C_UNION_E) + { + type = objc_skip_typespec (type); + } + return ++type; + + case _C_PTR: + /* Just skip the following typespec */ + + return objc_skip_typespec (++type); + + default: + { + objc_error (nil, OBJC_ERR_BAD_TYPE, "unknown type %s\n", type); + return 0; + } + } +} + +/* + Skip an offset as part of a method encoding. This is prepended by a + '+' if the argument is passed in registers. +*/ +inline const char * +objc_skip_offset (const char *type) +{ + if (*type == '+') + type++; + while (isdigit ((unsigned char) *++type)) + ; + return type; +} + +/* + Skip an argument specification of a method encoding. +*/ +const char * +objc_skip_argspec (const char *type) +{ + type = objc_skip_typespec (type); + type = objc_skip_offset (type); + return type; +} + +/* + Return the number of arguments that the method MTH expects. + Note that all methods need two implicit arguments `self' and + `_cmd'. +*/ +int +method_get_number_of_arguments (struct objc_method *mth) +{ + int i = 0; + const char *type = mth->method_types; + while (*type) + { + type = objc_skip_argspec (type); + i += 1; + } + return i - 1; +} + +/* + Return the size of the argument block needed on the stack to invoke + the method MTH. This may be zero, if all arguments are passed in + registers. +*/ + +int +method_get_sizeof_arguments (struct objc_method *mth) +{ + const char *type = objc_skip_typespec (mth->method_types); + return atoi (type); +} + +/* + Return a pointer to the next argument of ARGFRAME. type points to + the last argument. Typical use of this look like: + + { + char *datum, *type; + for (datum = method_get_first_argument (method, argframe, &type); + datum; datum = method_get_next_argument (argframe, &type)) + { + unsigned flags = objc_get_type_qualifiers (type); + type = objc_skip_type_qualifiers (type); + if (*type != _C_PTR) + [portal encodeData: datum ofType: type]; + else + { + if ((flags & _F_IN) == _F_IN) + [portal encodeData: *(char **) datum ofType: ++type]; + } + } + } +*/ + +char * +method_get_next_argument (arglist_t argframe, const char **type) +{ + const char *t = objc_skip_argspec (*type); + + if (*t == '\0') + return 0; + + *type = t; + t = objc_skip_typespec (t); + + if (*t == '+') + return argframe->arg_regs + atoi (++t); + else + return argframe->arg_ptr + atoi (t); +} + +/* + Return a pointer to the value of the first argument of the method + described in M with the given argumentframe ARGFRAME. The type + is returned in TYPE. type must be passed to successive calls of + method_get_next_argument. +*/ +char * +method_get_first_argument (struct objc_method *m, + arglist_t argframe, + const char **type) +{ + *type = m->method_types; + return method_get_next_argument (argframe, type); +} + +/* + Return a pointer to the ARGth argument of the method + M from the frame ARGFRAME. The type of the argument + is returned in the value-result argument TYPE +*/ + +char * +method_get_nth_argument (struct objc_method *m, + arglist_t argframe, int arg, + const char **type) +{ + const char *t = objc_skip_argspec (m->method_types); + + if (arg > method_get_number_of_arguments (m)) + return 0; + + while (arg--) + t = objc_skip_argspec (t); + + *type = t; + t = objc_skip_typespec (t); + + if (*t == '+') + return argframe->arg_regs + atoi (++t); + else + return argframe->arg_ptr + atoi (t); +} + +unsigned +objc_get_type_qualifiers (const char *type) +{ + unsigned res = 0; + BOOL flag = YES; + + while (flag) + switch (*type++) + { + case _C_CONST: res |= _F_CONST; break; + case _C_IN: res |= _F_IN; break; + case _C_INOUT: res |= _F_INOUT; break; + case _C_OUT: res |= _F_OUT; break; + case _C_BYCOPY: res |= _F_BYCOPY; break; + case _C_BYREF: res |= _F_BYREF; break; + case _C_ONEWAY: res |= _F_ONEWAY; break; + case _C_GCINVISIBLE: res |= _F_GCINVISIBLE; break; + default: flag = NO; + } + + return res; +} + + +/* The following three functions can be used to determine how a + structure is laid out by the compiler. For example: + + struct objc_struct_layout layout; + int i; + + objc_layout_structure (type, &layout); + while (objc_layout_structure_next_member (&layout)) + { + int position, align; + const char *type; + + objc_layout_structure_get_info (&layout, &position, &align, &type); + printf ("element %d has offset %d, alignment %d\n", + i++, position, align); + } + + These functions are used by objc_sizeof_type and objc_alignof_type + functions to compute the size and alignment of structures. The + previous method of computing the size and alignment of a structure + was not working on some architectures, particulary on AIX, and in + the presence of bitfields inside the structure. */ +void +objc_layout_structure (const char *type, + struct objc_struct_layout *layout) +{ + const char *ntype; + + if (*type != _C_UNION_B && *type != _C_STRUCT_B) + { + objc_error (nil, OBJC_ERR_BAD_TYPE, + "record (or union) type expected in objc_layout_structure, got %s\n", + type); + } + + type ++; + layout->original_type = type; + + /* Skip "=" if any. Avoid embedded structures and unions. */ + ntype = type; + while (*ntype != _C_STRUCT_E && *ntype != _C_STRUCT_B && *ntype != _C_UNION_B + && *ntype++ != '=') + /* do nothing */; + + /* If there's a "=", ntype - 1 points to '='; skip the the name */ + if (*(ntype - 1) == '=') + type = ntype; + + layout->type = type; + layout->prev_type = NULL; + layout->record_size = 0; + layout->record_align = BITS_PER_UNIT; + + layout->record_align = MAX (layout->record_align, STRUCTURE_SIZE_BOUNDARY); +} + + +BOOL +objc_layout_structure_next_member (struct objc_struct_layout *layout) +{ + register int desired_align = 0; + + /* The following are used only if the field is a bitfield */ + register const char *bfld_type = 0; + register int bfld_type_size, bfld_type_align = 0, bfld_field_size = 0; + + /* The current type without the type qualifiers */ + const char *type; + BOOL unionp = layout->original_type[-1] == _C_UNION_B; + + /* Add the size of the previous field to the size of the record. */ + if (layout->prev_type) + { + type = objc_skip_type_qualifiers (layout->prev_type); + if (unionp) + layout->record_size = MAX (layout->record_size, + objc_sizeof_type (type) * BITS_PER_UNIT); + + else if (*type != _C_BFLD) + layout->record_size += objc_sizeof_type (type) * BITS_PER_UNIT; + else { + /* Get the bitfield's type */ + for (bfld_type = type + 1; + isdigit ((unsigned char)*bfld_type); + bfld_type++) + /* do nothing */; + + bfld_type_size = objc_sizeof_type (bfld_type) * BITS_PER_UNIT; + bfld_type_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT; + bfld_field_size = atoi (objc_skip_typespec (bfld_type)); + layout->record_size += bfld_field_size; + } + } + + if ((unionp && *layout->type == _C_UNION_E) + || (!unionp && *layout->type == _C_STRUCT_E)) + return NO; + + /* Skip the variable name if any */ + if (*layout->type == '"') + { + for (layout->type++; *layout->type++ != '"';) + /* do nothing */; + } + + type = objc_skip_type_qualifiers (layout->type); + + if (*type != _C_BFLD) + desired_align = objc_alignof_type (type) * BITS_PER_UNIT; + else + { + desired_align = 1; + /* Skip the bitfield's offset */ + for (bfld_type = type + 1; + isdigit ((unsigned char) *bfld_type); + bfld_type++) + /* do nothing */; + + bfld_type_size = objc_sizeof_type (bfld_type) * BITS_PER_UNIT; + bfld_type_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT; + bfld_field_size = atoi (objc_skip_typespec (bfld_type)); + } + +#ifdef BIGGEST_FIELD_ALIGNMENT + desired_align = MIN (desired_align, BIGGEST_FIELD_ALIGNMENT); +#endif +#ifdef ADJUST_FIELD_ALIGN + desired_align = ADJUST_FIELD_ALIGN (type, desired_align); +#endif + + /* Record must have at least as much alignment as any field. + Otherwise, the alignment of the field within the record + is meaningless. */ +#ifndef PCC_BITFIELD_TYPE_MATTERS + layout->record_align = MAX (layout->record_align, desired_align); +#else /* PCC_BITFIELD_TYPE_MATTERS */ + if (*type == _C_BFLD) + { + /* For these machines, a zero-length field does not + affect the alignment of the structure as a whole. + It does, however, affect the alignment of the next field + within the structure. */ + if (bfld_field_size) + layout->record_align = MAX (layout->record_align, desired_align); + else + desired_align = objc_alignof_type (bfld_type) * BITS_PER_UNIT; + + /* A named bit field of declared type `int' + forces the entire structure to have `int' alignment. + Q1: How is encoded this thing and how to check for it? + Q2: How to determine maximum_field_alignment at runtime? */ + +/* if (DECL_NAME (field) != 0) */ + { + int type_align = bfld_type_align; +#if 0 + if (maximum_field_alignment != 0) + type_align = MIN (type_align, maximum_field_alignment); + else if (DECL_PACKED (field)) + type_align = MIN (type_align, BITS_PER_UNIT); +#endif + + layout->record_align = MAX (layout->record_align, type_align); + } + } + else + layout->record_align = MAX (layout->record_align, desired_align); +#endif /* PCC_BITFIELD_TYPE_MATTERS */ + + /* Does this field automatically have alignment it needs + by virtue of the fields that precede it and the record's + own alignment? */ + + if (*type == _C_BFLD) + layout->record_size = atoi (type + 1); + else if (layout->record_size % desired_align != 0) + { + /* No, we need to skip space before this field. + Bump the cumulative size to multiple of field alignment. */ + layout->record_size = ROUND (layout->record_size, desired_align); + } + + /* Jump to the next field in record. */ + + layout->prev_type = layout->type; + layout->type = objc_skip_typespec (layout->type); /* skip component */ + + return YES; +} + + +void objc_layout_finish_structure (struct objc_struct_layout *layout, + unsigned int *size, + unsigned int *align) +{ + BOOL unionp = layout->original_type[-1] == _C_UNION_B; + if (layout->type + && ((!unionp && *layout->type == _C_STRUCT_E) + || (unionp && *layout->type == _C_UNION_E))) + { + /* Work out the alignment of the record as one expression and store + in the record type. Round it up to a multiple of the record's + alignment. */ +#if defined (ROUND_TYPE_ALIGN) && ! defined (__sparc__) + layout->record_align = ROUND_TYPE_ALIGN (layout->original_type-1, + 1, + layout->record_align); +#else + layout->record_align = MAX (1, layout->record_align); +#endif + +#ifdef ROUND_TYPE_SIZE + layout->record_size = ROUND_TYPE_SIZE (layout->original_type, + layout->record_size, + layout->record_align); +#else + /* Round the size up to be a multiple of the required alignment */ + layout->record_size = ROUND (layout->record_size, layout->record_align); +#endif + + layout->type = NULL; + } + if (size) + *size = layout->record_size / BITS_PER_UNIT; + if (align) + *align = layout->record_align / BITS_PER_UNIT; +} + + +void objc_layout_structure_get_info (struct objc_struct_layout *layout, + unsigned int *offset, + unsigned int *align, + const char **type) +{ + if (offset) + *offset = layout->record_size / BITS_PER_UNIT; + if (align) + *align = layout->record_align / BITS_PER_UNIT; + if (type) + *type = layout->prev_type; +} diff --git a/exception.c b/exception.c new file mode 100644 index 0000000..6a872bf --- /dev/null +++ b/exception.c @@ -0,0 +1,496 @@ +/* The implementation of exception handling primitives for Objective-C. + Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include +#include "objc/objc-api.h" +#include "unwind.h" +#include "unwind-pe.h" + + +#ifdef __ARM_EABI_UNWINDER__ + +const _Unwind_Exception_Class __objc_exception_class + = {'G', 'N', 'U', 'C', 'O', 'B', 'J', 'C'}; + +#else + +/* This is the exception class we report -- "GNUCOBJC". */ +static const _Unwind_Exception_Class __objc_exception_class + = ((((((((_Unwind_Exception_Class) 'G' + << 8 | (_Unwind_Exception_Class) 'N') + << 8 | (_Unwind_Exception_Class) 'U') + << 8 | (_Unwind_Exception_Class) 'C') + << 8 | (_Unwind_Exception_Class) 'O') + << 8 | (_Unwind_Exception_Class) 'B') + << 8 | (_Unwind_Exception_Class) 'J') + << 8 | (_Unwind_Exception_Class) 'C'); + +#endif + +/* This is the object that is passed around by the Objective C runtime + to represent the exception in flight. */ + +struct ObjcException +{ + /* This bit is needed in order to interact with the unwind runtime. */ + struct _Unwind_Exception base; + + /* The actual object we want to throw. Note: must come immediately after + unwind header. */ + id value; + +#ifdef __ARM_EABI_UNWINDER__ + /* Note: we use the barrier cache defined in the unwind control block for + ARM EABI. */ +#else + /* Cache some internal unwind data between phase 1 and phase 2. */ + _Unwind_Ptr landingPad; + int handlerSwitchValue; +#endif +}; + + + +struct lsda_header_info +{ + _Unwind_Ptr Start; + _Unwind_Ptr LPStart; + _Unwind_Ptr ttype_base; + const unsigned char *TType; + const unsigned char *action_table; + unsigned char ttype_encoding; + unsigned char call_site_encoding; +}; + +/* This hook allows libraries to sepecify special actions when an + exception is thrown without a handler in place. + */ +void (*_objc_unexpected_exception) (id exception); /* !T:SAFE */ + +static const unsigned char * +parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p, + struct lsda_header_info *info) +{ + _uleb128_t tmp; + unsigned char lpstart_encoding; + + info->Start = (context ? _Unwind_GetRegionStart (context) : 0); + + /* Find @LPStart, the base to which landing pad offsets are relative. */ + lpstart_encoding = *p++; + if (lpstart_encoding != DW_EH_PE_omit) + p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart); + else + info->LPStart = info->Start; + + /* Find @TType, the base of the handler and exception spec type data. */ + info->ttype_encoding = *p++; + if (info->ttype_encoding != DW_EH_PE_omit) + { + p = read_uleb128 (p, &tmp); + info->TType = p + tmp; + } + else + info->TType = 0; + + /* The encoding and length of the call-site table; the action table + immediately follows. */ + info->call_site_encoding = *p++; + p = read_uleb128 (p, &tmp); + info->action_table = p + tmp; + + return p; +} + +#ifdef __ARM_EABI_UNWINDER__ + +static Class +get_ttype_entry (struct lsda_header_info *info, _uleb128_t i) +{ + _Unwind_Ptr ptr; + + ptr = (_Unwind_Ptr) (info->TType - (i * 4)); + ptr = _Unwind_decode_target2 (ptr); + + if (ptr) + return objc_get_class ((const char *) ptr); + else + return 0; +} + +#else + +static Class +get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i) +{ + _Unwind_Ptr ptr; + + i *= size_of_encoded_value (info->ttype_encoding); + read_encoded_value_with_base (info->ttype_encoding, info->ttype_base, + info->TType - i, &ptr); + + /* NULL ptr means catch-all. */ + if (ptr) + return objc_get_class ((const char *) ptr); + else + return 0; +} + +#endif + +/* Like unto the method of the same name on Object, but takes an id. */ +/* ??? Does this bork the meta-type system? Can/should we look up an + isKindOf method on the id? */ + +static int +isKindOf (id value, Class target) +{ + Class c; + + /* NULL target is catch-all. */ + if (target == 0) + return 1; + + for (c = value->class_pointer; c; c = class_get_super_class (c)) + if (c == target) + return 1; + return 0; +} + +/* Using a different personality function name causes link failures + when trying to mix code using different exception handling models. */ +#ifdef SJLJ_EXCEPTIONS +#define PERSONALITY_FUNCTION __gnu_objc_personality_sj0 +#define __builtin_eh_return_data_regno(x) x +#else +#define PERSONALITY_FUNCTION __gnu_objc_personality_v0 +#endif + +#ifdef __ARM_EABI_UNWINDER__ + +#define CONTINUE_UNWINDING \ + do \ + { \ + if (__gnu_unwind_frame(ue_header, context) != _URC_OK) \ + return _URC_FAILURE; \ + return _URC_CONTINUE_UNWIND; \ + } \ + while (0) + +_Unwind_Reason_Code +PERSONALITY_FUNCTION (_Unwind_State state, + struct _Unwind_Exception *ue_header, + struct _Unwind_Context *context) +#else + +#define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND + +_Unwind_Reason_Code +PERSONALITY_FUNCTION (int version, + _Unwind_Action actions, + _Unwind_Exception_Class exception_class, + struct _Unwind_Exception *ue_header, + struct _Unwind_Context *context) +#endif +{ + struct ObjcException *xh = (struct ObjcException *) ue_header; + + struct lsda_header_info info; + const unsigned char *language_specific_data; + const unsigned char *action_record; + const unsigned char *p; + _Unwind_Ptr landing_pad, ip; + int handler_switch_value; + int saw_cleanup = 0, saw_handler, foreign_exception; + void *return_object; + int ip_before_insn = 0; + +#ifdef __ARM_EABI_UNWINDER__ + _Unwind_Action actions; + + switch (state & _US_ACTION_MASK) + { + case _US_VIRTUAL_UNWIND_FRAME: + actions = _UA_SEARCH_PHASE; + break; + + case _US_UNWIND_FRAME_STARTING: + actions = _UA_CLEANUP_PHASE; + if (!(state & _US_FORCE_UNWIND) + && ue_header->barrier_cache.sp == _Unwind_GetGR (context, 13)) + actions |= _UA_HANDLER_FRAME; + break; + + case _US_UNWIND_FRAME_RESUME: + CONTINUE_UNWINDING; + break; + + default: + abort(); + } + actions |= state & _US_FORCE_UNWIND; + + /* TODO: Foreign exceptions need some attention (e.g. rethrowing doesn't + work). */ + foreign_exception = 0; + + /* The dwarf unwinder assumes the context structure holds things like the + function and LSDA pointers. The ARM implementation caches these in + the exception header (UCB). To avoid rewriting everything we make the + virtual IP register point at the UCB. */ + ip = (_Unwind_Ptr) ue_header; + _Unwind_SetGR (context, 12, ip); + +#else /* !__ARM_EABI_UNWINDER. */ + /* Interface version check. */ + if (version != 1) + return _URC_FATAL_PHASE1_ERROR; + + foreign_exception = (exception_class != __objc_exception_class); +#endif + + /* Shortcut for phase 2 found handler for domestic exception. */ + if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME) + && !foreign_exception) + { +#ifdef __ARM_EABI_UNWINDER__ + handler_switch_value = (int) ue_header->barrier_cache.bitpattern[1]; + landing_pad = (_Unwind_Ptr) ue_header->barrier_cache.bitpattern[3]; +#else + handler_switch_value = xh->handlerSwitchValue; + landing_pad = xh->landingPad; +#endif + goto install_context; + } + + language_specific_data = (const unsigned char *) + _Unwind_GetLanguageSpecificData (context); + + /* If no LSDA, then there are no handlers or cleanups. */ + if (! language_specific_data) + CONTINUE_UNWINDING; + + /* Parse the LSDA header. */ + p = parse_lsda_header (context, language_specific_data, &info); + info.ttype_base = base_of_encoded_value (info.ttype_encoding, context); +#ifdef HAVE_GETIPINFO + ip = _Unwind_GetIPInfo (context, &ip_before_insn); +#else + ip = _Unwind_GetIP (context); +#endif + if (!ip_before_insn) + --ip; + landing_pad = 0; + action_record = 0; + handler_switch_value = 0; + +#ifdef SJLJ_EXCEPTIONS + /* The given "IP" is an index into the call-site table, with two + exceptions -- -1 means no-action, and 0 means terminate. But + since we're using uleb128 values, we've not got random access + to the array. */ + if ((int) ip < 0) + return _URC_CONTINUE_UNWIND; + else + { + _uleb128_t cs_lp, cs_action; + do + { + p = read_uleb128 (p, &cs_lp); + p = read_uleb128 (p, &cs_action); + } + while (--ip); + + /* Can never have null landing pad for sjlj -- that would have + been indicated by a -1 call site index. */ + landing_pad = cs_lp + 1; + if (cs_action) + action_record = info.action_table + cs_action - 1; + goto found_something; + } +#else + /* Search the call-site table for the action associated with this IP. */ + while (p < info.action_table) + { + _Unwind_Ptr cs_start, cs_len, cs_lp; + _uleb128_t cs_action; + + /* Note that all call-site encodings are "absolute" displacements. */ + p = read_encoded_value (0, info.call_site_encoding, p, &cs_start); + p = read_encoded_value (0, info.call_site_encoding, p, &cs_len); + p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp); + p = read_uleb128 (p, &cs_action); + + /* The table is sorted, so if we've passed the ip, stop. */ + if (ip < info.Start + cs_start) + p = info.action_table; + else if (ip < info.Start + cs_start + cs_len) + { + if (cs_lp) + landing_pad = info.LPStart + cs_lp; + if (cs_action) + action_record = info.action_table + cs_action - 1; + goto found_something; + } + } +#endif /* SJLJ_EXCEPTIONS */ + + /* If ip is not present in the table, C++ would call terminate. */ + /* ??? As with Java, it's perhaps better to tweek the LSDA to + that no-action is mapped to no-entry. */ + CONTINUE_UNWINDING; + + found_something: + saw_cleanup = 0; + saw_handler = 0; + + if (landing_pad == 0) + { + /* If ip is present, and has a null landing pad, there are + no cleanups or handlers to be run. */ + } + else if (action_record == 0) + { + /* If ip is present, has a non-null landing pad, and a null + action table offset, then there are only cleanups present. + Cleanups use a zero switch value, as set above. */ + saw_cleanup = 1; + } + else + { + /* Otherwise we have a catch handler. */ + _sleb128_t ar_filter, ar_disp; + + while (1) + { + p = action_record; + p = read_sleb128 (p, &ar_filter); + read_sleb128 (p, &ar_disp); + + if (ar_filter == 0) + { + /* Zero filter values are cleanups. */ + saw_cleanup = 1; + } + + /* During forced unwinding, we only run cleanups. With a + foreign exception class, we have no class info to match. */ + else if ((actions & _UA_FORCE_UNWIND) || foreign_exception) + ; + + else if (ar_filter > 0) + { + /* Positive filter values are handlers. */ + + Class catch_type = get_ttype_entry (&info, ar_filter); + + if (isKindOf (xh->value, catch_type)) + { + handler_switch_value = ar_filter; + saw_handler = 1; + break; + } + } + else + { + /* Negative filter values are exception specifications, + which Objective-C does not use. */ + abort (); + } + + if (ar_disp == 0) + break; + action_record = p + ar_disp; + } + } + + if (! saw_handler && ! saw_cleanup) + CONTINUE_UNWINDING; + + if (actions & _UA_SEARCH_PHASE) + { + if (!saw_handler) + CONTINUE_UNWINDING; + + /* For domestic exceptions, we cache data from phase 1 for phase 2. */ + if (!foreign_exception) + { +#ifdef __ARM_EABI_UNWINDER__ + ue_header->barrier_cache.sp = _Unwind_GetGR (context, 13); + ue_header->barrier_cache.bitpattern[1] = (_uw) handler_switch_value; + ue_header->barrier_cache.bitpattern[3] = (_uw) landing_pad; +#else + xh->handlerSwitchValue = handler_switch_value; + xh->landingPad = landing_pad; +#endif + } + return _URC_HANDLER_FOUND; + } + + install_context: + if (saw_cleanup == 0) + { + return_object = xh->value; + if (!(actions & _UA_SEARCH_PHASE)) + _Unwind_DeleteException(&xh->base); + } + + _Unwind_SetGR (context, __builtin_eh_return_data_regno (0), + __builtin_extend_pointer (saw_cleanup ? xh : return_object)); + _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), + handler_switch_value); + _Unwind_SetIP (context, landing_pad); + return _URC_INSTALL_CONTEXT; +} + +static void +__objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)), + struct _Unwind_Exception *exc) +{ + free (exc); +} + +void +objc_exception_throw (id value) +{ + struct ObjcException *header = calloc (1, sizeof (*header)); + + memcpy (&header->base.exception_class, &__objc_exception_class, + sizeof (__objc_exception_class)); + header->base.exception_cleanup = __objc_exception_cleanup; + header->value = value; + +#ifdef SJLJ_EXCEPTIONS + _Unwind_SjLj_RaiseException (&header->base); +#else + _Unwind_RaiseException (&header->base); +#endif + + /* Some sort of unwinding error. */ + if (_objc_unexpected_exception != 0) + { + (*_objc_unexpected_exception) (value); + } + abort (); +} diff --git a/gc.c b/gc.c new file mode 100644 index 0000000..cc8fe11 --- /dev/null +++ b/gc.c @@ -0,0 +1,451 @@ +/* Basic data types for Objective C. + Copyright (C) 1998, 2002, 2004, 2005, 2006, 2009 Free Software Foundation, Inc. + Contributed by Ovidiu Predescu. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "objc/objc.h" +#include "objc/encoding.h" + +#include +#include +#include + +#if OBJC_WITH_GC + +#include +#include + +/* gc_typed.h uses the following but doesn't declare them */ +typedef GC_word word; +typedef GC_signed_word signed_word; +#define BITS_PER_WORD (CHAR_BIT * sizeof (word)) + +#include + +/* The following functions set up in `mask` the corresponding pointers. + The offset is incremented with the size of the type. */ + +#define ROUND(V, A) \ + ({ typeof (V) __v = (V); typeof (A) __a = (A); \ + __a * ((__v+__a - 1)/__a); }) + +#define SET_BIT_FOR_OFFSET(mask, offset) \ + GC_set_bit (mask, offset / sizeof (void *)) + +/* Some prototypes */ +static void +__objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset); +static void +__objc_gc_setup_union (GC_bitmap mask, const char *type, int offset); + + +static void +__objc_gc_setup_array (GC_bitmap mask, const char *type, int offset) +{ + int i, len = atoi (type + 1); + + while (isdigit (*++type)) + /* do nothing */; /* skip the size of the array */ + + switch (*type) { + case _C_ARY_B: + for (i = 0; i < len; i++) + __objc_gc_setup_array (mask, type, offset); + break; + + case _C_STRUCT_B: + for (i = 0; i < len; i++) + __objc_gc_setup_struct (mask, type, offset); + break; + + case _C_UNION_B: + for (i = 0; i < len; i++) + __objc_gc_setup_union (mask, type, offset); + break; + + default: + break; + } +} + +static void +__objc_gc_setup_struct (GC_bitmap mask, const char *type, int offset) +{ + struct objc_struct_layout layout; + unsigned int position; + const char *mtype; + + objc_layout_structure (type, &layout); + + while (objc_layout_structure_next_member (&layout)) + { + BOOL gc_invisible = NO; + + objc_layout_structure_get_info (&layout, &position, NULL, &mtype); + + /* Skip the variable name */ + if (*mtype == '"') + { + for (mtype++; *mtype++ != '"';) + /* do nothing */; + } + + if (*mtype == _C_GCINVISIBLE) + { + gc_invisible = YES; + mtype++; + } + + /* Add to position the offset of this structure */ + position += offset; + + switch (*mtype) { + case _C_ID: + case _C_CLASS: + case _C_SEL: + case _C_PTR: + case _C_CHARPTR: + case _C_ATOM: + if (! gc_invisible) + SET_BIT_FOR_OFFSET (mask, position); + break; + + case _C_ARY_B: + __objc_gc_setup_array (mask, mtype, position); + break; + + case _C_STRUCT_B: + __objc_gc_setup_struct (mask, mtype, position); + break; + + case _C_UNION_B: + __objc_gc_setup_union (mask, mtype, position); + break; + + default: + break; + } + } +} + +static void +__objc_gc_setup_union (GC_bitmap mask, const char *type, int offset) +{ + /* Sub-optimal, quick implementation: assume the union is made of + pointers, set up the mask accordingly. */ + + int i, size, align; + + /* Skip the variable name */ + if (*type == '"') + { + for (type++; *type++ != '"';) + /* do nothing */; + } + + size = objc_sizeof_type (type); + align = objc_alignof_type (type); + + offset = ROUND (offset, align); + for (i = 0; i < size; i += sizeof (void *)) + { + SET_BIT_FOR_OFFSET (mask, offset); + offset += sizeof (void *); + } +} + + +/* Iterates over the types in the structure that represents the class + encoding and sets the bits in mask according to each ivar type. */ +static void +__objc_gc_type_description_from_type (GC_bitmap mask, const char *type) +{ + struct objc_struct_layout layout; + unsigned int offset, align; + const char *ivar_type; + + objc_layout_structure (type, &layout); + + while (objc_layout_structure_next_member (&layout)) + { + BOOL gc_invisible = NO; + + objc_layout_structure_get_info (&layout, &offset, &align, &ivar_type); + + /* Skip the variable name */ + if (*ivar_type == '"') + { + for (ivar_type++; *ivar_type++ != '"';) + /* do nothing */; + } + + if (*ivar_type == _C_GCINVISIBLE) + { + gc_invisible = YES; + ivar_type++; + } + + switch (*ivar_type) { + case _C_ID: + case _C_CLASS: + case _C_SEL: + case _C_PTR: + case _C_CHARPTR: + if (! gc_invisible) + SET_BIT_FOR_OFFSET (mask, offset); + break; + + case _C_ARY_B: + __objc_gc_setup_array (mask, ivar_type, offset); + break; + + case _C_STRUCT_B: + __objc_gc_setup_struct (mask, ivar_type, offset); + break; + + case _C_UNION_B: + __objc_gc_setup_union (mask, ivar_type, offset); + break; + + default: + break; + } + } +} + +/* Computes in *type the full type encoding of this class including + its super classes. '*size' gives the total number of bytes allocated + into *type, '*current' the number of bytes used so far by the + encoding. */ +static void +__objc_class_structure_encoding (Class class, char **type, int *size, + int *current) +{ + int i, ivar_count; + struct objc_ivar_list *ivars; + + if (! class) + { + strcat (*type, "{"); + (*current)++; + return; + } + + /* Add the type encodings of the super classes */ + __objc_class_structure_encoding (class->super_class, type, size, current); + + ivars = class->ivars; + if (! ivars) + return; + + ivar_count = ivars->ivar_count; + + for (i = 0; i < ivar_count; i++) + { + struct objc_ivar *ivar = &(ivars->ivar_list[i]); + const char *ivar_type = ivar->ivar_type; + int len = strlen (ivar_type); + + if (*current + len + 1 >= *size) + { + /* Increase the size of the encoding string so that it + contains this ivar's type. */ + *size = ROUND (*current + len + 1, 10); + *type = objc_realloc (*type, *size); + } + strcat (*type + *current, ivar_type); + *current += len; + } +} + + +/* Allocates the memory that will hold the type description for class + and calls the __objc_class_structure_encoding that generates this + value. */ +void +__objc_generate_gc_type_description (Class class) +{ + GC_bitmap mask; + int bits_no, size; + int type_size = 10, current; + char *class_structure_type; + + if (! CLS_ISCLASS (class)) + return; + + /* We have to create a mask in which each bit counts for a pointer member. + We take into consideration all the non-pointer instance variables and we + round them up to the alignment. */ + + /* The number of bits in the mask is the size of an instance in bytes divided + by the size of a pointer. */ + bits_no = (ROUND (class_get_instance_size (class), sizeof (void *)) + / sizeof (void *)); + size = ROUND (bits_no, BITS_PER_WORD) / BITS_PER_WORD; + mask = objc_atomic_malloc (size * sizeof (int)); + memset (mask, 0, size * sizeof (int)); + + class_structure_type = objc_atomic_malloc (type_size); + *class_structure_type = current = 0; + __objc_class_structure_encoding (class, &class_structure_type, + &type_size, ¤t); + if (current + 1 == type_size) + class_structure_type = objc_realloc (class_structure_type, ++type_size); + strcat (class_structure_type + current, "}"); +#ifdef DEBUG + printf ("type description for '%s' is %s\n", class->name, class_structure_type); +#endif + + __objc_gc_type_description_from_type (mask, class_structure_type); + objc_free (class_structure_type); + +#ifdef DEBUG + printf (" mask for '%s', type '%s' (bits %d, mask size %d) is:", + class_structure_type, class->name, bits_no, size); + { + int i; + for (i = 0; i < size; i++) + printf (" %lx", mask[i]); + } + puts (""); +#endif + + class->gc_object_type = (void *) GC_make_descriptor (mask, bits_no); +} + + +/* Returns YES if type denotes a pointer type, NO otherwise */ +static inline BOOL +__objc_ivar_pointer (const char *type) +{ + type = objc_skip_type_qualifiers (type); + + return (*type == _C_ID + || *type == _C_CLASS + || *type == _C_SEL + || *type == _C_PTR + || *type == _C_CHARPTR + || *type == _C_ATOM); +} + + +/* Mark the instance variable whose name is given by ivarname as a + weak pointer (a pointer hidden to the garbage collector) if + gc_invisible is true. If gc_invisible is false it unmarks the + instance variable and makes it a normal pointer, visible to the + garbage collector. + + This operation only makes sense on instance variables that are + pointers. */ +void +class_ivar_set_gcinvisible (Class class, const char *ivarname, + BOOL gc_invisible) +{ + int i, ivar_count; + struct objc_ivar_list *ivars; + + if (! class || ! ivarname) + return; + + ivars = class->ivars; + if (! ivars) + return; + + ivar_count = ivars->ivar_count; + + for (i = 0; i < ivar_count; i++) + { + struct objc_ivar *ivar = &(ivars->ivar_list[i]); + const char *type; + + if (! ivar->ivar_name || strcmp (ivar->ivar_name, ivarname)) + continue; + + assert (ivar->ivar_type); + type = ivar->ivar_type; + + /* Skip the variable name */ + if (*type == '"') + { + for (type++; *type++ != '"';) + /* do nothing */; + } + + if (*type == _C_GCINVISIBLE) + { + char *new_type; + size_t len; + + if (gc_invisible || ! __objc_ivar_pointer (type)) + return; /* The type of the variable already matches the + requested gc_invisible type */ + + /* The variable is gc_invisible so we make it gc visible. */ + new_type = objc_atomic_malloc (strlen(ivar->ivar_type)); + len = (type - ivar->ivar_type); + memcpy (new_type, ivar->ivar_type, len); + new_type[len] = 0; + strcat (new_type, type + 1); + ivar->ivar_type = new_type; + } + else + { + char *new_type; + size_t len; + + if (! gc_invisible || ! __objc_ivar_pointer (type)) + return; /* The type of the variable already matches the + requested gc_invisible type */ + + /* The variable is gc visible so we make it gc_invisible. */ + new_type = objc_malloc (strlen(ivar->ivar_type) + 2); + len = (type - ivar->ivar_type); + memcpy (new_type, ivar->ivar_type, len); + new_type[len] = 0; + strcat (new_type, "!"); + strcat (new_type, type); + ivar->ivar_type = new_type; + } + + __objc_generate_gc_type_description (class); + return; + } + + /* Search the instance variable in the superclasses */ + class_ivar_set_gcinvisible (class->super_class, ivarname, gc_invisible); +} + +#else /* !OBJC_WITH_GC */ + +void +__objc_generate_gc_type_description (Class class __attribute__ ((__unused__))) +{ +} + +void class_ivar_set_gcinvisible (Class class __attribute__ ((__unused__)), + const char *ivarname __attribute__ ((__unused__)), + BOOL gc_invisible __attribute__ ((__unused__))) +{ +} + +#endif /* OBJC_WITH_GC */ diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..5754299 --- /dev/null +++ b/hash.c @@ -0,0 +1,281 @@ +/* Hash tables for Objective C internal structures + Copyright (C) 1993, 1996, 1997, 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "assert.h" + +#include "objc/hash.h" + +#include "objc/runtime-legacy.h" /* for DEBUG_PRINTF */ + +/* These two macros determine when a hash table is full and + by how much it should be expanded respectively. + + These equations are percentages. */ +#define FULLNESS(cache) \ + ((((cache)->size * 75) / 100) <= (cache)->used) +#define EXPANSION(cache) \ + ((cache)->size * 2) + +cache_ptr +objc_hash_new (unsigned int size, hash_func_type hash_func, + compare_func_type compare_func) +{ + cache_ptr cache; + + /* Pass me a value greater than 0 and a power of 2. */ + assert (size); + assert (! (size & (size - 1))); + + /* Allocate the cache structure. calloc insures + its initialization for default values. */ + cache = (cache_ptr) objc_calloc (1, sizeof (struct cache)); + assert (cache); + + /* Allocate the array of buckets for the cache. + calloc initializes all of the pointers to NULL. */ + cache->node_table + = (node_ptr *) objc_calloc (size, sizeof (node_ptr)); + assert (cache->node_table); + + cache->size = size; + + /* This should work for all processor architectures? */ + cache->mask = (size - 1); + + /* Store the hashing function so that codes can be computed. */ + cache->hash_func = hash_func; + + /* Store the function that compares hash keys to + determine if they are equal. */ + cache->compare_func = compare_func; + + return cache; +} + + +void +objc_hash_delete (cache_ptr cache) +{ + node_ptr node; + node_ptr next_node; + unsigned int i; + + /* Purge all key/value pairs from the table. */ + /* Step through the nodes one by one and remove every node WITHOUT + using objc_hash_next. this makes objc_hash_delete much more efficient. */ + for (i = 0;i < cache->size;i++) { + if ((node = cache->node_table[i])) { + /* an entry in the hash table has been found, now step through the + nodes next in the list and free them. */ + while ((next_node = node->next)) { + objc_hash_remove (cache,node->key); + node = next_node; + } + + objc_hash_remove (cache,node->key); + } + } + + /* Release the array of nodes and the cache itself. */ + objc_free(cache->node_table); + objc_free(cache); +} + + +void +objc_hash_add (cache_ptr *cachep, const void *key, void *value) +{ + size_t indx = (*(*cachep)->hash_func)(*cachep, key); + node_ptr node = (node_ptr) objc_calloc (1, sizeof (struct cache_node)); + + + assert (node); + + /* Initialize the new node. */ + node->key = key; + node->value = value; + node->next = (*cachep)->node_table[indx]; + + /* Debugging. + Check the list for another key. */ +#ifdef DEBUG + { node_ptr node1 = (*cachep)->node_table[indx]; + + while (node1) { + + assert (node1->key != key); + node1 = node1->next; + } + } +#endif + + /* Install the node as the first element on the list. */ + (*cachep)->node_table[indx] = node; + + /* Bump the number of entries in the cache. */ + ++(*cachep)->used; + + /* Check the hash table's fullness. We're going + to expand if it is above the fullness level. */ + if (FULLNESS (*cachep)) { + + /* The hash table has reached its fullness level. Time to + expand it. + + I'm using a slow method here but is built on other + primitive functions thereby increasing its + correctness. */ + node_ptr node1 = NULL; + cache_ptr new = objc_hash_new (EXPANSION (*cachep), + (*cachep)->hash_func, + (*cachep)->compare_func); + + DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n", + (int) *cachep, (*cachep)->size, new->size); + + /* Copy the nodes from the first hash table to the new one. */ + while ((node1 = objc_hash_next (*cachep, node1))) + objc_hash_add (&new, node1->key, node1->value); + + /* Trash the old cache. */ + objc_hash_delete (*cachep); + + /* Return a pointer to the new hash table. */ + *cachep = new; + } +} + + +void +objc_hash_remove (cache_ptr cache, const void *key) +{ + size_t indx = (*cache->hash_func)(cache, key); + node_ptr node = cache->node_table[indx]; + + + /* We assume there is an entry in the table. Error if it is not. */ + assert (node); + + /* Special case. First element is the key/value pair to be removed. */ + if ((*cache->compare_func)(node->key, key)) { + cache->node_table[indx] = node->next; + objc_free(node); + } else { + + /* Otherwise, find the hash entry. */ + node_ptr prev = node; + BOOL removed = NO; + + do { + + if ((*cache->compare_func)(node->key, key)) { + prev->next = node->next, removed = YES; + objc_free(node); + } else + prev = node, node = node->next; + } while (! removed && node); + assert (removed); + } + + /* Decrement the number of entries in the hash table. */ + --cache->used; +} + + +node_ptr +objc_hash_next (cache_ptr cache, node_ptr node) +{ + /* If the scan is being started then reset the last node + visitied pointer and bucket index. */ + if (! node) + cache->last_bucket = 0; + + /* If there is a node visited last then check for another + entry in the same bucket; Otherwise step to the next bucket. */ + if (node) { + if (node->next) + /* There is a node which follows the last node + returned. Step to that node and retun it. */ + return node->next; + else + ++cache->last_bucket; + } + + /* If the list isn't exhausted then search the buckets for + other nodes. */ + if (cache->last_bucket < cache->size) { + /* Scan the remainder of the buckets looking for an entry + at the head of the list. Return the first item found. */ + while (cache->last_bucket < cache->size) + if (cache->node_table[cache->last_bucket]) + return cache->node_table[cache->last_bucket]; + else + ++cache->last_bucket; + + /* No further nodes were found in the hash table. */ + return NULL; + } else + return NULL; +} + + +/* Given KEY, return corresponding value for it in CACHE. + Return NULL if the KEY is not recorded. */ + +void * +objc_hash_value_for_key (cache_ptr cache, const void *key) +{ + node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)]; + void *retval = NULL; + + if (node) + do { + if ((*cache->compare_func)(node->key, key)) { + retval = node->value; + break; + } else + node = node->next; + } while (! retval && node); + + return retval; +} + +/* Given KEY, return YES if it exists in the CACHE. + Return NO if it does not */ + +BOOL +objc_hash_is_key_in_hash (cache_ptr cache, const void *key) +{ + node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)]; + + if (node) + do { + if ((*cache->compare_func)(node->key, key)) + return YES; + else + node = node->next; + } while (node); + + return NO; +} diff --git a/init.c b/init.c new file mode 100644 index 0000000..b59f949 --- /dev/null +++ b/init.c @@ -0,0 +1,968 @@ +/* GNU Objective C Runtime initialization + Copyright (C) 1993, 1995, 1996, 1997, 2002, 2009 + Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +load support contributed by Ovidiu Predescu + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "objc/runtime-legacy.h" +#include "lock.h" + +/* The version number of this runtime. This must match the number + defined in gcc (objc-act.c). */ +#define OBJC_BACK_COMPATIBLE_VERSION 8 +#define OBJC_VERSION 9 +#define PROTOCOL_VERSION 2 +#define OBJC2_PROTOCOL_VERSION 3 + +void __objc_sync_init(void); + +/* This list contains all modules currently loaded into the runtime. */ +static struct objc_list *__objc_module_list = 0; /* !T:MUTEX */ + +/* This list contains all proto_list's not yet assigned class links. */ +static struct objc_list *unclaimed_proto_list = 0; /* !T:MUTEX */ + +/* List of unresolved static instances. */ +static struct objc_list *uninitialized_statics = 0; /* !T:MUTEX */ + +/* Global runtime "write" mutex. */ +static mutex_t objc_runtime_mutex; +objc_mutex_t __objc_runtime_mutex = &objc_runtime_mutex; + +/* Number of threads that are alive. */ +int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ + +/* Check compiler vs runtime version. */ +static void init_check_module_version (Module_t); + +/* Assign isa links to protos. */ +static void __objc_init_protocols (struct objc_protocol_list *protos); + +/* Add protocol to class. */ +static void __objc_class_add_protocols (Class, struct objc_protocol_list *); + +/* This is a hook which is called by __objc_exec_class every time a + class or a category is loaded into the runtime. This may e.g. help + a dynamic loader determine the classes that have been loaded when + an object file is dynamically linked in. */ +void (*_objc_load_callback) (Class class, Category *category); /* !T:SAFE */ + +/* Is all categories/classes resolved? */ +BOOL __objc_dangling_categories = NO; /* !T:UNUSED */ + +extern SEL +__sel_register_typed_name (const char *name, const char *types, + struct objc_selector *orig, BOOL is_const); + +/* Sends +load to all classes and categories in certain situations. */ +static void objc_send_load (void); + +/* Inserts all the classes defined in module in a tree of classes that + resembles the class hierarchy. This tree is traversed in preorder + and the classes in its nodes receive the +load message if these + methods were not executed before. The algorithm ensures that when + the +load method of a class is executed all the superclasses have + been already received the +load message. */ +static void __objc_create_classes_tree (Module_t module); + +static void __objc_call_callback (Module_t module); + +/* A special version that works only before the classes are completely + installed in the runtime. */ +static BOOL class_is_subclass_of_class (Class class, Class superclass); + +typedef struct objc_class_tree { + Class class; + struct objc_list *subclasses; /* `head' is pointer to an objc_class_tree */ +} objc_class_tree; + +/* This is a linked list of objc_class_tree trees. The head of these + trees are root classes (their super class is Nil). These different + trees represent different class hierarchies. */ +static struct objc_list *__objc_class_tree_list = NULL; + +/* Keeps the +load methods who have been already executed. This hash + should not be destroyed during the execution of the program. */ +static cache_ptr __objc_load_methods = NULL; + +/* This function is used when building the class tree used to send + ordinately the +load message to all classes needing it. The tree + is really needed so that superclasses will get the message before + subclasses. + + This tree will contain classes which are being loaded (or have just + being loaded), and whose super_class pointers have not yet been + resolved. This implies that their super_class pointers point to a + string with the name of the superclass; when the first message is + sent to the class (/an object of that class) the class links will + be resolved, which will replace the super_class pointers with + pointers to the actual superclasses. + + Unfortunately, the tree might also contain classes which had been + loaded previously, and whose class links have already been + resolved. + + This function returns the superclass of a class in both cases, and + can be used to build the determine the class relationships while + building the tree. +*/ +static Class class_superclass_of_class (Class class) +{ + char *super_class_name; + + /* If the class links have been resolved, use the resolved + * links. */ + if (CLS_ISRESOLV (class)) + return class->super_class; + + /* Else, 'class' has not yet been resolved. This means that its + * super_class pointer is really the name of the super class (rather + * than a pointer to the actual superclass). */ + super_class_name = (char *)class->super_class; + + /* Return Nil for a root class. */ + if (super_class_name == NULL) + return Nil; + + /* Lookup the superclass of non-root classes. */ + return objc_lookup_class (super_class_name); +} + + +/* Creates a tree of classes whose topmost class is directly inherited + from `upper' and the bottom class in this tree is + `bottom_class'. The classes in this tree are super classes of + `bottom_class'. `subclasses' member of each tree node point to the + next subclass tree node. */ + +static objc_class_tree * +create_tree_of_subclasses_inherited_from (Class bottom_class, Class upper) +{ + Class superclass = bottom_class->super_class ? + objc_lookup_class ((char *) bottom_class->super_class) + : Nil; + + objc_class_tree *tree, *prev; + + DEBUG_PRINTF ("create_tree_of_subclasses_inherited_from:"); + DEBUG_PRINTF ("bottom_class = %s, upper = %s\n", + (bottom_class ? bottom_class->name : NULL), + (upper ? upper->name : NULL)); + + tree = prev = objc_calloc (1, sizeof (objc_class_tree)); + prev->class = bottom_class; + + while (superclass != upper) + { + tree = objc_calloc (1, sizeof (objc_class_tree)); + tree->class = superclass; + tree->subclasses = list_cons (prev, tree->subclasses); + superclass = class_superclass_of_class (superclass); + prev = tree; + } + + return tree; +} + +/* Insert the `class' into the proper place in the `tree' class + hierarchy. This function returns a new tree if the class has been + successfully inserted into the tree or NULL if the class is not + part of the classes hierarchy described by `tree'. This function is + private to objc_tree_insert_class (), you should not call it + directly. */ + +static objc_class_tree * +__objc_tree_insert_class (objc_class_tree *tree, Class class) +{ + DEBUG_PRINTF ("__objc_tree_insert_class: tree = %x, class = %s\n", + (int)tree, class->name); + + if (tree == NULL) + return create_tree_of_subclasses_inherited_from (class, NULL); + else if (class == tree->class) + { + /* `class' has been already inserted */ + DEBUG_PRINTF ("1. class %s was previously inserted\n", class->name); + return tree; + } + else if (class_superclass_of_class (class) == tree->class) + { + /* If class is a direct subclass of tree->class then add class to the + list of subclasses. First check to see if it wasn't already + inserted. */ + struct objc_list *list = tree->subclasses; + objc_class_tree *node; + + while (list) + { + /* Class has been already inserted; do nothing just return + the tree. */ + if (((objc_class_tree *) list->head)->class == class) + { + DEBUG_PRINTF ("2. class %s was previously inserted\n", + class->name); + return tree; + } + list = list->tail; + } + + /* Create a new node class and insert it into the list of subclasses */ + node = objc_calloc (1, sizeof (objc_class_tree)); + node->class = class; + tree->subclasses = list_cons (node, tree->subclasses); + DEBUG_PRINTF ("3. class %s inserted\n", class->name); + return tree; + } + else + { + /* The class is not a direct subclass of tree->class. Search for + class's superclasses in the list of subclasses. */ + struct objc_list *subclasses = tree->subclasses; + + /* Precondition: the class must be a subclass of tree->class; + otherwise return NULL to indicate our caller that it must + take the next tree. */ + if (! class_is_subclass_of_class (class, tree->class)) + return NULL; + + for (; subclasses != NULL; subclasses = subclasses->tail) + { + Class aClass = ((objc_class_tree *) (subclasses->head))->class; + + if (class_is_subclass_of_class (class, aClass)) + { + /* If we found one of class's superclasses we insert the + class into its subtree and return the original tree + since nothing has been changed. */ + subclasses->head + = __objc_tree_insert_class (subclasses->head, class); + DEBUG_PRINTF ("4. class %s inserted\n", class->name); + return tree; + } + } + + /* We haven't found a subclass of `class' in the `subclasses' + list. Create a new tree of classes whose topmost class is a + direct subclass of tree->class. */ + { + objc_class_tree *new_tree + = create_tree_of_subclasses_inherited_from (class, tree->class); + tree->subclasses = list_cons (new_tree, tree->subclasses); + DEBUG_PRINTF ("5. class %s inserted\n", class->name); + return tree; + } + } +} + +/* This function inserts `class' in the right tree hierarchy classes. */ + +static void +objc_tree_insert_class (Class class) +{ + struct objc_list *list_node; + objc_class_tree *tree; + + list_node = __objc_class_tree_list; + while (list_node) + { + tree = __objc_tree_insert_class (list_node->head, class); + if (tree) + { + list_node->head = tree; + break; + } + else + list_node = list_node->tail; + } + + /* If the list was finished but the class hasn't been inserted, + insert it here. */ + if (! list_node) + { + __objc_class_tree_list = list_cons (NULL, __objc_class_tree_list); + __objc_class_tree_list->head = __objc_tree_insert_class (NULL, class); + } +} + +/* Traverse tree in preorder. Used to send +load. */ + +static void +objc_preorder_traverse (objc_class_tree *tree, + int level, + void (*function) (objc_class_tree *, int)) +{ + struct objc_list *node; + + (*function) (tree, level); + for (node = tree->subclasses; node; node = node->tail) + objc_preorder_traverse (node->head, level + 1, function); +} + +/* Traverse tree in postorder. Used to destroy a tree. */ + +static void +objc_postorder_traverse (objc_class_tree *tree, + int level, + void (*function) (objc_class_tree *, int)) +{ + struct objc_list *node; + + for (node = tree->subclasses; node; node = node->tail) + objc_postorder_traverse (node->head, level + 1, function); + (*function) (tree, level); +} + +/* Used to print a tree class hierarchy. */ + +#ifdef DEBUG +static void +__objc_tree_print (objc_class_tree *tree, int level) +{ + int i; + + for (i = 0; i < level; i++) + printf (" "); + printf ("%s\n", tree->class->name); +} +#endif + +/* Walks on a linked list of methods in the reverse order and executes + all the methods corresponding to `op' selector. Walking in the + reverse order assures the +load of class is executed first and then + +load of categories because of the way in which categories are + added to the class methods. */ + +static void +__objc_send_message_in_list (MethodList_t method_list, Class class, SEL op) +{ + int i; + + if (! method_list) + return; + + /* First execute the `op' message in the following method lists */ + __objc_send_message_in_list (method_list->method_next, class, op); + + /* Search the method list. */ + for (i = 0; i < method_list->method_count; i++) + { + Method_t mth = &method_list->method_list[i]; + + if (mth->method_name && sel_eq (mth->method_name, op) + && ! objc_hash_is_key_in_hash (__objc_load_methods, mth->method_imp)) + { + /* Add this method into the +load hash table */ + objc_hash_add (&__objc_load_methods, + mth->method_imp, + mth->method_imp); + + DEBUG_PRINTF ("sending +load in class: %s\n", class->name); + + /* The method was found and wasn't previously executed. */ + (*mth->method_imp) ((id)class, mth->method_name); + + break; + } + } +} + +static void +__objc_send_load (objc_class_tree *tree, + int level __attribute__ ((__unused__))) +{ + static SEL load_sel = 0; + Class class = tree->class; + MethodList_t method_list = class->class_pointer->methods; + + if (! load_sel) + load_sel = sel_register_name ("load"); + + __objc_send_message_in_list (method_list, class, load_sel); +} + +static void +__objc_destroy_class_tree_node (objc_class_tree *tree, + int level __attribute__ ((__unused__))) +{ + objc_free (tree); +} + +/* This is used to check if the relationship between two classes + before the runtime completely installs the classes. */ + +static BOOL +class_is_subclass_of_class (Class class, Class superclass) +{ + for (; class != Nil;) + { + if (class == superclass) + return YES; + class = class_superclass_of_class (class); + } + + return NO; +} + +/* This list contains all the classes in the runtime system for whom + their superclasses are not yet known to the runtime. */ +static struct objc_list *unresolved_classes = 0; + +/* Extern function used to reference the Object and NXConstantString + classes. */ + +extern void __objc_force_linking (void); + +void +__objc_force_linking (void) +{ + extern void __objc_linking (void); + __objc_linking (); +} + +/* Run through the statics list, removing modules as soon as all its + statics have been initialized. */ + +static void +objc_init_statics (void) +{ + struct objc_list **cell = &uninitialized_statics; + struct objc_static_instances **statics_in_module; + + LOCK(__objc_runtime_mutex); + + while (*cell) + { + int module_initialized = 1; + + for (statics_in_module = (*cell)->head; + *statics_in_module; statics_in_module++) + { + struct objc_static_instances *statics = *statics_in_module; + Class class = objc_lookup_class (statics->class_name); + + if (! class) + module_initialized = 0; + /* Actually, the static's class_pointer will be NULL when we + haven't been here before. However, the comparison is to be + reminded of taking into account class posing and to think about + possible semantics... */ + else if (class != statics->instances[0]->class_pointer) + { + id *inst; + + for (inst = &statics->instances[0]; *inst; inst++) + { + (*inst)->class_pointer = class; + + /* ??? Make sure the object will not be freed. With + refcounting, invoke `-retain'. Without refcounting, do + nothing and hope that `-free' will never be invoked. */ + + /* ??? Send the object an `-initStatic' or something to + that effect now or later on? What are the semantics of + statically allocated instances, besides the trivial + NXConstantString, anyway? */ + } + } + } + if (module_initialized) + { + /* Remove this module from the uninitialized list. */ + struct objc_list *this = *cell; + *cell = this->tail; + objc_free (this); + } + else + cell = &(*cell)->tail; + } + + UNLOCK(__objc_runtime_mutex); +} /* objc_init_statics */ + +void __objc_init_protocol_table(void); +/* This function is called by constructor functions generated for each + module compiled. (_GLOBAL_$I$...) The purpose of this function is + to gather the module pointers so that they may be processed by the + initialization routines as soon as possible. */ + +void +__objc_exec_class (Module_t module) +{ + /* Have we processed any constructors previously? This flag is used to + indicate that some global data structures need to be built. */ + static BOOL previous_constructors = 0; + + static struct objc_list *unclaimed_categories = 0; + + /* The symbol table (defined in objc-api.h) generated by gcc */ + Symtab_t symtab = module->symtab; + + /* The statics in this module */ + struct objc_static_instances **statics + = symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt]; + + /* Entry used to traverse hash lists */ + struct objc_list **cell; + + /* The table of selector references for this module */ + SEL selectors = symtab->refs; + + /* dummy counter */ + int i; + + DEBUG_PRINTF ("received module: %s\n", module->name); + + /* check compiler version */ + init_check_module_version (module); + + /* On the first call of this routine, initialize some data structures. */ + // FIXME: This should really be using a pthread_once or equivalent. + if (! previous_constructors) + { + /* Initialize thread-safe system */ + __objc_init_thread_system (); + __objc_sync_init(); + __objc_runtime_threads_alive = 1; + INIT_LOCK(objc_runtime_mutex); + + __objc_init_selector_tables (); + __objc_init_protocol_table (); + __objc_init_class_tables (); + __objc_init_dispatch_tables (); + __objc_class_tree_list = list_cons (NULL, __objc_class_tree_list); + __objc_load_methods = objc_hash_new (128, + (hash_func_type)objc_hash_ptr, + objc_compare_ptrs); + previous_constructors = 1; + } + + /* Save the module pointer for later processing. (not currently used) */ + LOCK(__objc_runtime_mutex); + __objc_module_list = list_cons (module, __objc_module_list); + + /* Replace referenced selectors from names to SEL's. */ + if (selectors) + { + for (i = 0; selectors[i].sel_id; ++i) + { + const char *name, *type; + name = (char *) selectors[i].sel_id; + type = (char *) selectors[i].sel_types; + /* Constructors are constant static data so we can safely store + pointers to them in the runtime structures. is_const == YES */ + __sel_register_typed_name (name, type, + (struct objc_selector *) &(selectors[i]), + YES); + } + } + + /* Parse the classes in the load module and gather selector information. */ + DEBUG_PRINTF ("gathering selectors from module: %s\n", module->name); + for (i = 0; i < symtab->cls_def_cnt; ++i) + { + Class class = (Class) symtab->defs[i]; + const char *superclass = (char *) class->super_class; + + /* Make sure we have what we think. */ + assert (CLS_ISCLASS (class)); + assert (CLS_ISMETA (class->class_pointer)); + DEBUG_PRINTF ("phase 1, processing class: %s\n", class->name); + + /* Initialize the subclass list to be NULL. + In some cases it isn't and this crashes the program. */ + class->subclass_list = NULL; + + /* Store the class in the class table and assign class numbers. */ + __objc_add_class_to_hash (class); + + /* Register all of the selectors in the class and meta class. */ + __objc_register_selectors_from_class (class); + __objc_register_selectors_from_class ((Class) class->class_pointer); + + /* Install the fake dispatch tables */ + __objc_install_premature_dtable (class); + __objc_install_premature_dtable (class->class_pointer); + + /* Register the instance methods as class methods, this is + only done for root classes. */ + __objc_register_instance_methods_to_class (class); + + if (class->protocols) + __objc_init_protocols (class->protocols); + + /* Check to see if the superclass is known in this point. If it's not + add the class to the unresolved_classes list. */ + if (superclass && ! objc_lookup_class (superclass)) + unresolved_classes = list_cons (class, unresolved_classes); + } + + /* Process category information from the module. */ + for (i = 0; i < symtab->cat_def_cnt; ++i) + { + Category_t category = symtab->defs[i + symtab->cls_def_cnt]; + Class class = objc_lookup_class (category->class_name); + + /* If the class for the category exists then append its methods. */ + if (class) + { + + DEBUG_PRINTF ("processing categories from (module,object): %s, %s\n", + module->name, + class->name); + + /* Do instance methods. */ + if (category->instance_methods) + class_add_method_list (class, category->instance_methods); + + /* Do class methods. */ + if (category->class_methods) + class_add_method_list ((Class) class->class_pointer, + category->class_methods); + + if (category->protocols) + { + __objc_init_protocols (category->protocols); + __objc_class_add_protocols (class, category->protocols); + } + + /* Register the instance methods as class methods, this is + only done for root classes. */ + __objc_register_instance_methods_to_class (class); + } + else + { + /* The object to which the category methods belong can't be found. + Save the information. */ + unclaimed_categories = list_cons (category, unclaimed_categories); + } + } + + if (statics) + uninitialized_statics = list_cons (statics, uninitialized_statics); + if (uninitialized_statics) + objc_init_statics (); + + /* Scan the unclaimed category hash. Attempt to attach any unclaimed + categories to objects. */ + for (cell = &unclaimed_categories; *cell; ) + { + Category_t category = (*cell)->head; + Class class = objc_lookup_class (category->class_name); + + if (class) + { + DEBUG_PRINTF ("attaching stored categories to object: %s\n", + class->name); + + list_remove_head (cell); + + if (category->instance_methods) + class_add_method_list (class, category->instance_methods); + + if (category->class_methods) + class_add_method_list ((Class) class->class_pointer, + category->class_methods); + + if (category->protocols) + { + __objc_init_protocols (category->protocols); + __objc_class_add_protocols (class, category->protocols); + } + + /* Register the instance methods as class methods, this is + only done for root classes. */ + __objc_register_instance_methods_to_class (class); + } + else + cell = &(*cell)->tail; + } + + if (unclaimed_proto_list && objc_lookup_class ("Protocol")) + { + list_mapcar (unclaimed_proto_list, + (void (*) (void *))__objc_init_protocols); + list_free (unclaimed_proto_list); + unclaimed_proto_list = 0; + } + + objc_send_load (); + + UNLOCK(__objc_runtime_mutex); +} + + static void +__objc_compute_ivar_offsets (Class class) +{ + int i = 0; + /* If this class was compiled with support for late-bound ivars, the + * instance_size field will contain 0 - {the size of the instance variables + * declared for just this class}. The individual instance variable offset + * fields will then be the offsets from the start of the class, and so must + * have the size of the parent class prepended. */ + if (class->instance_size <= 0) + { + Class super = class_superclass_of_class(class); + if (Nil == super) { return; } + long ivar_start = super->instance_size; + class->instance_size = ivar_start - class->instance_size; + /* For each instance variable, we add the offset if required (it will be zero + * if this class is compiled with a static ivar layout). We then set the + * value of a global variable to the offset value. + * + * Any class compiled with support for the non-fragile ABI, but not actually + * using it, will export the ivar offset field as a symbol. + * + * Note that using non-fragile ivars breaks @defs(). If you need equivalent + * functionality, provide an alternative @interface with all variables + * declared @public. + */ + for (i = 0 ; i < class->ivars->ivar_count ; i++) + { + struct objc_ivar *ivar = &class->ivars->ivar_list[i]; + ivar->ivar_offset += ivar_start; + /* If we're using the new ABI then we also set up the faster ivar + * offset variables. + */ + if (CLS_ISNEW_ABI(class)) + { + //*(class->ivar_offsets[i]) = ivar->ivar_offset; + } + } + } +} + +static void +objc_send_load (void) +{ + if (! __objc_module_list) + return; + + /* Try to find out if all the classes loaded so far also have their + superclasses known to the runtime. We suppose that the objects + that are allocated in the +load method are in general of a class + declared in the same module. */ + if (unresolved_classes) + { + Class class = unresolved_classes->head; + + while (objc_lookup_class ((char *) class->super_class)) + { + list_remove_head (&unresolved_classes); + if (unresolved_classes) + class = unresolved_classes->head; + else + break; + } + + /* If we still have classes for whom we don't have yet their + super classes known to the runtime we don't send the +load + messages. */ + if (unresolved_classes) + return; + } + + /* Special check to allow creating and sending messages to constant + strings in +load methods. If these classes are not yet known, + even if all the other classes are known, delay sending of +load. */ + if (! objc_lookup_class ("NXConstantString") || + ! objc_lookup_class ("Object")) + return; + + /* Iterate over all modules in the __objc_module_list and call on + them the __objc_create_classes_tree function. This function + creates a tree of classes that resembles the class hierarchy. */ + list_mapcar (__objc_module_list, + (void (*) (void *)) __objc_create_classes_tree); + + while (__objc_class_tree_list) + { +#ifdef DEBUG + objc_preorder_traverse (__objc_class_tree_list->head, + 0, __objc_tree_print); +#endif + objc_preorder_traverse (__objc_class_tree_list->head, + 0, __objc_send_load); + objc_postorder_traverse (__objc_class_tree_list->head, + 0, __objc_destroy_class_tree_node); + list_remove_head (&__objc_class_tree_list); + } + + list_mapcar (__objc_module_list, (void (*) (void *)) __objc_call_callback); + list_free (__objc_module_list); + __objc_module_list = NULL; +} + +static void +__objc_create_classes_tree (Module_t module) +{ + /* The runtime mutex is locked in this point */ + + Symtab_t symtab = module->symtab; + int i; + + /* Iterate thru classes defined in this module and insert them in + the classes tree hierarchy. */ + for (i = 0; i < symtab->cls_def_cnt; i++) + { + Class class = (Class) symtab->defs[i]; + + objc_tree_insert_class (class); + __objc_compute_ivar_offsets (class); + } +} + +static void +__objc_call_callback (Module_t module) +{ + /* The runtime mutex is locked in this point. */ + + Symtab_t symtab = module->symtab; + int i; + + /* Iterate thru classes defined in this module and call the callback + for each one. */ + for (i = 0; i < symtab->cls_def_cnt; i++) + { + Class class = (Class) symtab->defs[i]; + + /* Call the _objc_load_callback for this class. */ + if (_objc_load_callback) + _objc_load_callback (class, 0); + } + + /* Call the _objc_load_callback for categories. Don't register the + instance methods as class methods for categories to root classes + since they were already added in the class. */ + for (i = 0; i < symtab->cat_def_cnt; i++) + { + Category_t category = symtab->defs[i + symtab->cls_def_cnt]; + Class class = objc_lookup_class (category->class_name); + + if (_objc_load_callback) + _objc_load_callback (class, category); + } +} + +/* Sanity check the version of gcc used to compile `module'. */ + +static void +init_check_module_version (Module_t module) +{ + if ((module->version < OBJC_BACK_COMPATIBLE_VERSION) || + (module->version > OBJC_VERSION) || (module->size != sizeof (Module))) + { + int code; + + if (module->version > OBJC_VERSION) + code = OBJC_ERR_OBJC_VERSION; + else if (module->version < OBJC_VERSION) + code = OBJC_ERR_GCC_VERSION; + else + code = OBJC_ERR_MODULE_SIZE; + + objc_error (nil, code, "Module %s version %d doesn't match runtime %d\n", + module->name, (int)module->version, OBJC_VERSION); + } +} + +static void +__objc_init_protocols (struct objc_protocol_list *protos) +{ + size_t i; + static Class proto_class = 0; + static Class proto_class2 = 0; + + if (! protos) + return; + + LOCK(__objc_runtime_mutex); + + if (! proto_class) + proto_class = objc_lookup_class ("Protocol"); + if (! proto_class2) + proto_class2 = objc_lookup_class ("Protocol2"); + + if (! proto_class) + { + unclaimed_proto_list = list_cons (protos, unclaimed_proto_list); + UNLOCK(__objc_runtime_mutex); + return; + } + +#if 0 + assert (protos->next == 0); /* only single ones allowed */ +#endif + + for (i = 0; i < protos->count; i++) + { + struct objc_protocol *aProto = protos->list[i]; + switch (((size_t)aProto->class_pointer)) + { + case PROTOCOL_VERSION: + { + /* assign class pointer */ + aProto->class_pointer = proto_class; + + /* init super protocols */ + __objc_init_protocols (aProto->protocol_list); + break; + } + // FIXME: Initialize empty protocol by updating fields to reflect + // those of a real protocol with the same name + case OBJC2_PROTOCOL_VERSION: + { + /* assign class pointer */ + aProto->class_pointer = proto_class2; + /* init super protocols */ + __objc_init_protocols (aProto->protocol_list); + //__objc_unique_protocol (aProto); + } + default: + { + if (protos->list[i]->class_pointer != proto_class + && protos->list[i]->class_pointer != proto_class2) + { + objc_error (nil, OBJC_ERR_PROTOCOL_VERSION, + "Version %d doesn't match runtime protocol version %d\n", + (int) ((char *) protos->list[i]->class_pointer + - (char *) 0), + PROTOCOL_VERSION); + } + } + } + } + + UNLOCK(__objc_runtime_mutex); +} + +static void +__objc_class_add_protocols (Class class, struct objc_protocol_list *protos) +{ + /* Well... */ + if (! protos) + return; + + /* Add it... */ + protos->next = class->protocols; + class->protocols = protos; +} diff --git a/libobjc_entry.c b/libobjc_entry.c new file mode 100644 index 0000000..13e4749 --- /dev/null +++ b/libobjc_entry.c @@ -0,0 +1,54 @@ +/* GNU Objective C Runtime DLL Entry + Copyright (C) 1997, 2009 Free Software Foundation, Inc. + Contributed by Scott Christley + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#include + +/* + DLL entry function for Objective-C Runtime library + This function gets called everytime a process/thread attaches to DLL + */ +WINBOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, + LPVOID lpReserved) +{ + switch(ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + break; + case DLL_PROCESS_DETACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} + +/* + This section terminates the list of imports under GCC. If you do not + include this then you will have problems when linking with DLLs. + */ +asm (".section .idata$3\n" ".long 0,0,0,0,0,0,0,0"); diff --git a/linking.m b/linking.m new file mode 100644 index 0000000..fa31bdf --- /dev/null +++ b/linking.m @@ -0,0 +1,39 @@ +/* Force linking of classes required by Objective C runtime. + Copyright (C) 1997, 2009 Free Software Foundation, Inc. + Contributed by Ovidiu Predescu (ovidiu@net-community.com). + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#include +#include + +/* Generate references to Object and NXConstanstString classes since they are + needed by the runtime system to run correctly. */ + + +void __objc_linking (void) +{ + [Object name]; + [NXConstantString name]; +} + diff --git a/lock.h b/lock.h new file mode 100644 index 0000000..837759d --- /dev/null +++ b/lock.h @@ -0,0 +1,44 @@ +// libobjc requires recursive mutexes. These are delegated to the underlying +// threading implementation. + +#ifndef __LIBOBJC_LOCK_H_INCLUDED__ +#define __LIBOBJC_LOCK_H_INCLUDED__ + +#ifdef WIN32 +# include +typedef HANDLE mutex_t; +# define INIT_LOCK(x) x = CreateMutex(NULL, FALSE, NULL) +# define LOCK(x) WaitForSingleObject(*x, INFINITE) +# define UNLOCK(x) ReleaseMutex(*x) +# define DESTROY_LOCK(x) CloseHandle(*x) +#else + +#define _XOPEN 500 +# include + +typedef pthread_mutex_t mutex_t; +// If this pthread implementation has a static initializer for recursive +// mutexes, use that, otherwise fall back to the portable version +# ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +# define INIT_LOCK(x) x = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +# elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER) +# define INIT_LOCK(x) x = PTHREAD_RECURSIVE_MUTEX_INITIALIZER +# else +# define INIT_LOCK(x) init_recursive_mutex(&(x)) + +static inline void init_recursive_mutex(pthread_mutex_t *x) +{ + pthread_mutexattr_t recursiveAttributes; + pthread_mutexattr_init(&recursiveAttributes); + pthread_mutexattr_settype(&recursiveAttributes, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(x, &recursiveAttributes); + pthread_mutexattr_destroy(&recursiveAttributes); +} +# endif + +# define LOCK(x) pthread_mutex_lock(x) +# define UNLOCK(x) pthread_mutex_unlock(x) +# define DESTROY_LOCK(x) pthread_mutex_destroy(x) +#endif + +#endif // __LIBOBJC_LOCK_H_INCLUDED__ diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..6d8af94 --- /dev/null +++ b/misc.c @@ -0,0 +1,184 @@ +/* GNU Objective C Runtime Miscellaneous + Copyright (C) 1993, 1994, 1995, 1996, 1997, 2002, 2009 + Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#define __USE_FIXED_PROTOTYPES__ +#include +#include "objc/runtime-legacy.h" + +/* +** Error handler function +** NULL so that default is to just print to stderr +*/ +static objc_error_handler _objc_error_handler = NULL; + +/* Trigger an objc error */ +void +objc_error (id object, int code, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + objc_verror (object, code, fmt, ap); + va_end (ap); +} + +/* Trigger an objc error */ +void +objc_verror (id object, int code, const char *fmt, va_list ap) +{ + BOOL result = NO; + + /* Call the error handler if its there + Otherwise print to stderr */ + if (_objc_error_handler) + result = (*_objc_error_handler) (object, code, fmt, ap); + else + vfprintf (stderr, fmt, ap); + + /* Continue if the error handler says its ok + Otherwise abort the program */ + if (result) + return; + else + abort (); +} + +/* Set the error handler */ +objc_error_handler +objc_set_error_handler (objc_error_handler func) +{ + objc_error_handler temp = _objc_error_handler; + _objc_error_handler = func; + return temp; +} + +/* +** Standard functions for memory allocation and disposal. +** Users should use these functions in their ObjC programs so +** that they work properly with garbage collectors as well as +** can take advantage of the exception/error handling available. +*/ + +void * +objc_malloc (size_t size) +{ + void *res = (void *) (*_objc_malloc) (size); + if (! res) + objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); + return res; +} + +void * +objc_atomic_malloc (size_t size) +{ + void *res = (void *) (*_objc_atomic_malloc) (size); + if (! res) + objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); + return res; +} + +void * +objc_valloc (size_t size) +{ + void *res = (void *) (*_objc_valloc) (size); + if (! res) + objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); + return res; +} + +void * +objc_realloc (void *mem, size_t size) +{ + void *res = (void *) (*_objc_realloc) (mem, size); + if (! res) + objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); + return res; +} + +void * +objc_calloc (size_t nelem, size_t size) +{ + void *res = (void *) (*_objc_calloc) (nelem, size); + if (! res) + objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted\n"); + return res; +} + +void +objc_free (void *mem) +{ + (*_objc_free) (mem); +} + +/* +** Hook functions for memory allocation and disposal. +** This makes it easy to substitute garbage collection systems +** such as Boehm's GC by assigning these function pointers +** to the GC's allocation routines. By default these point +** to the ANSI standard malloc, realloc, free, etc. +** +** Users should call the normal objc routines above for +** memory allocation and disposal within their programs. +*/ + +#if OBJC_WITH_GC +#include + +static void * +GC_calloc (size_t nelem, size_t size) +{ + void *p = GC_malloc (nelem * size); + if (! p) + objc_error (nil, OBJC_ERR_MEMORY, "Virtual memory exhausted!\n"); + + memset (p, 0, nelem * size); + return p; +} + +static void +noFree (void *p) +{ +} + +void *(*_objc_malloc) (size_t) = GC_malloc; +void *(*_objc_atomic_malloc) (size_t) = GC_malloc_atomic; +void *(*_objc_valloc) (size_t) = GC_malloc; +void *(*_objc_realloc) (void *, size_t) = GC_realloc; +void *(*_objc_calloc) (size_t, size_t) = GC_calloc; +void (*_objc_free) (void *) = noFree; + +#else /* !OBJC_WITH_GC */ + +void *(*_objc_malloc) (size_t) = malloc; +void *(*_objc_atomic_malloc) (size_t) = malloc; +void *(*_objc_valloc) (size_t) = malloc; +void *(*_objc_realloc) (void *, size_t) = realloc; +void *(*_objc_calloc) (size_t, size_t) = calloc; +void (*_objc_free) (void *) = free; + + +#endif /* !OBJC_WITH_GC */ diff --git a/mutation.m b/mutation.m new file mode 100644 index 0000000..acc395f --- /dev/null +++ b/mutation.m @@ -0,0 +1,11 @@ +#include +#include + +// This function is exported as a weak symbol to enable GNUstep or some other +// framework to replace it trivially +void __attribute__((weak)) objc_enumerationMutation(void *obj) +{ + fprintf(stderr, "Mutation occured during enumeration."); + abort(); +} + diff --git a/nil_method.c b/nil_method.c new file mode 100644 index 0000000..c070b0c --- /dev/null +++ b/nil_method.c @@ -0,0 +1,54 @@ +/* GNU Objective C Runtime nil receiver function + Copyright (C) 1993, 1995, 1996, 2002, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +/* This is the nil method, the function that is called when the receiver + of a method is nil */ + +#include "objc/runtime-legacy.h" + +/* When the receiver of a method invocation is nil, the runtime + returns nil_method() as the method implementation. This function + will be casted to whatever function was supposed to be executed to + execute that method (that function will take an id, followed by a + SEL, followed by who knows what arguments, depends on the method), + and executed. + + For this reason, nil_method() should be a function which can be + called in place of any function taking an 'id' argument followed by + a 'SEL' argument, followed by zero, or one, or any number of + arguments (both a fixed number, or a variable number !). + + There is no "proper" implementation of such a nil_method function + in C, however in all existing implementations it does not matter + when extra arguments are present, so we can simply create a function + taking a receiver and a selector, and all other arguments will be + ignored. :-) +*/ + +id +nil_method (id receiver, SEL op __attribute__ ((__unused__))) +{ + return receiver; +} diff --git a/objc/Availability.h b/objc/Availability.h new file mode 100644 index 0000000..81d4cf9 --- /dev/null +++ b/objc/Availability.h @@ -0,0 +1,12 @@ + +#ifdef STRICT_MACOS_X +# define OBJC_NONPORTABLE __attribute__((error("Function not supported by the Apple runtime"))) +#else +# define OBJC_NONPORTABLE +#endif + +#if !defined(__DEPRECATE_DIRECT_ACCESS) || defined(__OBJC_LEGACY_GNU_MODE__) || defined(__OBJC_RUNTIME_INTERNAL__) +# define OBJC_DEPRECATED +#else +# define OBJC_DEPRECATED __attribute__((deprecated)) +#endif diff --git a/objc/NXConstStr.h b/objc/NXConstStr.h new file mode 100644 index 0000000..40eb66e --- /dev/null +++ b/objc/NXConstStr.h @@ -0,0 +1,51 @@ +/* Interface for the NXConstantString class for Objective-C. + Copyright (C) 1995, 2004, 2009 Free Software Foundation, Inc. + Contributed by Pieter J. Schoenmakers + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __nxconstantstring_INCLUDE_GNU +#define __nxconstantstring_INCLUDE_GNU + +#include "Object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +@interface NXConstantString: Object +{ + char *c_string; + unsigned int len; +} + +-(const char *) cString; +-(unsigned int) length; + +@end + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/objc/Object.h b/objc/Object.h new file mode 100644 index 0000000..6b8c6f1 --- /dev/null +++ b/objc/Object.h @@ -0,0 +1,131 @@ +/* Interface for the Object class for Objective-C. + Copyright (C) 1993, 1994, 1995, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __object_INCLUDE_GNU +#define __object_INCLUDE_GNU + +#include "objc.h" +#include "typedstream.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * All classes are derived from Object. As such, + * this is the overhead tacked onto those objects. + */ +@interface Object +{ + Class isa; /* A pointer to the instance's class structure */ +} + + /* Initializing classes and instances */ ++ initialize; +- init; + + /* Creating, freeing, and copying instances */ ++ new; ++ alloc; +- free; +- copy; +- shallowCopy; +- deepen; +- deepCopy; + + /* Identifying classes */ +- (Class)class; +- (Class)superClass; +- (MetaClass)metaClass; +- (const char *)name; + + /* Identifying and comparing objects */ +- self; +- (unsigned int)hash; +- (BOOL)isEqual:anObject; +- (int)compare:(id)anotherObject; + + /* Testing object type */ +- (BOOL)isMetaClass; +- (BOOL)isClass; +- (BOOL)isInstance; + + /* Testing inheritance relationships */ +- (BOOL)isKindOf:(Class)aClassObject; +- (BOOL)isMemberOf:(Class)aClassObject; +- (BOOL)isKindOfClassNamed:(const char *)aClassName; +- (BOOL)isMemberOfClassNamed:(const char *)aClassName; + + /* Testing class functionality */ ++ (BOOL)instancesRespondTo:(SEL)aSel; +- (BOOL)respondsTo:(SEL)aSel; + + /* Testing protocol conformance */ +- (BOOL)conformsTo:(Protocol*)aProtocol; + + /* Introspection */ ++ (IMP)instanceMethodFor:(SEL)aSel; +- (IMP)methodFor:(SEL)aSel; ++ (struct objc_method_description *)descriptionForInstanceMethod:(SEL)aSel; +- (struct objc_method_description *)descriptionForMethod:(SEL)aSel; + + /* Sending messages determined at run time */ +- perform:(SEL)aSel; +- perform:(SEL)aSel with:anObject; +- perform:(SEL)aSel with:anObject1 with:anObject2; + + /* Forwarding */ +- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame; +- (retval_t)performv:(SEL)aSel :(arglist_t)argFrame; + + /* Posing */ ++ poseAs:(Class)aClassObject; +- (Class)transmuteClassTo:(Class)aClassObject; + + /* Enforcing intentions */ +- subclassResponsibility:(SEL)aSel; +- notImplemented:(SEL)aSel; +- shouldNotImplement:(SEL)aSel; + + /* Error handling */ +- doesNotRecognize:(SEL)aSel; +- error:(const char *)aString, ...; + + /* Archiving */ ++ (int)version; ++ setVersion:(int)aVersion; ++ (int)streamVersion: (TypedStream*)aStream; + +- read: (TypedStream*)aStream; +- write: (TypedStream*)aStream; +- awake; + +@end + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/objc/Protocol.h b/objc/Protocol.h new file mode 100644 index 0000000..75cb388 --- /dev/null +++ b/objc/Protocol.h @@ -0,0 +1,62 @@ +/* Declare the class Protocol for Objective C programs. + Copyright (C) 1993, 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __Protocol_INCLUDE_GNU +#define __Protocol_INCLUDE_GNU + +#include "Object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +@interface Protocol : Object +{ +@protected + char *protocol_name; + struct objc_protocol_list *protocol_list; + struct objc_method_description_list *instance_methods, *class_methods; +} + +/* Obtaining attributes intrinsic to the protocol */ + +- (const char *)name; + +/* Testing protocol conformance */ + +- (BOOL) conformsTo: (Protocol *)aProtocolObject; + +/* Looking up information specific to a protocol */ + +- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel; +- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel; + +@end + +#ifdef __cplusplus +} +#endif + +#endif /* not __Protocol_INCLUDE_GNU */ diff --git a/objc/blocks_runtime.h b/objc/blocks_runtime.h new file mode 100644 index 0000000..5edab46 --- /dev/null +++ b/objc/blocks_runtime.h @@ -0,0 +1,12 @@ +/* + * Blocks Runtime + */ + +#ifdef __cplusplus +#define BLOCKS_EXPORT extern "C" +#else +#define BLOCKS_EXPORT extern +#endif + +BLOCKS_EXPORT void *Block_copy(void *); +BLOCKS_EXPORT void Block_release(void *); diff --git a/objc/encoding.h b/objc/encoding.h new file mode 100644 index 0000000..57ac224 --- /dev/null +++ b/objc/encoding.h @@ -0,0 +1,107 @@ +/* Encoding of types for Objective C. + Copyright (C) 1993, 1997, 2002, 2004, 2009 Free Software Foundation, Inc. + +Author: Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __encoding_INCLUDE_GNU +#define __encoding_INCLUDE_GNU + +#include "objc-api.h" +#include "Availability.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define _C_CONST 'r' +#define _C_IN 'n' +#define _C_INOUT 'N' +#define _C_OUT 'o' +#define _C_BYCOPY 'O' +#define _C_BYREF 'R' +#define _C_ONEWAY 'V' +#define _C_GCINVISIBLE '!' + +#define _F_CONST 0x01 +#define _F_IN 0x01 +#define _F_OUT 0x02 +#define _F_INOUT 0x03 +#define _F_BYCOPY 0x04 +#define _F_BYREF 0x08 +#define _F_ONEWAY 0x10 +#define _F_GCINVISIBLE 0x20 + +int objc_aligned_size (const char *type) OBJC_DEPRECATED; +int objc_sizeof_type (const char *type) OBJC_DEPRECATED; +int objc_alignof_type (const char *type) OBJC_DEPRECATED; +int objc_aligned_size (const char *type) OBJC_DEPRECATED; +int objc_promoted_size (const char *type) OBJC_DEPRECATED; + +const char *objc_skip_type_qualifiers (const char *type) OBJC_DEPRECATED; +const char *objc_skip_typespec (const char *type) OBJC_DEPRECATED; +const char *objc_skip_offset (const char *type) OBJC_DEPRECATED; +const char *objc_skip_argspec (const char *type) OBJC_DEPRECATED; +int method_get_number_of_arguments (struct objc_method *) OBJC_DEPRECATED; +int method_get_sizeof_arguments (struct objc_method *) OBJC_DEPRECATED; + +char *method_get_first_argument (struct objc_method *, + arglist_t argframe, + const char **type) OBJC_DEPRECATED; +char *method_get_next_argument (arglist_t argframe, + const char **type) OBJC_DEPRECATED; +char *method_get_nth_argument (struct objc_method *m, + arglist_t argframe, + int arg, + const char **type) OBJC_DEPRECATED; + +unsigned objc_get_type_qualifiers (const char *type) OBJC_DEPRECATED; + + +struct objc_struct_layout +{ + const char *original_type; + const char *type; + const char *prev_type; + unsigned int record_size; + unsigned int record_align; +}; + +void objc_layout_structure (const char *type, + struct objc_struct_layout *layout) OBJC_DEPRECATED; +BOOL objc_layout_structure_next_member (struct objc_struct_layout *layout) OBJC_DEPRECATED; +void objc_layout_finish_structure (struct objc_struct_layout *layout, + unsigned int *size, + unsigned int *align) OBJC_DEPRECATED; +void objc_layout_structure_get_info (struct objc_struct_layout *layout, + unsigned int *offset, + unsigned int *align, + const char **type) OBJC_DEPRECATED; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __encoding_INCLUDE_GNU */ diff --git a/objc/hash.h b/objc/hash.h new file mode 100644 index 0000000..475a4fc --- /dev/null +++ b/objc/hash.h @@ -0,0 +1,216 @@ +/* Hash tables for Objective C method dispatch. + Copyright (C) 1993, 1995, 1996, 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + + +#ifndef __hash_INCLUDE_GNU +#define __hash_INCLUDE_GNU + +#include +#include +#include "objc.h" +#include "Availability.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * This data structure is used to hold items + * stored in a hash table. Each node holds + * a key/value pair. + * + * Items in the cache are really of type void *. + */ +typedef struct cache_node +{ + struct cache_node *next; /* Pointer to next entry on the list. + NULL indicates end of list. */ + const void *key; /* Key used to locate the value. Used + to locate value when more than one + key computes the same hash + value. */ + void *value; /* Value stored for the key. */ +} *node_ptr; + + +/* + * This data type is the function that computes a hash code given a key. + * Therefore, the key can be a pointer to anything and the function specific + * to the key type. + * + * Unfortunately there is a mutual data structure reference problem with this + * typedef. Therefore, to remove compiler warnings the functions passed to + * objc_hash_new will have to be casted to this type. + */ +typedef unsigned int (*hash_func_type) (void *, const void *); + +/* + * This data type is the function that compares two hash keys and returns an + * integer greater than, equal to, or less than 0, according as the first + * parameter is lexicographically greater than, equal to, or less than the + * second. + */ + +typedef int (*compare_func_type) (const void *, const void *); + + +/* + * This data structure is the cache. + * + * It must be passed to all of the hashing routines + * (except for new). + */ +typedef struct cache +{ + /* Variables used to implement the hash itself. */ + node_ptr *node_table; /* Pointer to an array of hash nodes. */ + /* Variables used to track the size of the hash table so to determine + when to resize it. */ + unsigned int size; /* Number of buckets allocated for the hash table + (number of array entries allocated for + "node_table"). Must be a power of two. */ + unsigned int used; /* Current number of entries in the hash table. */ + unsigned int mask; /* Precomputed mask. */ + + /* Variables used to implement indexing through the hash table. */ + + unsigned int last_bucket; /* Tracks which entry in the array where + the last value was returned. */ + /* Function used to compute a hash code given a key. + This function is specified when the hash table is created. */ + hash_func_type hash_func; + /* Function used to compare two hash keys to see if they are equal. */ + compare_func_type compare_func; +} *cache_ptr; + + +/* Two important hash tables. */ +extern cache_ptr module_hash_table, class_hash_table; + +/* Allocate and initialize a hash table. */ + +cache_ptr objc_hash_new (unsigned int size, + hash_func_type hash_func, + compare_func_type compare_func) OBJC_DEPRECATED; + +/* Deallocate all of the hash nodes and the cache itself. */ + +void objc_hash_delete (cache_ptr cache) OBJC_DEPRECATED; + +/* Add the key/value pair to the hash table. If the + hash table reaches a level of fullness then it will be resized. + + assert if the key is already in the hash. */ + +void objc_hash_add (cache_ptr *cachep, const void *key, void *value) OBJC_DEPRECATED; + +/* Remove the key/value pair from the hash table. + assert if the key isn't in the table. */ + +void objc_hash_remove (cache_ptr cache, const void *key) OBJC_DEPRECATED; + +/* Used to index through the hash table. Start with NULL + to get the first entry. + + Successive calls pass the value returned previously. + ** Don't modify the hash during this operation *** + + Cache nodes are returned such that key or value can + be extracted. */ + +node_ptr objc_hash_next (cache_ptr cache, node_ptr node) OBJC_DEPRECATED; + +/* Used to return a value from a hash table using a given key. */ + +void *objc_hash_value_for_key (cache_ptr cache, const void *key) OBJC_DEPRECATED; + +/* Used to determine if the given key exists in the hash table */ + +BOOL objc_hash_is_key_in_hash (cache_ptr cache, const void *key) OBJC_DEPRECATED; + +/************************************************ + + Useful hashing functions. + + Declared inline for your pleasure. + +************************************************/ + +/* Calculate a hash code by performing some + manipulation of the key pointer. (Use the lowest bits + except for those likely to be 0 due to alignment.) */ + +static inline unsigned int +objc_hash_ptr (cache_ptr cache, const void *key) +{ + return ((size_t)key / sizeof (void *)) & cache->mask; +} + + +/* Calculate a hash code by iterating over a NULL + terminate string. */ +static inline unsigned int +objc_hash_string (cache_ptr cache, const void *key) +{ + unsigned int ret = 0; + unsigned int ctr = 0; + const char *ckey = (const char *) key; + + while (*ckey) { + ret ^= *ckey++ << ctr; + ctr = (ctr + 1) % sizeof (void *); + } + + return ret & cache->mask; +} + + +/* Compare two pointers for equality. */ +static inline int +objc_compare_ptrs (const void *k1, const void *k2) +{ + return (k1 == k2); +} + + +/* Compare two strings. */ +static inline int +objc_compare_strings (const void *k1, const void *k2) +{ + if (k1 == k2) + return 1; + else if (k1 == 0 || k2 == 0) + return 0; + else + return ! strcmp ((const char *) k1, (const char *) k2); +} + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* not __hash_INCLUDE_GNU */ diff --git a/objc/objc-api.h b/objc/objc-api.h new file mode 100644 index 0000000..0a9b223 --- /dev/null +++ b/objc/objc-api.h @@ -0,0 +1,685 @@ +/* GNU Objective-C Runtime API. + Copyright (C) 1993, 1995, 1996, 1997, 2001, 2002, 2003, 2004, 2005, + 2007, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __objc_api_INCLUDE_GNU +#define __objc_api_INCLUDE_GNU + +#include "objc.h" +#include "hash.h" +#include "thr.h" +#include "objc-decls.h" +#include "Availability.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* For functions which return Method_t */ +#define METHOD_NULL (Method_t)0 + /* Boolean typedefs */ +/* Method descriptor returned by introspective Object methods. + This is really just the first part of the more complete objc_method + structure defined below and used internally by the runtime. */ +struct objc_method_description +{ + SEL name; /* this is a selector, not a string */ + char *types; /* type encoding */ +}; + +/* Filer types used to describe Ivars and Methods. */ +#define _C_ID '@' +#define _C_CLASS '#' +#define _C_SEL ':' +#define _C_CHR 'c' +#define _C_UCHR 'C' +#define _C_SHT 's' +#define _C_USHT 'S' +#define _C_INT 'i' +#define _C_UINT 'I' +#define _C_LNG 'l' +#define _C_ULNG 'L' +#define _C_LNG_LNG 'q' +#define _C_ULNG_LNG 'Q' +#define _C_FLT 'f' +#define _C_DBL 'd' +#define _C_BFLD 'b' +#define _C_BOOL 'B' +#define _C_VOID 'v' +#define _C_UNDEF '?' +#define _C_PTR '^' +#define _C_CHARPTR '*' +#define _C_ATOM '%' +#define _C_ARY_B '[' +#define _C_ARY_E ']' +#define _C_UNION_B '(' +#define _C_UNION_E ')' +#define _C_STRUCT_B '{' +#define _C_STRUCT_E '}' +#define _C_VECTOR '!' +#define _C_COMPLEX 'j' + + +/* Error handling + + Call objc_error() or objc_verror() to record an error; this error + routine will generally exit the program but not necessarily if the + user has installed his own error handler. + + Call objc_set_error_handler to assign your own function for + handling errors. The function should return YES if it is ok + to continue execution, or return NO or just abort if the + program should be stopped. The default error handler is just to + print a message on stderr. + + The error handler function should be of type objc_error_handler + The first parameter is an object instance of relevance. + The second parameter is an error code. + The third parameter is a format string in the printf style. + The fourth parameter is a variable list of arguments. */ +extern void objc_error(id object, int code, const char* fmt, ...) OBJC_DEPRECATED; +extern void objc_verror(id object, int code, const char* fmt, va_list ap) OBJC_DEPRECATED; +typedef BOOL (*objc_error_handler)(id, int code, const char *fmt, va_list ap) OBJC_DEPRECATED; +extern objc_error_handler objc_set_error_handler(objc_error_handler func) OBJC_DEPRECATED; + +/* Error codes + These are used by the runtime library, and your + error handling may use them to determine if the error is + hard or soft thus whether execution can continue or abort. */ +#define OBJC_ERR_UNKNOWN 0 /* Generic error */ + +#define OBJC_ERR_OBJC_VERSION 1 /* Incorrect runtime version */ +#define OBJC_ERR_GCC_VERSION 2 /* Incorrect compiler version */ +#define OBJC_ERR_MODULE_SIZE 3 /* Bad module size */ +#define OBJC_ERR_PROTOCOL_VERSION 4 /* Incorrect protocol version */ + +#define OBJC_ERR_MEMORY 10 /* Out of memory */ + +#define OBJC_ERR_RECURSE_ROOT 20 /* Attempt to archive the root + object more than once. */ +#define OBJC_ERR_BAD_DATA 21 /* Didn't read expected data */ +#define OBJC_ERR_BAD_KEY 22 /* Bad key for object */ +#define OBJC_ERR_BAD_CLASS 23 /* Unknown class */ +#define OBJC_ERR_BAD_TYPE 24 /* Bad type specification */ +#define OBJC_ERR_NO_READ 25 /* Cannot read stream */ +#define OBJC_ERR_NO_WRITE 26 /* Cannot write stream */ +#define OBJC_ERR_STREAM_VERSION 27 /* Incorrect stream version */ +#define OBJC_ERR_BAD_OPCODE 28 /* Bad opcode */ + +#define OBJC_ERR_UNIMPLEMENTED 30 /* Method is not implemented */ + +#define OBJC_ERR_BAD_STATE 40 /* Bad thread state */ + +/* Set this variable nonzero to print a line describing each + message that is sent. (this is currently disabled) */ +extern BOOL objc_trace; + + +/* For every class which happens to have statically allocated instances in + this module, one OBJC_STATIC_INSTANCES is allocated by the compiler. + INSTANCES is NULL terminated and points to all statically allocated + instances of this class. */ +struct objc_static_instances +{ + char *class_name; +#ifdef __cplusplus + id instances[1]; +#else + id instances[0]; +#endif +}; + +/* Whereas a Module (defined further down) is the root (typically) of a file, + a Symtab is the root of the class and category definitions within the + module. + + A Symtab contains a variable length array of pointers to classes and + categories defined in the module. */ +typedef struct objc_symtab { + unsigned long sel_ref_cnt; /* Unknown. */ + SEL refs; /* Unknown. */ + unsigned short cls_def_cnt; /* Number of classes compiled + (defined) in the module. */ + unsigned short cat_def_cnt; /* Number of categories + compiled (defined) in the + module. */ + + void *defs[1]; /* Variable array of pointers. + cls_def_cnt of type Class + followed by cat_def_cnt of + type Category_t, followed + by a NULL terminated array + of objc_static_instances. */ +} Symtab, *Symtab_t; + + +/* +** The compiler generates one of these structures for each module that +** composes the executable (eg main.m). +** +** This data structure is the root of the definition tree for the module. +** +** A collect program runs between ld stages and creates a ObjC ctor array. +** That array holds a pointer to each module structure of the executable. +*/ +typedef struct objc_module { + unsigned long version; /* Compiler revision. */ + unsigned long size; /* sizeof(Module). */ + const char* name; /* Name of the file where the + module was generated. The + name includes the path. */ + + Symtab_t symtab; /* Pointer to the Symtab of + the module. The Symtab + holds an array of + pointers to + the classes and categories + defined in the module. */ +} Module, *Module_t; + + +/* +** The compiler generates one of these structures for a class that has +** instance variables defined in its specification. +*/ +typedef struct objc_ivar { + const char* ivar_name; /* Name of the instance + variable as entered in the + class definition. */ + const char* ivar_type; /* Description of the Ivar's + type. Useful for + debuggers. */ + int ivar_offset; /* Byte offset from the base + address of the instance + structure to the variable. */ +} *Ivar_t; + +typedef struct objc_ivar_list { + int ivar_count; /* Number of structures (Ivar) + contained in the list. One + structure per instance + variable defined in the + class. */ + struct objc_ivar ivar_list[1]; /* Variable length + structure. */ +} IvarList, *IvarList_t; + +enum PropertyAttributeKind +{ + OBJC_PR_noattr = 0x00, + OBJC_PR_readonly = 0x01, + OBJC_PR_getter = 0x02, + OBJC_PR_assign = 0x04, + OBJC_PR_readwrite = 0x08, + OBJC_PR_retain = 0x10, + OBJC_PR_copy = 0x20, + OBJC_PR_nonatomic = 0x40, + OBJC_PR_setter = 0x80 +}; + +/** + * Structure used for property enumeration. Note that property enumeration is currently quite broken on OS X, so achieving full compatibility there is impossible. Instead, we strive to achieve com + */ +struct objc_property { + const char *name; + const char attributes; + const char isSynthesized; + const char *setter_name; + const char *setter_types; + const char *getter_name; + const char *getter_types; +}; + +struct objc_property_list { + int property_count; + struct objc_property_list *next; /* UNUSED. In future, categories will be + allowed to add properties and then this will + be used to link the declarations together. */ + struct objc_property properties[1]; +}; + +/* +** The compiler generates one (or more) of these structures for a class that +** has methods defined in its specification. +** +** The implementation of a class can be broken into separate pieces in a file +** and categories can break them across modules. To handle this problem is a +** singly linked list of methods. +*/ +typedef struct objc_method { + SEL method_name; /* The selector used to identify + the method. The types for this + selector should match the next + field. */ + + const char* method_types; /* Description of the method's + parameter list. Useful for + debuggers. */ + IMP method_imp; /* Address of the method in the + executable. */ +} Method, *Method_t; + +typedef struct objc_method_list { + struct objc_method_list* method_next; /* This variable is used to link + a method list to another. It + is a singly linked list. */ + int method_count; /* Number of methods defined in + this structure. */ + Method method_list[1]; /* Variable length + structure. */ +} MethodList, *MethodList_t; + +struct objc_protocol_list { + struct objc_protocol_list *next; + size_t count; + Protocol *list[1]; +}; + +/* +** This is used to assure consistent access to the info field of +** classes +*/ +#ifndef HOST_BITS_PER_LONG +#define HOST_BITS_PER_LONG (sizeof(long)*8) +#endif + +#define __CLS_INFO(cls) ((cls)->info) +#define __CLS_ISINFO(cls, mask) ((__CLS_INFO(cls)&mask)==mask) +#define __CLS_SETINFO(cls, mask) (__CLS_INFO(cls) |= mask) + +/* The structure is of type MetaClass */ +#define _CLS_META 0x2L +#define CLS_ISMETA(cls) ((cls)&&__CLS_ISINFO(cls, _CLS_META)) + + +/* The structure is of type Class */ +#define _CLS_CLASS 0x1L +#define CLS_ISCLASS(cls) ((cls)&&__CLS_ISINFO(cls, _CLS_CLASS)) + +/* +** The class is initialized within the runtime. This means that +** it has had correct super and sublinks assigned +*/ +#define _CLS_RESOLV 0x8L +#define CLS_ISRESOLV(cls) __CLS_ISINFO(cls, _CLS_RESOLV) +#define CLS_SETRESOLV(cls) __CLS_SETINFO(cls, _CLS_RESOLV) + +/* +** The class has been send a +initialize message or a such is not +** defined for this class +*/ +#define _CLS_INITIALIZED 0x04L +#define CLS_ISINITIALIZED(cls) __CLS_ISINFO(cls, _CLS_INITIALIZED) +#define CLS_SETINITIALIZED(cls) __CLS_SETINFO(cls, _CLS_INITIALIZED) + +/* +** The class uses the new, Objective-C 2, runtime ABI. This ABI defines an ABI +** version field inside the class, and so will be used for all subsequent +** versions that retain some degree of compatibility. +*/ +#define _CLS_NEW_ABI 0x10L +#define CLS_ISNEW_ABI(cls) __CLS_ISINFO(cls, _CLS_NEW_ABI) +#define CLS_SETNEW_ABI(cls) __CLS_SETINFO(cls, _CLS_NEW_ABI) +/* +** The class was created at runtime and exists in heap-backed memory. +*/ +#define _CLS_RUNTIME 0x20L +#define CLS_ISRUNTIME(cls) __CLS_ISINFO(cls, _CLS_RUNTIME) +#define CLS_SETRUNTIME(cls) __CLS_SETINFO(cls, _CLS_RUNTIME) +/* +** Instances of this class have a reference count and plane ID prepended to +** them. The default for this is set for classes, unset for metaclasses. It +** should be cleared by protocols, constant strings, and objects not allocated +** by NSAllocateObject(). + */ +#define _CLS_PLANE_AWARE 0x40L +#define CLS_ISPLANE_AWARE(cls) __CLS_ISINFO(cls, _CLS_PLANE_AWARE) +#define CLS_SETPLANE_AWARE(cls) __CLS_SETINFO(cls, _CLS_PLANE_AWARE) +/* +** The class number of this class. This must be the same for both the +** class and its meta class object +*/ +#define CLS_GETNUMBER(cls) (__CLS_INFO(cls) >> (HOST_BITS_PER_LONG/2)) +#define CLS_SETNUMBER(cls, num) \ + ({ (cls)->info <<= (HOST_BITS_PER_LONG/2); \ + (cls)->info >>= (HOST_BITS_PER_LONG/2); \ + __CLS_SETINFO(cls, (((unsigned long)num) << (HOST_BITS_PER_LONG/2))); }) + +/* +** The compiler generates one of these structures for each category. A class +** may have many categories and contain both instance and factory methods. +*/ +typedef struct objc_category { + const char* category_name; /* Name of the category. Name + contained in the () of the + category definition. */ + const char* class_name; /* Name of the class to which + the category belongs. */ + MethodList_t instance_methods; /* Linked list of instance + methods defined in the + category. NULL indicates no + instance methods defined. */ + MethodList_t class_methods; /* Linked list of factory + methods defined in the + category. NULL indicates no + class methods defined. */ + struct objc_protocol_list *protocols; /* List of Protocols + conformed to */ +} Category, *Category_t; + +/* +** Structure used when a message is send to a class's super class. The +** compiler generates one of these structures and passes it to +** objc_msg_super. +*/ +typedef struct objc_super { + id self; /* Id of the object sending + the message. */ +#ifdef __cplusplus + Class super_class; +#else + Class class; /* Object's super class. */ +#endif +} Super, *Super_t; + +IMP objc_msg_lookup_super(Super_t super, SEL sel); + +retval_t objc_msg_sendv(id, SEL, arglist_t); + + + +/* +** This is a hook which is called by objc_lookup_class and +** objc_get_class if the runtime is not able to find the class. +** This may e.g. try to load in the class using dynamic loading. +** The function is guaranteed to be passed a non-NULL name string. +*/ +objc_EXPORT Class (*_objc_lookup_class)(const char *name); + +/* +** This is a hook which is called by __objc_exec_class every time a class +** or a category is loaded into the runtime. This may e.g. help a +** dynamic loader determine the classes that have been loaded when +** an object file is dynamically linked in. +*/ +objc_EXPORT void (*_objc_load_callback)(Class _class, Category* category); + +/* +** Hook functions for allocating, copying and disposing of instances +*/ +objc_EXPORT id (*_objc_object_alloc)(Class _class); +objc_EXPORT id (*_objc_object_copy)(id object); +objc_EXPORT id (*_objc_object_dispose)(id object); + +/* +** Standard functions for memory allocation and disposal. +** Users should use these functions in their ObjC programs so +** that they work properly with garbage collectors as well as +** can take advantage of the exception/error handling available. +*/ +void * +objc_malloc(size_t size); + +void * +objc_atomic_malloc(size_t size); + +void * +objc_valloc(size_t size); + +void * +objc_realloc(void *mem, size_t size); + +void * +objc_calloc(size_t nelem, size_t size); + +void +objc_free(void *mem); + +/* +** Hook functions for memory allocation and disposal. +** This makes it easy to substitute garbage collection systems +** such as Boehm's GC by assigning these function pointers +** to the GC's allocation routines. By default these point +** to the ANSI standard malloc, realloc, free, etc. +** +** Users should call the normal objc routines above for +** memory allocation and disposal within their programs. +*/ +objc_EXPORT void *(*_objc_malloc)(size_t); +objc_EXPORT void *(*_objc_atomic_malloc)(size_t); +objc_EXPORT void *(*_objc_valloc)(size_t); +objc_EXPORT void *(*_objc_realloc)(void *, size_t); +objc_EXPORT void *(*_objc_calloc)(size_t, size_t); +objc_EXPORT void (*_objc_free)(void *); + +/* +** Hooks for method forwarding. This makes it easy to substitute a +** library, such as ffcall, that implements closures, thereby avoiding +** gcc's __builtin_apply problems. __objc_msg_forward2's result will +** be preferred over that of __objc_msg_forward if both are set and +** return non-NULL. +*/ +objc_EXPORT IMP (*__objc_msg_forward)(SEL); +objc_EXPORT IMP (*__objc_msg_forward2)(id, SEL); + +/* +** Hook for uncaught exceptions. This hook is called when an exception +** is thrown and no valid exception handler is in place. The function +** is expected never to return. If the function returns the result is +** currently undefined. +*/ +objc_EXPORT void (*_objc_unexpected_exception)(id); + + +Method_t class_get_class_method(MetaClass _class, SEL aSel) OBJC_DEPRECATED; + +Method_t class_get_instance_method(Class _class, SEL aSel) OBJC_DEPRECATED; + +Class class_pose_as(Class impostor, Class superclass) OBJC_DEPRECATED; + +Class objc_get_class(const char *name) OBJC_DEPRECATED; + +Class objc_lookup_class(const char *name) OBJC_DEPRECATED; + +Class objc_next_class(void **enum_state) OBJC_DEPRECATED; + +const char *sel_get_name(SEL selector) OBJC_DEPRECATED; + +const char *sel_get_type(SEL selector) OBJC_DEPRECATED; + +SEL sel_get_uid(const char *name) OBJC_DEPRECATED; + +SEL sel_get_any_uid(const char *name) OBJC_DEPRECATED; + +SEL sel_get_any_typed_uid(const char *name) OBJC_DEPRECATED; + +SEL sel_get_typed_uid(const char *name, const char*) OBJC_DEPRECATED; + +SEL sel_register_name(const char *name) OBJC_DEPRECATED; + +SEL sel_register_typed_name(const char *name, const char*type) OBJC_DEPRECATED; + + +BOOL sel_is_mapped (SEL aSel) OBJC_DEPRECATED; + +extern id class_create_instance(Class _class) OBJC_DEPRECATED; + +static inline const char * +class_get_class_name(Class _class) +{ + return CLS_ISCLASS(_class)?_class->name:((_class==Nil)?"Nil":0); +} + +static inline long +class_get_instance_size(Class _class) +{ + return CLS_ISCLASS(_class)?_class->instance_size:0; +} + +static inline MetaClass +class_get_meta_class(Class _class) +{ + return CLS_ISCLASS(_class)?_class->class_pointer:Nil; +} + +static inline Class +class_get_super_class(Class _class) +{ + return CLS_ISCLASS(_class)?_class->super_class:Nil; +} + +static inline int +class_get_version(Class _class) +{ + return CLS_ISCLASS(_class)?_class->version:-1; +} + +static inline BOOL +class_is_class(Class _class) +{ + return CLS_ISCLASS(_class); +} + +static inline BOOL +class_is_meta_class(Class _class) +{ + return CLS_ISMETA(_class); +} + + +static inline void +class_set_version(Class _class, long version) +{ + if (CLS_ISCLASS(_class)) + _class->version = version; +} + +static inline void * +class_get_gc_object_type (Class _class) +{ + return CLS_ISCLASS(_class) ? _class->gc_object_type : NULL; +} + +/* Mark the instance variable as innaccessible to the garbage collector */ +extern void class_ivar_set_gcinvisible (Class _class, + const char* ivarname, + BOOL gcInvisible); + +static inline IMP +method_get_imp(Method_t method) +{ + return (method!=METHOD_NULL)?method->method_imp:(IMP)0; +} + +IMP get_imp (Class _class, SEL sel); + +/* Redefine on NeXTSTEP so as not to conflict with system function */ +#ifdef __NeXT__ +#define object_copy gnu_object_copy +#define object_dispose gnu_object_dispose +#endif + +id object_copy(id object); + +id object_dispose(id object); + +static inline Class +object_get_class(id object) +{ + return ((object!=nil) + ? (CLS_ISCLASS(object->class_pointer) + ? object->class_pointer + : (CLS_ISMETA(object->class_pointer) + ? (Class)object + : Nil)) + : Nil); +} + +static inline const char * +object_get_class_name(id object) +{ + return ((object!=nil)?(CLS_ISCLASS(object->class_pointer) + ?object->class_pointer->name + :((Class)object)->name) + :"Nil"); +} + +static inline MetaClass +object_get_meta_class(id object) +{ + return ((object!=nil)?(CLS_ISCLASS(object->class_pointer) + ?object->class_pointer->class_pointer + :(CLS_ISMETA(object->class_pointer) + ?object->class_pointer + :Nil)) + :Nil); +} + +static inline Class +object_get_super_class +(id object) +{ + return ((object!=nil)?(CLS_ISCLASS(object->class_pointer) + ?object->class_pointer->super_class + :(CLS_ISMETA(object->class_pointer) + ?((Class)object)->super_class + :Nil)) + :Nil); +} + +static inline BOOL +object_is_class (id object) +{ + return ((object != nil) && CLS_ISMETA (object->class_pointer)); +} + +static inline BOOL +object_is_instance (id object) +{ + return ((object != nil) && CLS_ISCLASS (object->class_pointer)); +} + +static inline BOOL +object_is_meta_class (id object) +{ + return ((object != nil) + && !object_is_instance (object) + && !object_is_class (object)); +} + +struct sarray* +objc_get_uninstalled_dtable(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* not __objc_api_INCLUDE_GNU */ + + + diff --git a/objc/objc-decls.h b/objc/objc-decls.h new file mode 100644 index 0000000..6054237 --- /dev/null +++ b/objc/objc-decls.h @@ -0,0 +1,46 @@ +/* GNU Objective-C Extern helpers for Win32. + Copyright (C) 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __objc_decls_INCLUDE_GNU +#define __objc_decls_INCLUDE_GNU + +#if defined (_WIN32) || defined (__WIN32__) || defined (WIN32) + +# ifdef DLL_EXPORT /* defined by libtool (if required) */ +# define objc_EXPORT __declspec(dllexport) +# define objc_DECLARE __declspec(dllexport) +#else +# define objc_EXPORT extern __declspec(dllimport) +# define objc_DECLARE extern __declspec(dllimport) +#endif + +#else + +# define objc_EXPORT extern +# define objc_DECLARE + +#endif + +#endif /* __objc_decls_INCLUDE_GNU */ diff --git a/objc/objc-list.h b/objc/objc-list.h new file mode 100644 index 0000000..e542bfc --- /dev/null +++ b/objc/objc-list.h @@ -0,0 +1,155 @@ +/* Generic single linked list to keep various information + Copyright (C) 1993, 1994, 1996, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __GNU_OBJC_LIST_H +#define __GNU_OBJC_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct objc_list { + void *head; + struct objc_list *tail; +}; + +/* Return a cons cell produced from (head . tail) */ + +static inline struct objc_list* +list_cons(void* head, struct objc_list* tail) +{ + struct objc_list* cell; + + cell = (struct objc_list*)objc_malloc(sizeof(struct objc_list)); + cell->head = head; + cell->tail = tail; + return cell; +} + +/* Return the length of a list, list_length(NULL) returns zero */ + +static inline int +list_length(struct objc_list* list) +{ + int i = 0; + while(list) + { + i += 1; + list = list->tail; + } + return i; +} + +/* Return the Nth element of LIST, where N count from zero. If N + larger than the list length, NULL is returned */ + +static inline void* +list_nth(int indx, struct objc_list* list) +{ + while(indx-- != 0) + { + if(list->tail) + list = list->tail; + else + return 0; + } + return list->head; +} + +/* Remove the element at the head by replacing it by its successor */ + +static inline void +list_remove_head(struct objc_list** list) +{ + if ((*list)->tail) + { + struct objc_list* tail = (*list)->tail; /* fetch next */ + *(*list) = *tail; /* copy next to list head */ + objc_free(tail); /* free next */ + } + else /* only one element in list */ + { + objc_free(*list); + (*list) = 0; + } +} + + +/* Remove the element with `car' set to ELEMENT */ + +static inline void +list_remove_elem(struct objc_list** list, void* elem) +{ + while (*list) { + if ((*list)->head == elem) + list_remove_head(list); + list = &((*list)->tail); + } +} + +/* Map FUNCTION over all elements in LIST */ + +static inline void +list_mapcar(struct objc_list* list, void(*function)(void*)) +{ + while(list) + { + (*function)(list->head); + list = list->tail; + } +} + +/* Return element that has ELEM as car */ + +static inline struct objc_list** +list_find(struct objc_list** list, void* elem) +{ + while(*list) + { + if ((*list)->head == elem) + return list; + list = &((*list)->tail); + } + return NULL; +} + +/* Free list (backwards recursive) */ + +static inline void +list_free(struct objc_list* list) +{ + if(list) + { + list_free(list->tail); + objc_free(list); + } +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* not __GNU_OBJC_LIST_H */ diff --git a/objc/objc.h b/objc/objc.h new file mode 100644 index 0000000..b4a3a26 --- /dev/null +++ b/objc/objc.h @@ -0,0 +1,209 @@ +/* Basic data types for Objective C. + Copyright (C) 1993, 1995, 1996, 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __objc_INCLUDE_GNU +#define __objc_INCLUDE_GNU + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* +** Definition of the boolean type. +*/ +#ifdef __vxworks +typedef int BOOL; +#else +typedef unsigned char BOOL; +#endif +#define YES (BOOL)1 +#define NO (BOOL)0 + +/* +** Definition of a selector. Selectors themselves are not unique, but +** the sel_id is a unique identifier. +*/ +typedef const struct objc_selector +{ + void *sel_id; + const char *sel_types; +} *SEL; + +inline static BOOL +sel_eq (SEL s1, SEL s2) +{ + if (s1 == 0 || s2 == 0) + return s1 == s2; + else + return s1->sel_id == s2->sel_id; +} + + +/* +** ObjC uses this typedef for untyped instances. +*/ +typedef struct objc_object { + struct objc_class* class_pointer; +} *id; + +/* +** Definition of method type. When retrieving the implementation of a +** method, this is type of the pointer returned. The idea of the +** definition of IMP is to represent a 'pointer to a general function +** taking an id, a SEL, followed by other unspecified arguments'. You +** must always cast an IMP to a pointer to a function taking the +** appropriate, specific types for that function, before calling it - +** to make sure the appropriate arguments are passed to it. The code +** generated by the compiler to perform method calls automatically +** does this cast inside method calls. +*/ +typedef id (*IMP)(id, SEL, ...); + +/* +** More simple types... +*/ +#define nil (id)0 /* id of Nil instance */ +#define Nil (Class)0 /* id of Nil class */ +typedef char *STR; /* String alias */ + +/* +** The compiler generates one of these structures for each class. +** +** This structure is the definition for classes. +** +** This structure is generated by the compiler in the executable and used by +** the run-time during normal messaging operations. Therefore some members +** change type. The compiler generates "char* const" and places a string in +** the following member variables: super_class. +*/ +typedef struct objc_class *MetaClass; +typedef struct objc_class *Class; +struct objc_class { + MetaClass class_pointer; /* Pointer to the class's + meta class. */ + struct objc_class* super_class; /* Pointer to the super + class. NULL for class + Object. */ + const char* name; /* Name of the class. */ + long version; /* Unknown. */ + unsigned long info; /* Bit mask. See class masks + defined above. */ + long instance_size; /* Size in bytes of the class. + The sum of the class + definition and all super + class definitions. */ + struct objc_ivar_list* ivars; /* Pointer to a structure that + describes the instance + variables in the class + definition. NULL indicates + no instance variables. Does + not include super class + variables. */ + struct objc_method_list* methods; /* Linked list of instance + methods defined for the + class. */ + struct sarray * dtable; /* Pointer to instance + method dispatch table. */ + struct objc_class* subclass_list; /* Subclasses */ + struct objc_class* sibling_class; + + struct objc_protocol_list *protocols; /* Protocols conformed to */ + void* gc_object_type; + /** + * New ABI. The following fields are only available with classes compiled to + * support the new ABI. You may test whether any given class supports this + * ABI by using the CLS_ISNEW_ABI() macro. + */ + + /** + * The version of the ABI used for this class. This is currently always zero. + */ + long abi_version; + + /** + * Array of pointers to variables where the runtime will store the ivar + * offset. These may be used for faster access to non-fragile ivars if all + * of the code is compiled for the new ABI. Each of these pointers should + * have the mangled name __objc_ivar_offset_value_{class name}.{ivar name} + * + * When using the compatible non-fragile ABI, this faster form should only be + * used for classes declared in the same compilation unit. + * + * The compiler should also emit symbols of the form + * __objc_ivar_offset_{class name}.{ivar name} which are pointers to the + * offset values. These should be emitted as weak symbols in every module + * where they are used. The legacy-compatible ABI uses these with a double + * layer of indirection. + */ + int **ivar_offsets; + /** + * List of declared properties on this class (NULL if none). This contains + * the accessor methods for each property. + */ + struct objc_property_list *properties; +}; + +#ifndef __OBJC__ +typedef struct objc_protocol { + struct objc_class* class_pointer; + char *protocol_name; + struct objc_protocol_list *protocol_list; + struct objc_method_description_list *instance_methods, *class_methods; +} Protocol; + +struct objc_protocol2 +{ + struct objc_class* class_pointer; + char *protocol_name; + struct objc_protocol_list *protocol_list; + struct objc_method_description_list *instance_methods, *class_methods; + // New ABI + struct objc_method_description_list *optional_instance_methods, *optional_class_methods; + struct objc_property_list *properties, *optional_properties; +}; + + + +#else /* __OBJC__ */ +@class Protocol; +#endif + +typedef void* retval_t; /* return value */ +typedef void(*apply_t)(void); /* function pointer */ +typedef union arglist { + char *arg_ptr; + char arg_regs[sizeof (char*)]; +} *arglist_t; /* argument frame */ + + +IMP objc_msg_lookup(id receiver, SEL op); + +#ifdef __cplusplus +} +#endif + +#endif /* not __objc_INCLUDE_GNU */ diff --git a/objc/runtime-legacy.h b/objc/runtime-legacy.h new file mode 100644 index 0000000..5a0e38f --- /dev/null +++ b/objc/runtime-legacy.h @@ -0,0 +1,97 @@ +/* GNU Objective C Runtime internal declarations + Copyright (C) 1993, 1995, 1996, 1997, 2002, 2004, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __objc_runtime_INCLUDE_GNU +#define __objc_runtime_INCLUDE_GNU + +#include /* for varargs and va_list's */ + +#include +#include + +#include /* so noone else will get system versions */ +#include + +#include "objc.h" /* core data types */ +#include "objc-api.h" /* runtime api functions */ + +#include "thr.h" /* thread and mutex support */ + +#include "hash.h" /* hash structures */ +#include "objc-list.h" /* linear lists */ +#include "Availability.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern void __objc_add_class_to_hash(Class) OBJC_DEPRECATED; /* (objc-class.c) */ +extern void __objc_init_selector_tables(void) OBJC_DEPRECATED; /* (objc-sel.c) */ +extern void __objc_init_class_tables(void) OBJC_DEPRECATED; /* (objc-class.c) */ +extern void __objc_init_dispatch_tables(void) OBJC_DEPRECATED; /* (objc-dispatch.c) */ +extern void __objc_install_premature_dtable(Class) OBJC_DEPRECATED; /* (objc-dispatch.c) */ +extern void __objc_resolve_class_links(void) OBJC_DEPRECATED; /* (objc-class.c) */ +extern void __objc_register_selectors_from_class(Class) OBJC_DEPRECATED; /* (objc-sel.c) */ +extern void __objc_register_selectors_from_list (MethodList_t) OBJC_DEPRECATED; /* (selector.c) */ +extern void __objc_update_dispatch_table_for_class (Class) OBJC_DEPRECATED;/* (objc-msg.c) */ + +extern int __objc_init_thread_system(void) OBJC_DEPRECATED; /* thread.c */ +extern int __objc_fini_thread_system(void) OBJC_DEPRECATED; /* thread.c */ +extern void __objc_print_dtable_stats(void) OBJC_DEPRECATED; /* sendmsg.c */ + +extern void class_add_method_list(Class, MethodList_t) OBJC_DEPRECATED; + +/* Registering instance methods as class methods for root classes */ +extern void __objc_register_instance_methods_to_class(Class) OBJC_DEPRECATED; +extern Method_t search_for_method_in_list(MethodList_t list, SEL op) OBJC_DEPRECATED; + +/* True when class links has been resolved */ +extern BOOL __objc_class_links_resolved OBJC_DEPRECATED; + +/* Number of selectors stored in each of the selector tables */ +extern unsigned int __objc_selector_max_index OBJC_DEPRECATED; + +/* Mutex locking __objc_selector_max_index and its arrays. */ +extern objc_mutex_t __objc_runtime_mutex; + +/* Number of threads which are alive. */ +extern int __objc_runtime_threads_alive; + +#ifdef DEBUG +#define DEBUG_PRINTF(format, args...) printf (format, ## args) +#else +#define DEBUG_PRINTF(format, args...) +#endif + +BOOL __objc_responds_to (id object, SEL sel) OBJC_DEPRECATED; /* for internal use only! */ +SEL __sel_register_typed_name (const char*, const char*, + struct objc_selector*, BOOL is_const) OBJC_DEPRECATED; +extern void __objc_generate_gc_type_description (Class) OBJC_DEPRECATED; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* not __objc_runtime_INCLUDE_GNU */ diff --git a/objc/runtime.h b/objc/runtime.h new file mode 100644 index 0000000..858dae9 --- /dev/null +++ b/objc/runtime.h @@ -0,0 +1,328 @@ +#ifndef __LIBOBJC_RUNTIME_H_INCLUDED__ +#define __LIBOBJC_RUNTIME_H_INCLUDED__ + +// If __LEGACY_GNU_MODE__ is defined then we include the old GNU runtime header +// instead of this one +#ifndef __OBJC_LEGACY_GNU_MODE__ + +#include +#include +#include + +#ifdef ERROR_UNSUPPORTED_RUNTIME_FUNCTIONS +# define OBJC_GNU_RUNTIME_UNSUPPORTED(x) \ + __attribute__((error(x " not supported by the GNU runtime"))) +#else +# define OBJC_GNU_RUNTIME_UNSUPPORTED(x) +#endif + +// Undo GNUstep substitutions +#ifdef class_setVersion +#undef class_setVersion +#endif +#ifdef class_getClassMethod +#undef class_getClassMethod +#endif +#ifdef objc_getClass +#undef objc_getClass +#endif +#ifdef objc_lookUpClass +#undef objc_lookUpClass +#endif + +typedef struct objc_ivar* Ivar; + +// Don't redefine these types if the old GNU header was included first. +#ifndef __objc_INCLUDE_GNU +typedef const struct objc_selector *SEL; + +typedef struct objc_class *Class; + +typedef struct objc_object +{ + Class isa; +} *id; + +struct objc_super { + id receiver; +# if !defined(__cplusplus) && !__OBJC2__ + Class class; +# else + Class super_class; +# endif +}; + +typedef id (*IMP)(id, SEL, ...); +typedef struct objc_method *Method; + +# ifdef STRICT_APPLE_COMPATIBILITY +typedef signed char BOOL; +# else +# ifdef __vxwords +typedef int BOOL +# else +typedef unsigned char BOOL; +# endif +# endif + +#else +// Method in the GNU runtime is a struct, Method_t is the pointer +# define Method Method_t +#endif // __objc_INCLUDE_GNU + + + +typedef void *objc_property_t; +#ifdef __OBJC__ +@class Protocol; +#else +typedef struct objc_protocol Protocol; +#endif + +#ifndef YES +#define YES ((BOOL)1) +#endif +#ifndef NO +#define NO ((BOOL)0) +#endif + +#ifdef __GNUC +#define _OBJC_NULL_PTR __null +#elif defined(__cplusplus) +#define _OBJC_NULL_PTR 0 +#else +#define _OBJC_NULL_PTR ((void*)0) +#endif + +#ifndef nil +#define nil ((id)_OBJC_NULL_PTR) +#endif + +#ifndef Nil +#define Nil ((Class)_OBJC_NULL_PTR) +#endif + +BOOL class_addIvar(Class cls, + const char *name, + size_t size, + uint8_t alignment, + const char *types); + +BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types); + +BOOL class_addProtocol(Class cls, Protocol *protocol); + +BOOL class_conformsToProtocol(Class cls, Protocol *protocol); + +Ivar * class_copyIvarList(Class cls, unsigned int *outCount); + +Method * class_copyMethodList(Class cls, unsigned int *outCount); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Property introspection") +objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount); + +Protocol ** class_copyProtocolList(Class cls, unsigned int *outCount); + +id class_createInstance(Class cls, size_t extraBytes); + +Method class_getClassMethod(Class aClass, SEL aSelector); + +Ivar class_getClassVariable(Class cls, const char* name); + +Method class_getInstanceMethod(Class aClass, SEL aSelector); + +size_t class_getInstanceSize(Class cls); + +Ivar class_getInstanceVariable(Class cls, const char* name); + +const char *class_getIvarLayout(Class cls); + +IMP class_getMethodImplementation(Class cls, SEL name); + +IMP class_getMethodImplementation_stret(Class cls, SEL name); + +const char * class_getName(Class cls); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Property introspection") +objc_property_t class_getProperty(Class cls, const char *name); + +Class class_getSuperclass(Class cls); + +int class_getVersion(Class theClass); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Weak instance variables") +const char *class_getWeakIvarLayout(Class cls); + +BOOL class_isMetaClass(Class cls); + +IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types); + +BOOL class_respondsToSelector(Class cls, SEL sel); + +void class_setIvarLayout(Class cls, const char *layout); + +__attribute__((deprecated)) +Class class_setSuperclass(Class cls, Class newSuper); + +void class_setVersion(Class theClass, int version); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Weak instance variables") +void class_setWeakIvarLayout(Class cls, const char *layout); + +const char * ivar_getName(Ivar ivar); + +ptrdiff_t ivar_getOffset(Ivar ivar); + +const char * ivar_getTypeEncoding(Ivar ivar); + +char * method_copyArgumentType(Method method, unsigned int index); + +char * method_copyReturnType(Method method); + +void method_exchangeImplementations(Method m1, Method m2); + +void method_getArgumentType(Method method, unsigned int index, char *dst, size_t dst_len); + +IMP method_getImplementation(Method method); + +SEL method_getName(Method method); + +unsigned method_getNumberOfArguments(Method method); + +void method_getReturnType(Method method, char *dst, size_t dst_len); + +const char * method_getTypeEncoding(Method method); + +IMP method_setImplementation(Method method, IMP imp); + +Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes); + +void objc_disposeClassPair(Class cls); + +id objc_getClass(const char *name); + +int objc_getClassList(Class *buffer, int bufferLen); + +id objc_getMetaClass(const char *name); + +id objc_getRequiredClass(const char *name); + +id objc_lookUpClass(const char *name); + +Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes); + +Protocol *objc_getProtocol(const char *name); + +void objc_registerClassPair(Class cls); + +void *object_getIndexedIvars(id obj); + +// FIXME: The GNU runtime has a version of this which omits the size parameter +//id object_copy(id obj, size_t size); + +id object_dispose(id obj); + +Class object_getClass(id obj); + +const char *object_getClassName(id obj); + +IMP objc_msg_lookup(id, SEL); +IMP objc_msg_lookup_super(struct objc_super*, SEL); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +BOOL protocol_conformsToProtocol(Protocol *p, Protocol *other); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, + BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +objc_property_t *protocol_copyPropertyList(Protocol *p, unsigned int *count); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *count); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +struct objc_method_description protocol_getMethodDescription(Protocol *p, + SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod); + +const char *protocol_getName(Protocol *p); + +OBJC_GNU_RUNTIME_UNSUPPORTED("Protocol introspection") +objc_property_t protocol_getProperty(Protocol *p, const char *name, + BOOL isRequiredProperty, BOOL isInstanceProperty); + +BOOL protocol_isEqual(Protocol *p, Protocol *other); + +/** + * The objc_slot structure is used to permit safe IMP caching. It is returned + * by the new lookup APIs. When you cache an IMP, you should store a copy of + * the version field and a pointer to the slot. + * + * The slot version is guaranteed never to be 0. When updating a cache, you + * should use code of the following form: + * + * 1) version = 0; + * 2) slot->cachedFor = receiver->isa; + * 3) slot_cache = slot; + * 4) version = slot->version; + * + * The runtime guarantees that the version in any cachable slot will never be + * 0. This should ensure that, if the version and cache pointer mismatch, the + * next access will cause a cache miss. + * + * When using a cached slot, you should compare the owner pointer to the isa + * pointer of the receiver and the message and the version of the slot to your + * cached version. + */ +struct objc_slot +{ + /** The class to which this slot is attached (used internally). */ + Class owner; + /** The class for which this slot was cached. Note that this can be + * modified by different cache owners, in different threads. Doing so may + * cause some cache misses, but if different methods are sending messages + * to the same object and sharing a cached slot then it may also improve + * cache hits. Profiling is probably required here. */ + Class cachedFor; + /** The type encoding for the method identified by this slot. */ + char *types; + /** The current version. This changes if the method changes or if a + * subclass overrides this method, potentially invalidating this cache. */ + int version; + /** The method pointer for this method. */ + IMP method; +}; +/** + * New ABI lookup function. Receiver may be modified during lookup or proxy + * forwarding and the sender may affect how lookup occurs. + */ +struct objc_slot *objc_msg_lookup_sender(id *receiver, SEL selector, id sender); +/** + * Hook provided to allow libraries to support fast proxies. + */ +id (*objc_proxy_lookup)(id receiver, SEL op); +/** + * New message lookup hook. This returns a slot, rather than an IMP. The + * version should be set to 0 to disable caching. + */ +struct objc_slot *(*objc_msg_forward3)(id receiver, SEL op); + +// Global self so that self is a valid symbol everywhere. Will be replaced by +// a real self in an inner scope if there is one. +static const id self = nil; +// This uses a GCC extension, but the is no good way of doing it otherwise. +#define objc_msgSend(theReceiver, theSelector, ...) \ +({\ + id __receiver = theReceiver;\ + SEL op = theSelector;\ + objc_msg_lookup_sender(&__receiver, op, self)(__receiver, op, ## __VA_ARGS__);\ +}) + +#define objc_msgSendSuper(super, op, ...) objc_msg_lookup_super(super, op)(super->receiver, op, ## __VA_ARGS__) + +#else +#include "runtime-legacy.h" +#endif // __LEGACY_GNU_MODE__ + +#endif // __LIBOBJC_RUNTIME_H_INCLUDED__ diff --git a/objc/sarray.h b/objc/sarray.h new file mode 100644 index 0000000..32a7ef2 --- /dev/null +++ b/objc/sarray.h @@ -0,0 +1,243 @@ +/* Sparse Arrays for Objective C dispatch tables + Copyright (C) 1993, 1995, 1996, 2004, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __sarray_INCLUDE_GNU +#define __sarray_INCLUDE_GNU + +#include "thr.h" + +#define OBJC_SPARSE2 /* 2-level sparse array */ +/* #define OBJC_SPARSE3 */ /* 3-level sparse array */ + +#ifdef OBJC_SPARSE2 +extern const char* __objc_sparse2_id; +#endif + +#ifdef OBJC_SPARSE3 +extern const char* __objc_sparse3_id; +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern int nbuckets; /* for stats */ +extern int nindices; +extern int narrays; +extern int idxsize; + +/* An unsigned integer of same size as a pointer */ +#define SIZET_BITS (sizeof(size_t)*8) + +#if defined(__sparc__) || defined(OBJC_SPARSE2) +#define PRECOMPUTE_SELECTORS +#endif + +#ifdef OBJC_SPARSE3 + +/* Buckets are 8 words each */ +#define BUCKET_BITS 3 +#define BUCKET_SIZE (1< + indices[x.off.ioffset]-> + buckets[x.off.boffset]-> + elems[x.off.eoffset]; +#else /* OBJC_SPARSE2 */ + return array->buckets[x.off.boffset]->elems[x.off.eoffset]; +#endif /* OBJC_SPARSE2 */ +#else /* not PRECOMPUTE_SELECTORS */ +#ifdef OBJC_SPARSE3 + return array-> + indices[indx/INDEX_CAPACITY]-> + buckets[(indx/BUCKET_SIZE)%INDEX_SIZE]-> + elems[indx%BUCKET_SIZE]; +#else /* OBJC_SPARSE2 */ + return array->buckets[indx/BUCKET_SIZE]->elems[indx%BUCKET_SIZE]; +#endif /* not OBJC_SPARSE3 */ +#endif /* not PRECOMPUTE_SELECTORS */ +} + +static inline void* sarray_get_safe(struct sarray* array, sidx indx) +{ + if(soffset_decode(indx) < array->capacity) + return sarray_get(array, indx); + else + return (array->empty_bucket->elems[0]); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __sarray_INCLUDE_GNU */ diff --git a/objc/thr.h b/objc/thr.h new file mode 100644 index 0000000..c67c093 --- /dev/null +++ b/objc/thr.h @@ -0,0 +1,61 @@ +/* Thread and mutex controls for Objective C. + Copyright (C) 1996, 1997, 2002, 2004, 2009 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#ifndef __thread_INCLUDE_GNU +#define __thread_INCLUDE_GNU + +#include "objc.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Recursive mutex type used to protect the runtime. */ +typedef void *objc_mutex_t; + +/** + * Global runtime mutex. Acquire this before calling any unsafe runtime + * functions. Note that this is never needed if you restrict yourself to the + * public interfaces, which are strongly recommended. + */ +extern objc_mutex_t __objc_runtime_mutex; +/* Frontend mutex functions */ +int objc_mutex_lock (objc_mutex_t mutex); +int objc_mutex_unlock (objc_mutex_t mutex); + +/** + * Callback for when the runtime becomes multithreaded. As the runtime is now + * always in multithreaded mode, the callback is called as soon as it is + * registered and then discarded. This interface is maintained solely for + * legacy compatibility. + */ +typedef void (*objc_thread_callback) (void); +objc_thread_callback objc_set_thread_callback (objc_thread_callback func); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* not __thread_INCLUDE_GNU */ diff --git a/objc/typedstream.h b/objc/typedstream.h new file mode 100644 index 0000000..0d7643f --- /dev/null +++ b/objc/typedstream.h @@ -0,0 +1,140 @@ +/* GNU Objective-C Typed Streams interface. + Copyright (C) 1993, 1995, 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#ifndef __typedstream_INCLUDE_GNU +#define __typedstream_INCLUDE_GNU + +#include "objc.h" +#include "hash.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef int (*objc_typed_read_func)(void*, char*, int); +typedef int (*objc_typed_write_func)(void*, const char*, int); +typedef int (*objc_typed_flush_func)(void*); +typedef int (*objc_typed_eof_func)(void*); + +#define OBJC_READONLY 0x01 +#define OBJC_WRITEONLY 0x02 + +#define OBJC_MANAGED_STREAM 0x01 +#define OBJC_FILE_STREAM 0x02 +#define OBJC_MEMORY_STREAM 0x04 + +#define OBJC_TYPED_STREAM_VERSION 0x01 + +typedef struct objc_typed_stream { + void* physical; + cache_ptr object_table; /* read/written objects */ + cache_ptr stream_table; /* other read/written but shared things.. */ + cache_ptr class_table; /* class version mapping */ + cache_ptr object_refs; /* forward references */ + int mode; /* OBJC_READONLY or OBJC_WRITEONLY */ + int type; /* MANAGED, FILE, MEMORY etc bit string */ + int version; /* version used when writing */ + int writing_root_p; + objc_typed_read_func read; + objc_typed_write_func write; + objc_typed_eof_func eof; + objc_typed_flush_func flush; +} TypedStream; + +/* opcode masks */ +#define _B_VALUE 0x1fU +#define _B_CODE 0xe0U +#define _B_SIGN 0x10U +#define _B_NUMBER 0x0fU + +/* standard opcodes */ +#define _B_INVALID 0x00U +#define _B_SINT 0x20U +#define _B_NINT 0x40U +#define _B_SSTR 0x60U +#define _B_NSTR 0x80U +#define _B_RCOMM 0xa0U +#define _B_UCOMM 0xc0U +#define _B_EXT 0xe0U + +/* eXtension opcodes */ +#define _BX_OBJECT 0x00U +#define _BX_CLASS 0x01U +#define _BX_SEL 0x02U +#define _BX_OBJREF 0x03U +#define _BX_OBJROOT 0x04U +#define _BX_EXT 0x1fU + +/* +** Read and write objects as specified by TYPE. All the `last' +** arguments are pointers to the objects to read/write. +*/ + +int objc_write_type (TypedStream* stream, const char* type, const void* data); +int objc_read_type (TypedStream* stream, const char* type, void* data); + +int objc_write_types (TypedStream* stream, const char* type, ...); +int objc_read_types (TypedStream* stream, const char* type, ...); + +int objc_write_object_reference (TypedStream* stream, id object); +int objc_write_root_object (TypedStream* stream, id object); + +long objc_get_stream_class_version (TypedStream* stream, Class class_type); + + +/* +** Convenience functions +*/ + +int objc_write_array (TypedStream* stream, const char* type, + int count, const void* data); +int objc_read_array (TypedStream* stream, const char* type, + int count, void* data); + +int objc_write_object (TypedStream* stream, id object); +int objc_read_object (TypedStream* stream, id* object); + + + +/* +** Open a typed stream for reading or writing. MODE may be either of +** OBJC_READONLY or OBJC_WRITEONLY. +*/ + +TypedStream* objc_open_typed_stream (FILE* physical, int mode); +TypedStream* objc_open_typed_stream_for_file (const char* file_name, int mode); + +void objc_close_typed_stream (TypedStream* stream); + +BOOL objc_end_of_typed_stream (TypedStream* stream); +void objc_flush_typed_stream (TypedStream* stream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* not __typedstream_INCLUDE_GNU */ diff --git a/objects.c b/objects.c new file mode 100644 index 0000000..4670d2e --- /dev/null +++ b/objects.c @@ -0,0 +1,101 @@ +/* GNU Objective C Runtime class related functions + Copyright (C) 1993, 1995, 1996, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#include "objc/objc.h" +#include "objc/runtime-legacy.h" /* the kitchen sink */ + +#if OBJC_WITH_GC +# include +#endif + +id __objc_object_alloc (Class); +id __objc_object_dispose (id); +id __objc_object_copy (id); + +id (*_objc_object_alloc) (Class) = __objc_object_alloc; /* !T:SINGLE */ +id (*_objc_object_dispose) (id) = __objc_object_dispose; /* !T:SINGLE */ +id (*_objc_object_copy) (id) = __objc_object_copy; /* !T:SINGLE */ + +id +class_create_instance (Class class) +{ + id new = nil; + +#if OBJC_WITH_GC + if (CLS_ISCLASS (class)) + new = (id) GC_malloc_explicitly_typed (class->instance_size, + class->gc_object_type); +#else + if (CLS_ISCLASS (class)) + new = (*_objc_object_alloc) (class); +#endif + + if (new != nil) + { + memset (new, 0, class->instance_size); + new->class_pointer = class; + } + return new; +} + +id +object_copy (id object) +{ + if ((object != nil) && CLS_ISCLASS (object->class_pointer)) + return (*_objc_object_copy) (object); + else + return nil; +} + +id +object_dispose (id object) +{ + if ((object != nil) && CLS_ISCLASS (object->class_pointer)) + { + if (_objc_object_dispose) + (*_objc_object_dispose) (object); + else + objc_free (object); + } + return nil; +} + +id __objc_object_alloc (Class class) +{ + return (id) objc_malloc (class->instance_size); +} + +id __objc_object_dispose (id object) +{ + objc_free (object); + return 0; +} + +id __objc_object_copy (id object) +{ + id copy = class_create_instance (object->class_pointer); + memcpy (copy, object, object->class_pointer->instance_size); + return copy; +} diff --git a/protocol.c b/protocol.c new file mode 100644 index 0000000..f3a000f --- /dev/null +++ b/protocol.c @@ -0,0 +1,81 @@ +#include +#include + +static cache_ptr Protocols = 0; + +struct objc_method_description_list +{ + int count; + struct objc_method_description list[1]; +}; + +static Class ObjC2ProtocolClass = 0; + +void __objc_init_protocol_table(void) +{ + Protocols = objc_hash_new(128, + (hash_func_type)objc_hash_string, + (compare_func_type)objc_compare_strings); +} + + +static int isEmptyProtocol(struct objc_protocol *aProto) +{ + int isEmpty = (aProto->instance_methods->count == 0) && + (aProto->class_methods->count == 0) && + (aProto->protocol_list->count == 0); + if (aProto->class_pointer == ObjC2ProtocolClass) + { + struct objc_protocol2 *p2 = (struct objc_protocol2*)aProto; + isEmpty &= (p2->optional_instance_methods->count == 0); + isEmpty &= (p2->optional_class_methods->count == 0); + isEmpty &= (p2->properties->property_count == 0); + isEmpty &= (p2->optional_properties->property_count == 0); + } + return isEmpty; +} + +// FIXME: Make p1 adopt all of the stuff in p2 +static void makeProtocolEqualToProtocol(struct objc_protocol *p1, + struct objc_protocol *p2) {} + +void __objc_unique_protocol(struct objc_protocol *aProto) +{ + if (ObjC2ProtocolClass == 0) + { + ObjC2ProtocolClass = objc_get_class("Protocol2"); + } + struct objc_protocol *oldProtocol = + objc_hash_value_for_key(Protocols, aProto->protocol_name); + if (NULL == oldProtocol) + { + // This is the first time we've seen this protocol, so add it to the + // hash table and ignore it. + objc_hash_add(&Protocols, aProto, aProto->protocol_name); + return; + } + if (isEmptyProtocol(oldProtocol)) + { + if (isEmptyProtocol(aProto)) + { + // Add protocol to a list somehow. + } + else + { + // This protocol is not empty, so we use its definitions + makeProtocolEqualToProtocol(oldProtocol, aProto); + } + } + else + { + if (isEmptyProtocol(aProto)) + { + makeProtocolEqualToProtocol(aProto, oldProtocol); + } + else + { + //FIXME: We should really perform a check here to make sure the + //protocols are actually the same. + } + } +} diff --git a/runtime.c b/runtime.c new file mode 100644 index 0000000..9345826 --- /dev/null +++ b/runtime.c @@ -0,0 +1,848 @@ +#include "objc/runtime.h" + +/* Make glibc export strdup() */ + +#if defined __GLIBC__ + #define __USE_BSD 1 +#endif + +#undef __objc_INCLUDE_GNU +#undef __thread_INCLUDE_GNU +#undef __objc_api_INCLUDE_GNU +#undef __encoding_INCLUDE_GNU + +#define objc_object objc_object_gnu +#define id object_ptr_gnu +#define IMP objc_imp_gnu +#define Method objc_method_gnu + +#define object_copy gnu_object_copy +#define object_dispose gnu_object_dispose +#define objc_super gnu_objc_super +#define objc_msg_lookup gnu_objc_msg_lookup +#define objc_msg_lookup_super gnu_objc_msg_lookup_super +#define BOOL GNU_BOOL +#define SEL GNU_SEL +#define Protocol GNU_Protocol +#define Class GNU_Class + +#undef YES +#undef NO +#undef Nil +#undef nil + +#include +#include +#undef GNU_BOOL +#include +#undef Class +#undef Protocol +#undef SEL +#undef objc_msg_lookup +#undef objc_msg_lookup_super +#undef objc_super +#undef Method +#undef IMP +#undef id +#undef objc_object + +// Reinstall definitions. +#undef YES +#undef NO +#undef Nil +#undef nil +#define YES ((BOOL)1) +#define NO ((BOOL)0) +#define nil ((id)_OBJC_NULL_PTR) +#define Nil ((Class)_OBJC_NULL_PTR) + +#include +#include +#include + +/** + * Private runtime function for updating a dtable. + */ +void __objc_update_dispatch_table_for_class(Class); +/** + * Private runtime function for determining whether a class responds to a + * selector. + */ +BOOL __objc_responds_to(Class, SEL); +/** + * Runtime library constant for uninitialized dispatch table. + */ +extern struct sarray *__objc_uninstalled_dtable; +/** + * Mutex used to protect the ObjC runtime library data structures. + */ +extern objc_mutex_t __objc_runtime_mutex; + +/** + * Looks up the instance method in a specific class, without recursing into + * superclasses. + */ +static Method class_getInstanceMethodNonrecursive(Class aClass, SEL aSelector) +{ + const char *name = sel_get_name(aSelector); + const char *types = sel_get_type(aSelector); + + for (struct objc_method_list *methods = aClass->methods; + methods != NULL ; methods = methods->method_next) + { + for (int i=0 ; imethod_count ; i++) + { + Method_t method = &methods->method_list[i]; + if (strcmp(sel_get_name(method->method_name), name) == 0) + { + if (NULL == types || + strcmp(types, method->method_types) == 0) + { + return method; + } + // Return NULL if the method exists with this name but has the + // wrong types + return NULL; + } + } + } + return NULL; +} + +static void objc_updateDtableForClassContainingMethod(Method m) +{ + Class nextClass = Nil; + void *state; + SEL sel = method_getName(m); + while (Nil != (nextClass = objc_next_class(&state))) + { + if (class_getInstanceMethodNonrecursive(nextClass, sel) == m) + { + __objc_update_dispatch_table_for_class(nextClass); + return; + } + } +} + + +BOOL class_addIvar(Class cls, + const char *name, + size_t size, + uint8_t alignment, + const char *types) +{ + if (CLS_ISRESOLV(cls) || CLS_ISMETA(cls)) + { + return NO; + } + + struct objc_ivar_list *ivarlist = cls->ivars; + + if (class_getInstanceVariable(cls, name) != NULL) { return NO; } + + ivarlist->ivar_count++; + // objc_ivar_list contains one ivar. Others follow it. + cls->ivars = objc_realloc(ivarlist, sizeof(struct objc_ivar_list) + + (ivarlist->ivar_count - 1) * sizeof(struct objc_ivar)); + + Ivar ivar = &cls->ivars->ivar_list[cls->ivars->ivar_count - 1]; + ivar->ivar_name = strdup(name); + ivar->ivar_type = strdup(types); + // Round up the offset of the ivar so it is correctly aligned. + ivar->ivar_offset = cls->instance_size + (cls->instance_size % alignment); + // Increase the instance size to make space for this. + cls->instance_size = ivar->ivar_offset + size; + return YES; +} + +BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) +{ + const char *methodName = sel_get_name(name); + struct objc_method_list *methods; + for (methods=cls->methods; methods!=NULL ; methods=methods->method_next) + { + for (int i=0 ; imethod_count ; i++) + { + Method_t method = &methods->method_list[i]; + if (strcmp(sel_get_name(method->method_name), methodName) == 0) + { + return NO; + } + } + } + + methods = objc_malloc(sizeof(struct objc_method_list)); + methods->method_next = cls->methods; + cls->methods = methods; + + methods->method_count = 1; + methods->method_list[0].method_name = sel_get_typed_uid(methodName, types); + methods->method_list[0].method_types = strdup(types); + methods->method_list[0].method_imp = (objc_imp_gnu)imp; + + if (CLS_ISRESOLV(cls)) + { + __objc_update_dispatch_table_for_class(cls); + } + + return YES; +} + +BOOL class_addProtocol(Class cls, Protocol *protocol) +{ + if (class_conformsToProtocol(cls, protocol)) { return NO; } + struct objc_protocol_list *protocols = cls->protocols; + protocols = objc_malloc(sizeof(struct objc_protocol_list)); + if (protocols == NULL) { return NO; } + protocols->next = cls->protocols; + protocols->count = 1; + protocols->list[0] = protocol; + cls->protocols = protocols; + + return YES; +} + +BOOL class_conformsToProtocol(Class cls, Protocol *protocol) +{ + for (struct objc_protocol_list *protocols = cls->protocols; + protocols != NULL ; protocols = protocols->next) + { + for (int i=0 ; icount ; i++) + { + if (strcmp(protocols->list[i]->protocol_name, + protocol->protocol_name) == 0) + { + return YES; + } + } + } + return NO; +} + +Ivar * class_copyIvarList(Class cls, unsigned int *outCount) +{ + struct objc_ivar_list *ivarlist = cls->ivars; + unsigned int count = 0; + if (ivarlist != NULL) + { + count = ivarlist->ivar_count; + } + if (outCount != NULL) + { + *outCount = count; + } + if (count == 0) + { + return NULL; + } + + Ivar *list = malloc(count * sizeof(struct objc_ivar *)); + for (unsigned int i=0; iivar_count; i++) + { + list[i] = &(ivarlist->ivar_list[i]); + } + return list; +} + +Method * class_copyMethodList(Class cls, unsigned int *outCount) +{ + unsigned int count = 0; + for (struct objc_method_list *methods = cls->methods; + methods != NULL ; methods = methods->method_next) + { + count += methods->method_count; + } + if (outCount != NULL) + { + *outCount = count; + } + if (count == 0) + { + return NULL; + } + + Method *list = malloc(count * sizeof(struct objc_method *)); + Method *copyDest = list; + + for (struct objc_method_list *methods = cls->methods; + methods != NULL ; methods = methods->method_next) + { + for (unsigned int i=0; imethod_count; i++) + { + copyDest[i] = &(methods->method_list[i]); + } + copyDest += methods->method_count; + } + + return list; +} + +Protocol ** class_copyProtocolList(Class cls, unsigned int *outCount) +{ + struct objc_protocol_list *protocolList = cls->protocols; + int listSize = 0; + for (struct objc_protocol_list *list = protocolList ; + list != NULL ; + list = list->next) + { + listSize += list->count; + } + if (listSize == 0) + { + *outCount = 0; + return NULL; + } + + Protocol **protocols = calloc(listSize, sizeof(Protocol*) + 1); + int index = 0; + for (struct objc_protocol_list *list = protocolList ; + list != NULL ; + list = list->next) + { + memcpy(&protocols[index], list->list, list->count * sizeof(Protocol*)); + index += list->count; + } + protocols[listSize] = NULL; + *outCount = listSize + 1; + return protocols; +} + +id class_createInstance(Class cls, size_t extraBytes) +{ + id obj = objc_malloc(cls->instance_size + extraBytes); + obj->isa = cls; + return obj; +} + +Method class_getInstanceMethod(Class aClass, SEL aSelector) +{ + Method method = class_getInstanceMethodNonrecursive(aClass, aSelector); + if (method == NULL) + { + // TODO: Check if this should be NULL or aClass + Class superclass = class_getSuperclass(aClass); + if (superclass == NULL) + { + return NULL; + } + return class_getInstanceMethod(superclass, aSelector); + } + return method; +} + +Method class_getClassMethod(Class aClass, SEL aSelector) +{ + return class_getInstanceMethod(aClass->class_pointer, aSelector); +} + +Ivar class_getClassVariable(Class cls, const char* name) +{ + assert(0 && "Class variables not implemented"); + return NULL; +} + +size_t class_getInstanceSize(Class cls) +{ + return class_get_instance_size(cls); +} + +Ivar class_getInstanceVariable(Class cls, const char* name) +{ + struct objc_ivar_list *ivarlist = cls->ivars; + + for (int i=0 ; iivar_count ; i++) + { + Ivar ivar = &ivarlist->ivar_list[i]; + if (strcmp(ivar->ivar_name, name) == 0) + { + return ivar; + } + } + return NULL; +} + +// The format of the char* is undocumented. This function is only ever used in +// conjunction with class_setIvarLayout(). +const char *class_getIvarLayout(Class cls) +{ + return (char*)cls->ivars; +} + +IMP class_getMethodImplementation(Class cls, SEL name) +{ + struct objc_object_gnu obj = { cls }; + return (IMP)objc_msg_lookup((id)&obj, name); +} + +IMP class_getMethodImplementation_stret(Class cls, SEL name) +{ + struct objc_object_gnu obj = { cls }; + return (IMP)objc_msg_lookup((id)&obj, name); +} + +const char * class_getName(Class cls) +{ + return class_get_class_name(cls); +} + +Class class_getSuperclass(Class cls) +{ + return class_get_super_class(cls); +} + +int class_getVersion(Class theClass) +{ + return class_get_version(theClass); +} + +const char *class_getWeakIvarLayout(Class cls) +{ + assert(0 && "Weak ivars not supported"); + return NULL; +} + +BOOL class_isMetaClass(Class cls) +{ + return CLS_ISMETA(cls); +} + +IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) +{ + Method method = class_getInstanceMethodNonrecursive(cls, name); + if (method == NULL) + { + class_addMethod(cls, name, imp, types); + return NULL; + } + IMP old = (IMP)method->method_imp; + method->method_imp = (objc_imp_gnu)imp; + return old; +} + + +BOOL class_respondsToSelector(Class cls, SEL sel) +{ + return __objc_responds_to(cls, sel); +} + +void class_setIvarLayout(Class cls, const char *layout) +{ + struct objc_ivar_list *list = (struct objc_ivar_list*)layout; + size_t listsize = sizeof(struct objc_ivar_list) + + sizeof(struct objc_ivar) * (list->ivar_count - 1); + cls->ivars = malloc(listsize); + memcpy(cls->ivars, list, listsize); +} + +__attribute__((deprecated)) +Class class_setSuperclass(Class cls, Class newSuper) +{ + Class oldSuper = cls->super_class; + cls->super_class = newSuper; + return oldSuper; +} + +void class_setVersion(Class theClass, int version) +{ + class_set_version(theClass, version); +} + +void class_setWeakIvarLayout(Class cls, const char *layout) +{ + assert(0 && "Not implemented"); +} + +const char * ivar_getName(Ivar ivar) +{ + return ivar->ivar_name; +} + +ptrdiff_t ivar_getOffset(Ivar ivar) +{ + return ivar->ivar_offset; +} + +const char * ivar_getTypeEncoding(Ivar ivar) +{ + return ivar->ivar_type; +} + +static size_t lengthOfTypeEncoding(const char *types) +{ + const char *end = objc_skip_argspec(types); + end--; + while (isdigit(*end)) { end--; } + size_t length = end - types + 1; + return length; +} +static char *copyTypeEncoding(const char *types) +{ + size_t length = lengthOfTypeEncoding(types); + char *copy = malloc(length + 1); + memcpy(copy, types, length); + copy[length] = '\0'; + return copy; +} +static const char * findParameterStart(const char *types, unsigned int index) +{ + for (unsigned int i=0 ; imethod_types, index); + if (NULL == types) + { + return NULL; + } + return copyTypeEncoding(types); +} + +char * method_copyReturnType(Method method) +{ + return copyTypeEncoding(method->method_types); +} + +void method_exchangeImplementations(Method m1, Method m2) +{ + IMP tmp = (IMP)m1->method_imp; + m1->method_imp = m2->method_imp; + m2->method_imp = (objc_imp_gnu)tmp; + objc_updateDtableForClassContainingMethod(m1); + objc_updateDtableForClassContainingMethod(m2); +} +void method_getArgumentType(Method method, + unsigned int index, + char *dst, + size_t dst_len) +{ + const char *types = findParameterStart(method->method_types, index); + if (NULL == types) + { + strncpy(dst, "", dst_len); + return; + } + size_t length = lengthOfTypeEncoding(types); + if (length < dst_len) + { + memcpy(dst, types, length); + dst[length] = '\0'; + } + else + { + memcpy(dst, types, dst_len); + } +} + +IMP method_getImplementation(Method method) +{ + return (IMP)method->method_imp; +} + +SEL method_getName(Method method) +{ + return method->method_name; +} + +unsigned method_getNumberOfArguments(Method method) +{ + const char *types = method->method_types; + unsigned int count = 0; + while('\0' == *types) + { + types = objc_skip_argspec(types); + count++; + } + return count - 1; +} + +void method_getReturnType(Method method, char *dst, size_t dst_len) +{ + //TODO: Coped and pasted code. Factor it out. + const char *types = method->method_types; + size_t length = lengthOfTypeEncoding(types); + if (length < dst_len) + { + memcpy(dst, types, length); + dst[length] = '\0'; + } + else + { + memcpy(dst, types, dst_len); + } +} + +const char * method_getTypeEncoding(Method method) +{ + return method->method_types; +} + +IMP method_setImplementation(Method method, IMP imp) +{ + IMP old = (IMP)method->method_imp; + method->method_imp = (objc_imp_gnu)old; + objc_updateDtableForClassContainingMethod(method); + return old; +} + +id objc_getClass(const char *name) +{ + return (id)objc_get_class(name); +} + +int objc_getClassList(Class *buffer, int bufferLen) +{ + int count = 0; + if (buffer == NULL) + { + void *state = NULL; + while(Nil != objc_next_class(&state)) + { + count++; + } + } + else + { + Class nextClass; + void *state = NULL; + while (Nil != (nextClass = objc_next_class(&state)) && bufferLen > 0) + { + count++; + bufferLen--; + *(buffer++) = nextClass; + } + } + return count; +} + +id objc_getMetaClass(const char *name) +{ + Class cls = (Class)objc_getClass(name); + return cls == Nil ? nil : (id)cls->class_pointer; +} + +id objc_getRequiredClass(const char *name) +{ + id cls = objc_getClass(name); + if (nil == cls) + { + abort(); + } + return cls; +} + +id objc_lookUpClass(const char *name) +{ + // TODO: Check these are the right way around. + return (id)objc_lookup_class(name); +} + +static void freeMethodLists(Class aClass) +{ + struct objc_method_list *methods = aClass->methods; + while(methods != NULL) + { + for (int i=0 ; imethod_count ; i++) + { + free((void*)methods->method_list[i].method_types); + } + struct objc_method_list *current = methods; + methods = methods->method_next; + free(current); + } +} + +static void freeIvarLists(Class aClass) +{ + struct objc_ivar_list *ivarlist = aClass->ivars; + if (NULL == ivarlist) { return; } + + for (int i=0 ; iivar_count ; i++) + { + Ivar ivar = &ivarlist->ivar_list[i]; + free((void*)ivar->ivar_type); + free((void*)ivar->ivar_name); + } + free(ivarlist); +} + +/* + * Removes a class from the subclass list found on its super class. + * Must be called with the objc runtime mutex locked. + */ +static inline void safe_remove_from_subclass_list(Class cls) +{ + Class sub = cls->super_class->subclass_list; + if (sub == cls) + { + cls->super_class->subclass_list = cls->sibling_class; + } + else + { + while (sub != NULL) + { + if (sub->sibling_class == cls) + { + sub->sibling_class = cls->sibling_class; + break; + } + sub = sub->sibling_class; + } + } +} + +void objc_disposeClassPair(Class cls) +{ + Class meta = ((id)cls)->isa; + // Remove from the runtime system so nothing tries updating the dtable + // while we are freeing the class. + objc_mutex_lock(__objc_runtime_mutex); + safe_remove_from_subclass_list(meta); + safe_remove_from_subclass_list(cls); + objc_mutex_unlock(__objc_runtime_mutex); + + // Free the method and ivar lists. + freeMethodLists(cls); + freeMethodLists(meta); + freeIvarLists(cls); + + // Free the class and metaclass + free(meta); + free(cls); +} + +Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes) +{ + // Check the class doesn't already exist. + if (nil != objc_lookUpClass(name)) { return Nil; } + + Class newClass = calloc(1, sizeof(struct objc_class) + extraBytes); + + if (Nil == newClass) { return Nil; } + + // Create the metaclass + Class metaClass = calloc(1, sizeof(struct objc_class)); + + // Initialize the metaclass + metaClass->class_pointer = superclass->class_pointer; + metaClass->super_class = superclass->class_pointer->super_class; + metaClass->info = _CLS_META | _CLS_RUNTIME | _CLS_NEW_ABI; + metaClass->dtable = __objc_uninstalled_dtable; + + // Set up the new class + newClass->class_pointer = metaClass; + newClass->super_class = superclass; + newClass->name = strdup(name); + newClass->info = _CLS_CLASS | _CLS_RUNTIME | _CLS_NEW_ABI; + newClass->dtable = __objc_uninstalled_dtable; + + return newClass; +} + +void *object_getIndexedIvars(id obj) +{ + if (class_isMetaClass(obj->isa)) + { + return ((char*)obj) + sizeof(struct objc_class); + } + return ((char*)obj) + obj->isa->instance_size; +} + +Class object_getClass(id obj) +{ + if (nil != obj) + { + return obj->isa; + } + return Nil; +} + +const char *object_getClassName(id obj) +{ + return class_getName(object_getClass(obj)); +} + +void objc_registerClassPair(Class cls) +{ + Class metaClass = cls->class_pointer; + // Initialize the dispatch table for the class and metaclass. + __objc_update_dispatch_table_for_class(metaClass); + __objc_update_dispatch_table_for_class(cls); + CLS_SETINITIALIZED(metaClass); + CLS_SETINITIALIZED(cls); + // Add pointer from super class + objc_mutex_lock(__objc_runtime_mutex); + cls->sibling_class = cls->super_class->subclass_list; + cls->super_class->subclass_list = cls; + metaClass->sibling_class = metaClass->super_class->subclass_list; + metaClass->super_class->subclass_list = metaClass; + objc_mutex_unlock(__objc_runtime_mutex); +} + +static id objectNew(id cls) +{ + static SEL newSel = NULL; + if (NULL == newSel) + { + newSel = sel_get_uid("new"); + } + IMP newIMP = (IMP)objc_msg_lookup((void*)cls, newSel); + return newIMP((id)cls, newSel); +} + +Protocol *objc_getProtocol(const char *name) +{ + // Protocols are not centrally registered in the GNU runtime. + Protocol *protocol = (Protocol*)(objectNew(objc_getClass("Protocol"))); + protocol->protocol_name = (char*)name; + return protocol; +} + +BOOL protocol_conformsToProtocol(Protocol *p, Protocol *other) +{ + return NO; +} + +struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, + BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count) +{ + *count = 0; + return NULL; +} + +Protocol **protocol_copyProtocolList(Protocol *p, unsigned int *count) +{ + *count = 0; + return NULL; +} + +const char *protocol_getName(Protocol *p) +{ + if (NULL != p) + { + return p->protocol_name; + } + return NULL; +} + +BOOL protocol_isEqual(Protocol *p, Protocol *other) +{ + if (NULL == p || NULL == other) + { + return NO; + } + if (p == other || + 0 == strcmp(p->protocol_name, other->protocol_name)) + { + return YES; + } + return NO; +} diff --git a/sarray.c b/sarray.c new file mode 100644 index 0000000..f61a723 --- /dev/null +++ b/sarray.c @@ -0,0 +1,517 @@ +/* Sparse Arrays for Objective C dispatch tables + Copyright (C) 1993, 1995, 1996, 2002, 2004, 2009 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#include "objc/sarray.h" +#include "objc/runtime-legacy.h" +#include +#include "assert.h" + +int nbuckets = 0; /* !T:MUTEX */ +int nindices = 0; /* !T:MUTEX */ +int narrays = 0; /* !T:MUTEX */ +int idxsize = 0; /* !T:MUTEX */ + +static void *first_free_data = NULL; /* !T:MUTEX */ + +#ifdef OBJC_SPARSE2 +const char *__objc_sparse2_id = "2 level sparse indices"; +#endif + +#ifdef OBJC_SPARSE3 +const char *__objc_sparse3_id = "3 level sparse indices"; +#endif + +/* This function removes any structures left over from free operations + that were not safe in a multi-threaded environment. */ +void +sarray_remove_garbage (void) +{ + void **vp; + void *np; + + objc_mutex_lock (__objc_runtime_mutex); + + vp = first_free_data; + first_free_data = NULL; + + while (vp) { + np = *vp; + objc_free (vp); + vp = np; + } + + objc_mutex_unlock (__objc_runtime_mutex); +} + +/* Free a block of dynamically allocated memory. If we are in multi-threaded + mode, it is ok to free it. If not, we add it to the garbage heap to be + freed later. */ + +static void +sarray_free_garbage (void *vp) +{ + objc_mutex_lock (__objc_runtime_mutex); + + if (__objc_runtime_threads_alive == 1) { + objc_free (vp); + if (first_free_data) + sarray_remove_garbage (); + } + else { + *(void **)vp = first_free_data; + first_free_data = vp; + } + + objc_mutex_unlock (__objc_runtime_mutex); +} + +/* sarray_at_put : copies data in such a way as to be thread reader safe. */ +void +sarray_at_put (struct sarray *array, sidx index, void *element) +{ +#ifdef OBJC_SPARSE3 + struct sindex **the_index; + struct sindex *new_index; +#endif + struct sbucket **the_bucket; + struct sbucket *new_bucket; +#ifdef OBJC_SPARSE3 + size_t ioffset; +#endif + size_t boffset; + size_t eoffset; +#ifdef PRECOMPUTE_SELECTORS + union sofftype xx; + xx.idx = index; +#ifdef OBJC_SPARSE3 + ioffset = xx.off.ioffset; +#endif + boffset = xx.off.boffset; + eoffset = xx.off.eoffset; +#else /* not PRECOMPUTE_SELECTORS */ +#ifdef OBJC_SPARSE3 + ioffset = index/INDEX_CAPACITY; + boffset = (index/BUCKET_SIZE)%INDEX_SIZE; + eoffset = index%BUCKET_SIZE; +#else + boffset = index/BUCKET_SIZE; + eoffset = index%BUCKET_SIZE; +#endif +#endif /* not PRECOMPUTE_SELECTORS */ + + assert (soffset_decode (index) < array->capacity); /* Range check */ + +#ifdef OBJC_SPARSE3 + the_index = &(array->indices[ioffset]); + the_bucket = &((*the_index)->buckets[boffset]); +#else + the_bucket = &(array->buckets[boffset]); +#endif + + if ((*the_bucket)->elems[eoffset] == element) + return; /* great! we just avoided a lazy copy */ + +#ifdef OBJC_SPARSE3 + + /* First, perform lazy copy/allocation of index if needed */ + + if ((*the_index) == array->empty_index) { + + /* The index was previously empty, allocate a new */ + new_index = (struct sindex *) objc_malloc (sizeof (struct sindex)); + memcpy (new_index, array->empty_index, sizeof (struct sindex)); + new_index->version.version = array->version.version; + *the_index = new_index; /* Prepared for install. */ + the_bucket = &((*the_index)->buckets[boffset]); + + nindices += 1; + } else if ((*the_index)->version.version != array->version.version) { + + /* This index must be lazy copied */ + struct sindex *old_index = *the_index; + new_index = (struct sindex *) objc_malloc (sizeof (struct sindex)); + memcpy (new_index, old_index, sizeof (struct sindex)); + new_index->version.version = array->version.version; + *the_index = new_index; /* Prepared for install. */ + the_bucket = &((*the_index)->buckets[boffset]); + + nindices += 1; + } + +#endif /* OBJC_SPARSE3 */ + + /* next, perform lazy allocation/copy of the bucket if needed */ + + if ((*the_bucket) == array->empty_bucket) { + + /* The bucket was previously empty (or something like that), */ + /* allocate a new. This is the effect of `lazy' allocation */ + new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket)); + memcpy ((void *) new_bucket, (const void *) array->empty_bucket, + sizeof (struct sbucket)); + new_bucket->version.version = array->version.version; + *the_bucket = new_bucket; /* Prepared for install. */ + + nbuckets += 1; + + } else if ((*the_bucket)->version.version != array->version.version) { + + /* Perform lazy copy. */ + struct sbucket *old_bucket = *the_bucket; + new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket)); + memcpy (new_bucket, old_bucket, sizeof (struct sbucket)); + new_bucket->version.version = array->version.version; + *the_bucket = new_bucket; /* Prepared for install. */ + + nbuckets += 1; + + } + (*the_bucket)->elems[eoffset] = element; +} + +void +sarray_at_put_safe (struct sarray *array, sidx index, void *element) +{ + if (soffset_decode (index) >= array->capacity) + sarray_realloc (array, soffset_decode (index) + 1); + sarray_at_put (array, index, element); +} + +struct sarray * +sarray_new (int size, void *default_element) +{ + struct sarray *arr; +#ifdef OBJC_SPARSE3 + size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1; + struct sindex **new_indices; +#else /* OBJC_SPARSE2 */ + size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1; + struct sbucket **new_buckets; +#endif + size_t counter; + + assert (size > 0); + + /* Allocate core array */ + arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); + arr->version.version = 0; + + /* Initialize members */ +#ifdef OBJC_SPARSE3 + arr->capacity = num_indices*INDEX_CAPACITY; + new_indices = (struct sindex **) + objc_malloc (sizeof (struct sindex *) * num_indices); + + arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex)); + arr->empty_index->version.version = 0; + + narrays += 1; + idxsize += num_indices; + nindices += 1; + +#else /* OBJC_SPARSE2 */ + arr->capacity = num_indices*BUCKET_SIZE; + new_buckets = (struct sbucket **) + objc_malloc (sizeof (struct sbucket *) * num_indices); + + narrays += 1; + idxsize += num_indices; + +#endif + + arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket)); + arr->empty_bucket->version.version = 0; + + nbuckets += 1; + + arr->ref_count = 1; + arr->is_copy_of = (struct sarray *) 0; + + for (counter = 0; counter < BUCKET_SIZE; counter++) + arr->empty_bucket->elems[counter] = default_element; + +#ifdef OBJC_SPARSE3 + for (counter = 0; counter < INDEX_SIZE; counter++) + arr->empty_index->buckets[counter] = arr->empty_bucket; + + for (counter = 0; counter < num_indices; counter++) + new_indices[counter] = arr->empty_index; + +#else /* OBJC_SPARSE2 */ + + for (counter = 0; counter < num_indices; counter++) + new_buckets[counter] = arr->empty_bucket; + +#endif + +#ifdef OBJC_SPARSE3 + arr->indices = new_indices; +#else /* OBJC_SPARSE2 */ + arr->buckets = new_buckets; +#endif + + return arr; +} + + +/* Reallocate the sparse array to hold `newsize' entries + Note: We really allocate and then free. We have to do this to ensure that + any concurrent readers notice the update. */ + +void +sarray_realloc (struct sarray *array, int newsize) +{ +#ifdef OBJC_SPARSE3 + size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY; + size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY); + size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY; + + struct sindex **new_indices; + struct sindex **old_indices; + +#else /* OBJC_SPARSE2 */ + size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE; + size_t new_max_index = ((newsize - 1)/BUCKET_SIZE); + size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE; + + struct sbucket **new_buckets; + struct sbucket **old_buckets; + +#endif + + size_t counter; + + assert (newsize > 0); + + /* The size is the same, just ignore the request */ + if (rounded_size <= array->capacity) + return; + + assert (array->ref_count == 1); /* stop if lazy copied... */ + + /* We are asked to extend the array -- allocate new bucket table, */ + /* and insert empty_bucket in newly allocated places. */ + if (rounded_size > array->capacity) + { + +#ifdef OBJC_SPARSE3 + new_max_index += 4; + rounded_size = (new_max_index + 1) * INDEX_CAPACITY; + +#else /* OBJC_SPARSE2 */ + new_max_index += 4; + rounded_size = (new_max_index + 1) * BUCKET_SIZE; +#endif + + /* update capacity */ + array->capacity = rounded_size; + +#ifdef OBJC_SPARSE3 + /* alloc to force re-read by any concurrent readers. */ + old_indices = array->indices; + new_indices = (struct sindex **) + objc_malloc ((new_max_index + 1) * sizeof (struct sindex *)); +#else /* OBJC_SPARSE2 */ + old_buckets = array->buckets; + new_buckets = (struct sbucket **) + objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *)); +#endif + + /* copy buckets below old_max_index (they are still valid) */ + for (counter = 0; counter <= old_max_index; counter++ ) { +#ifdef OBJC_SPARSE3 + new_indices[counter] = old_indices[counter]; +#else /* OBJC_SPARSE2 */ + new_buckets[counter] = old_buckets[counter]; +#endif + } + +#ifdef OBJC_SPARSE3 + /* reset entries above old_max_index to empty_bucket */ + for (counter = old_max_index + 1; counter <= new_max_index; counter++) + new_indices[counter] = array->empty_index; +#else /* OBJC_SPARSE2 */ + /* reset entries above old_max_index to empty_bucket */ + for (counter = old_max_index + 1; counter <= new_max_index; counter++) + new_buckets[counter] = array->empty_bucket; +#endif + +#ifdef OBJC_SPARSE3 + /* install the new indices */ + array->indices = new_indices; +#else /* OBJC_SPARSE2 */ + array->buckets = new_buckets; +#endif + +#ifdef OBJC_SPARSE3 + /* free the old indices */ + sarray_free_garbage (old_indices); +#else /* OBJC_SPARSE2 */ + sarray_free_garbage (old_buckets); +#endif + + idxsize += (new_max_index-old_max_index); + return; + } +} + + +/* Free a sparse array allocated with sarray_new */ + +void +sarray_free (struct sarray *array) { +#ifdef OBJC_SPARSE3 + size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY; + struct sindex **old_indices; +#else + size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE; + struct sbucket **old_buckets; +#endif + size_t counter = 0; + + assert (array->ref_count != 0); /* Freed multiple times!!! */ + + if (--(array->ref_count) != 0) /* There exists copies of me */ + return; + +#ifdef OBJC_SPARSE3 + old_indices = array->indices; +#else + old_buckets = array->buckets; +#endif + + /* Free all entries that do not point to empty_bucket */ + for (counter = 0; counter <= old_max_index; counter++ ) { +#ifdef OBJC_SPARSE3 + struct sindex *idx = old_indices[counter]; + if ((idx != array->empty_index) && + (idx->version.version == array->version.version)) { + int c2; + for (c2 = 0; c2 < INDEX_SIZE; c2++) { + struct sbucket *bkt = idx->buckets[c2]; + if ((bkt != array->empty_bucket) && + (bkt->version.version == array->version.version)) + { + sarray_free_garbage (bkt); + nbuckets -= 1; + } + } + sarray_free_garbage (idx); + nindices -= 1; + } +#else /* OBJC_SPARSE2 */ + struct sbucket *bkt = array->buckets[counter]; + if ((bkt != array->empty_bucket) && + (bkt->version.version == array->version.version)) + { + sarray_free_garbage (bkt); + nbuckets -= 1; + } +#endif + } + +#ifdef OBJC_SPARSE3 + /* free empty_index */ + if (array->empty_index->version.version == array->version.version) { + sarray_free_garbage (array->empty_index); + nindices -= 1; + } +#endif + + /* free empty_bucket */ + if (array->empty_bucket->version.version == array->version.version) { + sarray_free_garbage (array->empty_bucket); + nbuckets -= 1; + } + idxsize -= (old_max_index + 1); + narrays -= 1; + +#ifdef OBJC_SPARSE3 + /* free bucket table */ + sarray_free_garbage (array->indices); + +#else + /* free bucket table */ + sarray_free_garbage (array->buckets); + +#endif + + /* If this is a copy of another array, we free it (which might just + * decrement its reference count so it will be freed when no longer in use). + */ + if (array->is_copy_of) + sarray_free (array->is_copy_of); + + /* free array */ + sarray_free_garbage (array); +} + +/* This is a lazy copy. Only the core of the structure is actually */ +/* copied. */ + +struct sarray * +sarray_lazy_copy (struct sarray *oarr) +{ + struct sarray *arr; + +#ifdef OBJC_SPARSE3 + size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1; + struct sindex **new_indices; +#else /* OBJC_SPARSE2 */ + size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1; + struct sbucket **new_buckets; +#endif + + /* Allocate core array */ + arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */ + arr->version.version = oarr->version.version + 1; +#ifdef OBJC_SPARSE3 + arr->empty_index = oarr->empty_index; +#endif + arr->empty_bucket = oarr->empty_bucket; + arr->ref_count = 1; + oarr->ref_count += 1; + arr->is_copy_of = oarr; + arr->capacity = oarr->capacity; + +#ifdef OBJC_SPARSE3 + /* Copy bucket table */ + new_indices = (struct sindex **) + objc_malloc (sizeof (struct sindex *) * num_indices); + memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices); + arr->indices = new_indices; +#else + /* Copy bucket table */ + new_buckets = (struct sbucket **) + objc_malloc (sizeof (struct sbucket *) * num_indices); + memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices); + arr->buckets = new_buckets; +#endif + + idxsize += num_indices; + narrays += 1; + + return arr; +} diff --git a/selector.c b/selector.c new file mode 100644 index 0000000..2cec38a --- /dev/null +++ b/selector.c @@ -0,0 +1,492 @@ +/* GNU Objective C Runtime selector related functions + Copyright (C) 1993, 1995, 1996, 1997, 2002, 2004, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#include "objc/runtime-legacy.h" +#include "objc/sarray.h" +#include "objc/encoding.h" + +/* Initial selector hash table size. Value doesn't matter much */ +#define SELECTOR_HASH_SIZE 128 + +/* Tables mapping selector names to uid and opposite */ +static struct sarray *__objc_selector_array = 0; /* uid -> sel !T:MUTEX */ +static struct sarray *__objc_selector_names = 0; /* uid -> name !T:MUTEX */ +static cache_ptr __objc_selector_hash = 0; /* name -> uid !T:MUTEX */ + +/* Number of selectors stored in each of the above tables */ +unsigned int __objc_selector_max_index = 0; /* !T:MUTEX */ + +void __objc_init_selector_tables (void) +{ + __objc_selector_array = sarray_new (SELECTOR_HASH_SIZE, 0); + __objc_selector_names = sarray_new (SELECTOR_HASH_SIZE, 0); + __objc_selector_hash + = objc_hash_new (SELECTOR_HASH_SIZE, + (hash_func_type) objc_hash_string, + (compare_func_type) objc_compare_strings); +} + +/* This routine is given a class and records all of the methods in its class + structure in the record table. */ +void +__objc_register_selectors_from_class (Class class) +{ + MethodList_t method_list; + + method_list = class->methods; + while (method_list) + { + __objc_register_selectors_from_list (method_list); + method_list = method_list->method_next; + } +} + + +/* This routine is given a list of methods and records each of the methods in + the record table. This is the routine that does the actual recording + work. + + The name and type pointers in the method list must be permanent and + immutable. + */ +void +__objc_register_selectors_from_list (MethodList_t method_list) +{ + int i = 0; + + objc_mutex_lock (__objc_runtime_mutex); + while (i < method_list->method_count) + { + Method_t method = &method_list->method_list[i]; + if (method->method_name) + { + method->method_name + = __sel_register_typed_name ((const char *) method->method_name, + method->method_types, 0, YES); + } + i += 1; + } + objc_mutex_unlock (__objc_runtime_mutex); +} + + +/* Register instance methods as class methods for root classes */ +void __objc_register_instance_methods_to_class (Class class) +{ + MethodList_t method_list; + MethodList_t class_method_list; + int max_methods_no = 16; + MethodList_t new_list; + Method_t curr_method; + + /* Only if a root class. */ + if (class->super_class) + return; + + /* Allocate a method list to hold the new class methods */ + new_list = objc_calloc (sizeof (struct objc_method_list) + + sizeof (struct objc_method[max_methods_no]), 1); + method_list = class->methods; + class_method_list = class->class_pointer->methods; + curr_method = &new_list->method_list[0]; + + /* Iterate through the method lists for the class */ + while (method_list) + { + int i; + + /* Iterate through the methods from this method list */ + for (i = 0; i < method_list->method_count; i++) + { + Method_t mth = &method_list->method_list[i]; + if (mth->method_name + && ! search_for_method_in_list (class_method_list, + mth->method_name)) + { + /* This instance method isn't a class method. + Add it into the new_list. */ + *curr_method = *mth; + + /* Reallocate the method list if necessary */ + if (++new_list->method_count == max_methods_no) + new_list = + objc_realloc (new_list, sizeof (struct objc_method_list) + + sizeof (struct + objc_method[max_methods_no += 16])); + curr_method = &new_list->method_list[new_list->method_count]; + } + } + + method_list = method_list->method_next; + } + + /* If we created any new class methods + then attach the method list to the class */ + if (new_list->method_count) + { + new_list = + objc_realloc (new_list, sizeof (struct objc_method_list) + + sizeof (struct objc_method[new_list->method_count])); + new_list->method_next = class->class_pointer->methods; + class->class_pointer->methods = new_list; + } + else + objc_free(new_list); + + __objc_update_dispatch_table_for_class (class->class_pointer); +} + + +/* Returns YES iff t1 and t2 have same method types, but we ignore + the argframe layout */ +BOOL +sel_types_match (const char *t1, const char *t2) +{ + if (! t1 || ! t2) + return NO; + while (*t1 && *t2) + { + if (*t1 == '+') t1++; + if (*t2 == '+') t2++; + while (isdigit ((unsigned char) *t1)) t1++; + while (isdigit ((unsigned char) *t2)) t2++; + /* xxx Remove these next two lines when qualifiers are put in + all selectors, not just Protocol selectors. */ + t1 = objc_skip_type_qualifiers (t1); + t2 = objc_skip_type_qualifiers (t2); + if (! *t1 && ! *t2) + return YES; + if (*t1 != *t2) + return NO; + t1++; + t2++; + } + return NO; +} + +/* return selector representing name */ +SEL +sel_get_typed_uid (const char *name, const char *types) +{ + struct objc_list *l; + sidx i; + + objc_mutex_lock (__objc_runtime_mutex); + + i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name); + if (i == 0) + { + objc_mutex_unlock (__objc_runtime_mutex); + return 0; + } + + for (l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i); + l; l = l->tail) + { + SEL s = (SEL) l->head; + if (types == 0 || s->sel_types == 0) + { + if (s->sel_types == types) + { + objc_mutex_unlock (__objc_runtime_mutex); + return s; + } + } + else if (sel_types_match (s->sel_types, types)) + { + objc_mutex_unlock (__objc_runtime_mutex); + return s; + } + } + + objc_mutex_unlock (__objc_runtime_mutex); + return 0; +} + +/* Return selector representing name; prefer a selector with non-NULL type */ +SEL +sel_get_any_typed_uid (const char *name) +{ + struct objc_list *l; + sidx i; + SEL s = NULL; + + objc_mutex_lock (__objc_runtime_mutex); + + i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name); + if (i == 0) + { + objc_mutex_unlock (__objc_runtime_mutex); + return 0; + } + + for (l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i); + l; l = l->tail) + { + s = (SEL) l->head; + if (s->sel_types) + { + objc_mutex_unlock (__objc_runtime_mutex); + return s; + } + } + + objc_mutex_unlock (__objc_runtime_mutex); + return s; +} + +/* return selector representing name */ +SEL +sel_get_any_uid (const char *name) +{ + struct objc_list *l; + sidx i; + + objc_mutex_lock (__objc_runtime_mutex); + + i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name); + if (soffset_decode (i) == 0) + { + objc_mutex_unlock (__objc_runtime_mutex); + return 0; + } + + l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i); + objc_mutex_unlock (__objc_runtime_mutex); + + if (l == 0) + return 0; + + return (SEL) l->head; +} + +/* return selector representing name */ +SEL +sel_get_uid (const char *name) +{ + return sel_register_typed_name (name, 0); +} + +/* Get name of selector. If selector is unknown, the empty string "" + is returned */ +const char *sel_get_name (SEL selector) +{ + const char *ret; + + objc_mutex_lock (__objc_runtime_mutex); + if ((soffset_decode ((sidx)selector->sel_id) > 0) + && (soffset_decode ((sidx)selector->sel_id) <= __objc_selector_max_index)) + ret = sarray_get_safe (__objc_selector_names, (sidx) selector->sel_id); + else + ret = 0; + objc_mutex_unlock (__objc_runtime_mutex); + return ret; +} + +BOOL +sel_is_mapped (SEL selector) +{ + unsigned int idx = soffset_decode ((sidx)selector->sel_id); + return ((idx > 0) && (idx <= __objc_selector_max_index)); +} + + +const char *sel_get_type (SEL selector) +{ + if (selector) + return selector->sel_types; + else + return 0; +} + +/* The uninstalled dispatch table */ +extern struct sarray *__objc_uninstalled_dtable; + +/* __sel_register_typed_name allocates lots of struct objc_selector:s + of 8 (16, if pointers are 64 bits) bytes at startup. To reduce the number + of malloc calls and memory lost to malloc overhead, we allocate + objc_selector:s in blocks here. This is only called from + __sel_register_typed_name, and __sel_register_typed_name may only be + called when __objc_runtime_mutex is locked. + + Note that the objc_selector:s allocated from __sel_register_typed_name + are never freed. + + 62 because 62 * sizeof (struct objc_selector) = 496 (992). This should + let malloc add some overhead and use a nice, round 512 (1024) byte chunk. + */ +#define SELECTOR_POOL_SIZE 62 +static struct objc_selector *selector_pool; +static int selector_pool_left; + +static struct objc_selector * +pool_alloc_selector(void) +{ + if (!selector_pool_left) + { + selector_pool = objc_malloc (sizeof (struct objc_selector) + * SELECTOR_POOL_SIZE); + selector_pool_left = SELECTOR_POOL_SIZE; + } + return &selector_pool[--selector_pool_left]; +} + +/* Store the passed selector name in the selector record and return its + selector value (value returned by sel_get_uid). + Assumes that the calling function has locked down __objc_runtime_mutex. */ +/* is_const parameter tells us if the name and types parameters + are really constant or not. If YES then they are constant and + we can just store the pointers. If NO then we need to copy + name and types because the pointers may disappear later on. */ +SEL +__sel_register_typed_name (const char *name, const char *types, + struct objc_selector *orig, BOOL is_const) +{ + struct objc_selector *j; + sidx i; + struct objc_list *l; + // FIXME: The linker is now going to be uniquing most of our selectors for + // us, so we want to eliminate this hash calculation except as a fallback + + i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name); + if (soffset_decode (i) != 0) + { + for (l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i); + l; l = l->tail) + { + SEL s = (SEL) l->head; + if (types == 0 || s->sel_types == 0) + { + if (s->sel_types == types) + { + if (orig) + { + orig->sel_id = (void *) i; + return orig; + } + else + return s; + } + } + else if (! strcmp (s->sel_types, types)) + { + if (orig) + { + orig->sel_id = (void *) i; + return orig; + } + else + return s; + } + } + if (orig) + j = orig; + else + j = pool_alloc_selector (); + + j->sel_id = (void *) i; + /* Can we use the pointer or must copy types? Don't copy if NULL */ + if ((is_const) || (types == 0)) + j->sel_types = (const char *) types; + else { + j->sel_types = (char *) objc_malloc (strlen (types) + 1); + strcpy ((char *) j->sel_types, types); + } + l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i); + } + else + { + __objc_selector_max_index += 1; + i = soffset_encode (__objc_selector_max_index); + if (orig) + j = orig; + else + j = pool_alloc_selector (); + + j->sel_id = (void *) i; + /* Can we use the pointer or must copy types? Don't copy if NULL */ + if ((is_const) || (types == 0)) + j->sel_types = (const char *) types; + else { + j->sel_types = (char *) objc_malloc (strlen (types) + 1); + strcpy ((char *) j->sel_types, types); + } + l = 0; + } + + DEBUG_PRINTF ("Record selector %s[%s] as: %ld\n", name, types, + (long) soffset_decode (i)); + + { + int is_new = (l == 0); + const char *new_name; + + /* Can we use the pointer or must copy name? Don't copy if NULL */ + if ((is_const) || (name == 0)) + new_name = name; + else { + new_name = (char *) objc_malloc (strlen (name) + 1); + strcpy ((char *) new_name, name); + } + + l = list_cons ((void *) j, l); + sarray_at_put_safe (__objc_selector_names, i, (void *) new_name); + sarray_at_put_safe (__objc_selector_array, i, (void *) l); + if (is_new) + objc_hash_add (&__objc_selector_hash, (void *) new_name, (void *) i); + } + + sarray_realloc (__objc_uninstalled_dtable, __objc_selector_max_index + 1); + + return (SEL) j; +} + +SEL +sel_register_name (const char *name) +{ + SEL ret; + + objc_mutex_lock (__objc_runtime_mutex); + /* Assume that name is not constant static memory and needs to be + copied before put into a runtime structure. is_const == NO */ + ret = __sel_register_typed_name (name, 0, 0, NO); + objc_mutex_unlock (__objc_runtime_mutex); + + return ret; +} + +SEL +sel_register_typed_name (const char *name, const char *type) +{ + SEL ret; + + objc_mutex_lock (__objc_runtime_mutex); + /* Assume that name and type are not constant static memory and need to + be copied before put into a runtime structure. is_const == NO */ + ret = __sel_register_typed_name (name, type, 0, NO); + objc_mutex_unlock (__objc_runtime_mutex); + + return ret; +} diff --git a/sendmsg.c b/sendmsg.c new file mode 100644 index 0000000..161979f --- /dev/null +++ b/sendmsg.c @@ -0,0 +1,660 @@ +/* GNU Objective C Runtime message lookup + Copyright (C) 1993, 1995, 1996, 1997, 1998, + 2001, 2002, 2004, 2009 Free Software Foundation, Inc. + Contributed by Kresten Krab Thorup + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#include +#include "objc/runtime-legacy.h" +#include "objc/sarray.h" +#include "objc/encoding.h" + +/* This is how we hack STRUCT_VALUE to be 1 or 0. */ +#define gen_rtx(args...) 1 +#define gen_rtx_MEM(args...) 1 +#define gen_rtx_REG(args...) 1 +/* Alread defined in gcc/coretypes.h. So prevent double definition warning. */ +#undef rtx +#define rtx int + +#if ! defined (STRUCT_VALUE) || STRUCT_VALUE == 0 +#define INVISIBLE_STRUCT_RETURN 1 +#else +#define INVISIBLE_STRUCT_RETURN 0 +#endif + +/* The uninstalled dispatch table */ +struct sarray *__objc_uninstalled_dtable = 0; /* !T:MUTEX */ + +/* Two hooks for method forwarding. If either is set, it is invoked + * to return a function that performs the real forwarding. If both + * are set, the result of __objc_msg_forward2 will be preferred over + * that of __objc_msg_forward. If both return NULL or are unset, + * the libgcc based functions (__builtin_apply and friends) are + * used. + */ +IMP (*__objc_msg_forward) (SEL) = NULL; +IMP (*__objc_msg_forward2) (id, SEL) = NULL; + +/* Send +initialize to class */ +static void __objc_send_initialize (Class); + +static void __objc_install_dispatch_table_for_class (Class); + +/* Forward declare some functions */ +static void __objc_init_install_dtable (id, SEL); + +static Method_t search_for_method_in_hierarchy (Class class, SEL sel); +Method_t search_for_method_in_list (MethodList_t list, SEL op); +id nil_method (id, SEL); + +/* Given a selector, return the proper forwarding implementation. */ +inline +IMP +__objc_get_forward_imp (id rcv, SEL sel) +{ + /* If a custom forwarding hook was registered, try getting a forwarding + function from it. There are two forward routine hooks, one that + takes the receiver as an argument and one that does not. */ + if (__objc_msg_forward2) + { + IMP result; + if ((result = __objc_msg_forward2 (rcv, sel)) != NULL) + return result; + } + if (__objc_msg_forward) + { + IMP result; + if ((result = __objc_msg_forward (sel)) != NULL) + return result; + } + fprintf(stderr, "Object forwarding not available"); + abort(); +} + +struct objc_slot +{ + Class owner; + Class cachedFor; + const char *types; + int version; + IMP method; +}; + +// Malloc slots a page at a time. +#define SLOT_POOL_SIZE ((4096) / sizeof(struct objc_slot)) +static struct objc_slot *slot_pool; +static int slot_pool_left; + +static struct objc_slot * +pool_alloc_slot(void) +{ + if (!slot_pool_left) + { + slot_pool = objc_malloc (sizeof (struct objc_slot) + * SLOT_POOL_SIZE); + slot_pool_left = SLOT_POOL_SIZE; + } + return &slot_pool[--slot_pool_left]; +} + +static inline IMP sarray_get_imp (struct sarray *dtable, size_t key) +{ + struct objc_slot *slot = sarray_get_safe (dtable, key); + return (NULL != slot) ? slot->method : (IMP)0; +} + +/* Given a class and selector, return the selector's implementation. */ +inline +IMP +get_imp (Class class, SEL sel) +{ + /* In a vanilla implementation we would first check if the dispatch + table is installed. Here instead, to get more speed in the + standard case (that the dispatch table is installed) we first try + to get the imp using brute force. Only if that fails, we do what + we should have been doing from the very beginning, that is, check + if the dispatch table needs to be installed, install it if it's + not installed, and retrieve the imp from the table if it's + installed. */ + IMP res = sarray_get_imp (class->dtable, (size_t) sel->sel_id); + if (res == 0) + { + /* Not a valid method */ + if (class->dtable == __objc_uninstalled_dtable) + { + /* The dispatch table needs to be installed. */ + objc_mutex_lock (__objc_runtime_mutex); + + /* Double-checked locking pattern: Check + __objc_uninstalled_dtable again in case another thread + installed the dtable while we were waiting for the lock + to be released. */ + if (class->dtable == __objc_uninstalled_dtable) + { + __objc_install_dispatch_table_for_class (class); + } + + objc_mutex_unlock (__objc_runtime_mutex); + /* Call ourselves with the installed dispatch table + and get the real method */ + res = get_imp (class, sel); + } + else + { + /* The dispatch table has been installed. */ + + /* Get the method from the dispatch table (we try to get it + again in case another thread has installed the dtable just + after we invoked sarray_get_safe, but before we checked + class->dtable == __objc_uninstalled_dtable). + */ + res = sarray_get_imp (class->dtable, (size_t) sel->sel_id); + if (res == 0) + { + /* The dispatch table has been installed, and the method + is not in the dispatch table. So the method just + doesn't exist for the class. Return the forwarding + implementation. */ + res = __objc_get_forward_imp ((id)class, sel); + } + } + } + return res; +} + +/* Query if an object can respond to a selector, returns YES if the +object implements the selector otherwise NO. Does not check if the +method can be forwarded. */ +inline +BOOL +__objc_responds_to (id object, SEL sel) +{ + void *res; + + /* Install dispatch table if need be */ + if (object->class_pointer->dtable == __objc_uninstalled_dtable) + { + objc_mutex_lock (__objc_runtime_mutex); + if (object->class_pointer->dtable == __objc_uninstalled_dtable) + { + __objc_install_dispatch_table_for_class (object->class_pointer); + } + objc_mutex_unlock (__objc_runtime_mutex); + } + + /* Get the method from the dispatch table */ + res = sarray_get_safe (object->class_pointer->dtable, (size_t) sel->sel_id); + return (res != 0); +} + +/* This is the lookup function. All entries in the table are either a + valid method *or* zero. If zero then either the dispatch table + needs to be installed or it doesn't exist and forwarding is attempted. */ +inline +IMP +objc_msg_lookup (id receiver, SEL op) +{ + IMP result; + if (receiver) + { + result = sarray_get_imp (receiver->class_pointer->dtable, + (sidx)op->sel_id); + if (result == 0) + { + /* Not a valid method */ + if (receiver->class_pointer->dtable == __objc_uninstalled_dtable) + { + /* The dispatch table needs to be installed. + This happens on the very first method call to the class. */ + __objc_init_install_dtable (receiver, op); + + /* Get real method for this in newly installed dtable */ + result = get_imp (receiver->class_pointer, op); + } + else + { + /* The dispatch table has been installed. Check again + if the method exists (just in case the dispatch table + has been installed by another thread after we did the + previous check that the method exists). + */ + result = sarray_get_imp (receiver->class_pointer->dtable, + (sidx)op->sel_id); + if (result == 0) + { + /* If the method still just doesn't exist for the + class, attempt to forward the method. */ + result = __objc_get_forward_imp (receiver, op); + } + } + } + return result; + } + else + return (IMP)nil_method; +} + +IMP +objc_msg_lookup_super (Super_t super, SEL sel) +{ + if (super->self) + return get_imp (super->class, sel); + else + return (IMP)nil_method; +} + +int method_get_sizeof_arguments (Method *); + +/* + * TODO: This never worked properly. Delete it after checking no one is + * misguided enough to be using it. + */ +retval_t +objc_msg_sendv (id object, SEL op, arglist_t arg_frame) +{ + fprintf(stderr, "objc_msg_sendv() never worked correctly. Don't use it.\n"); + abort(); +} + +void +__objc_init_dispatch_tables () +{ + __objc_uninstalled_dtable = sarray_new (200, 0); +} + +/* This function is called by objc_msg_lookup when the + dispatch table needs to be installed; thus it is called once + for each class, namely when the very first message is sent to it. */ +static void +__objc_init_install_dtable (id receiver, SEL op __attribute__ ((__unused__))) +{ + objc_mutex_lock (__objc_runtime_mutex); + + /* This may happen, if the programmer has taken the address of a + method before the dtable was initialized... too bad for him! */ + if (receiver->class_pointer->dtable != __objc_uninstalled_dtable) + { + objc_mutex_unlock (__objc_runtime_mutex); + return; + } + + if (CLS_ISCLASS (receiver->class_pointer)) + { + /* receiver is an ordinary object */ + assert (CLS_ISCLASS (receiver->class_pointer)); + + /* install instance methods table */ + __objc_install_dispatch_table_for_class (receiver->class_pointer); + + /* call +initialize -- this will in turn install the factory + dispatch table if not already done :-) */ + __objc_send_initialize (receiver->class_pointer); + } + else + { + /* receiver is a class object */ + assert (CLS_ISCLASS ((Class)receiver)); + assert (CLS_ISMETA (receiver->class_pointer)); + + /* Install real dtable for factory methods */ + __objc_install_dispatch_table_for_class (receiver->class_pointer); + + __objc_send_initialize ((Class)receiver); + } + objc_mutex_unlock (__objc_runtime_mutex); +} + +/* Install dummy table for class which causes the first message to + that class (or instances hereof) to be initialized properly */ +void +__objc_install_premature_dtable (Class class) +{ + assert (__objc_uninstalled_dtable); + class->dtable = __objc_uninstalled_dtable; +} + +/* Send +initialize to class if not already done */ +static void +__objc_send_initialize (Class class) +{ + /* This *must* be a class object */ + assert (CLS_ISCLASS (class)); + assert (! CLS_ISMETA (class)); + + if (! CLS_ISINITIALIZED (class)) + { + CLS_SETINITIALIZED (class); + CLS_SETINITIALIZED (class->class_pointer); + + /* Create the garbage collector type memory description */ + __objc_generate_gc_type_description (class); + + if (class->super_class) + __objc_send_initialize (class->super_class); + + { + SEL op = sel_register_name ("initialize"); + IMP imp = 0; + MethodList_t method_list = class->class_pointer->methods; + + while (method_list) { + int i; + Method_t method; + + for (i = 0; i < method_list->method_count; i++) { + method = &(method_list->method_list[i]); + if (method->method_name + && method->method_name->sel_id == op->sel_id) { + imp = method->method_imp; + break; + } + } + + if (imp) + break; + + method_list = method_list->method_next; + + } + if (imp) + (*imp) ((id) class, op); + + } + } +} +/* Walk on the methods list of class and install the methods in the reverse + order of the lists. Since methods added by categories are before the methods + of class in the methods list, this allows categories to substitute methods + declared in class. However if more than one category replaces the same + method nothing is guaranteed about what method will be used. + Assumes that __objc_runtime_mutex is locked down. */ +static void +__objc_install_methods_in_dtable (Class class, MethodList_t method_list) +{ + int i; + + if (! method_list) + return; + + if (method_list->method_next) + __objc_install_methods_in_dtable (class, method_list->method_next); + + for (i = 0; i < method_list->method_count; i++) + { + Method_t method = &(method_list->method_list[i]); + size_t sel_id = (size_t)method->method_name->sel_id; + /* If there is an existing slot with this value, just update it. */ + struct objc_slot *slot = sarray_get_safe(class->dtable, sel_id); + if (NULL != slot && slot->owner == class) + { + slot->method = method->method_imp; + slot->version++; + } + else + { + //NOTE: We can improve this by sharing slots between subclasses where + // the IMPs are the same. + slot = pool_alloc_slot(); + slot->owner = class; + slot->types = method->method_types; + slot->method = method->method_imp; + slot->version = 1; + sarray_at_put_safe (class->dtable, sel_id, slot); + /* Invalidate the superclass's slot, if it has one. */ + slot = (NULL != class->super_class) ? + sarray_get_safe(class->super_class->dtable, sel_id) : NULL; + if (NULL != slot) + { + slot->version++; + } + } + } +} + +/* Assumes that __objc_runtime_mutex is locked down. */ +static void +__objc_install_dispatch_table_for_class (Class class) +{ + Class super; + + /* If the class has not yet had its class links resolved, we must + re-compute all class links */ + if (! CLS_ISRESOLV (class)) + __objc_resolve_class_links (); + + super = class->super_class; + + if (super != 0 && (super->dtable == __objc_uninstalled_dtable)) + __objc_install_dispatch_table_for_class (super); + + /* Allocate dtable if necessary */ + if (super == 0) + { + objc_mutex_lock (__objc_runtime_mutex); + class->dtable = sarray_new (__objc_selector_max_index, 0); + objc_mutex_unlock (__objc_runtime_mutex); + } + else + class->dtable = sarray_lazy_copy (super->dtable); + + __objc_install_methods_in_dtable (class, class->methods); +} + +static void merge_methods_from_superclass (Class class) +{ + Class super = class->super_class; + do + { + MethodList_t method_list = class->methods; + while (method_list) + { + int i; + + /* Search the method list. */ + for (i = 0; i < method_list->method_count; ++i) + { + Method_t method = &method_list->method_list[i]; + size_t sel_id = (size_t)method->method_name->sel_id; + struct objc_slot *slot = + sarray_get_safe(class->dtable, sel_id); + // If the slot already exists in this dtable, we have either + // overridden it in the subclass, or it is already pointing to + // the same slot as the superclass. If not, then we just install + // the slot pointer into this dtable. + if (NULL == slot) + { + slot = sarray_get_safe(super->dtable, sel_id); + // If slot is NULL here, something has gone badly wrong with + // the superclass already. + sarray_at_put_safe (class->dtable, sel_id, slot); + } + } + method_list = method_list->method_next; + } + } + while (super = super->super_class); + if (class->subclass_list) /* Traverse subclasses */ + for (Class next = class->subclass_list; next; next = next->sibling_class) + merge_methods_from_superclass (next); +} + +void +__objc_update_dispatch_table_for_class (Class class) +{ + Class next; + struct sarray *arr; + + /* not yet installed -- skip it */ + if (class->dtable == __objc_uninstalled_dtable) + return; + + __objc_install_methods_in_dtable (class, class->methods); + objc_mutex_lock (__objc_runtime_mutex); + + if (class->subclass_list) /* Traverse subclasses */ + for (next = class->subclass_list; next; next = next->sibling_class) + merge_methods_from_superclass (next); + + objc_mutex_unlock (__objc_runtime_mutex); +} + + +/* This function adds a method list to a class. This function is + typically called by another function specific to the run-time. As + such this function does not worry about thread safe issues. + + This one is only called for categories. Class objects have their + methods installed right away, and their selectors are made into + SEL's by the function __objc_register_selectors_from_class. */ +void +class_add_method_list (Class class, MethodList_t list) +{ + /* Passing of a linked list is not allowed. Do multiple calls. */ + assert (! list->method_next); + + __objc_register_selectors_from_list(list); + + /* Add the methods to the class's method list. */ + list->method_next = class->methods; + class->methods = list; + + /* Update the dispatch table of class */ + __objc_update_dispatch_table_for_class (class); +} + +Method_t +class_get_instance_method (Class class, SEL op) +{ + return search_for_method_in_hierarchy (class, op); +} + +Method_t +class_get_class_method (MetaClass class, SEL op) +{ + return search_for_method_in_hierarchy (class, op); +} + + +/* Search for a method starting from the current class up its hierarchy. + Return a pointer to the method's method structure if found. NULL + otherwise. */ + +static Method_t +search_for_method_in_hierarchy (Class cls, SEL sel) +{ + Method_t method = NULL; + Class class; + + if (! sel_is_mapped (sel)) + return NULL; + + /* Scan the method list of the class. If the method isn't found in the + list then step to its super class. */ + for (class = cls; ((! method) && class); class = class->super_class) + method = search_for_method_in_list (class->methods, sel); + + return method; +} + + + +/* Given a linked list of method and a method's name. Search for the named + method's method structure. Return a pointer to the method's method + structure if found. NULL otherwise. */ +Method_t +search_for_method_in_list (MethodList_t list, SEL op) +{ + MethodList_t method_list = list; + + if (! sel_is_mapped (op)) + return NULL; + + /* If not found then we'll search the list. */ + while (method_list) + { + int i; + + /* Search the method list. */ + for (i = 0; i < method_list->method_count; ++i) + { + Method_t method = &method_list->method_list[i]; + + if (method->method_name) + if (method->method_name->sel_id == op->sel_id) + return method; + } + + /* The method wasn't found. Follow the link to the next list of + methods. */ + method_list = method_list->method_next; + } + + return NULL; +} + +void +__objc_print_dtable_stats () +{ + int total = 0; + + objc_mutex_lock (__objc_runtime_mutex); + +#ifdef OBJC_SPARSE2 + printf ("memory usage: (%s)\n", "2-level sparse arrays"); +#else + printf ("memory usage: (%s)\n", "3-level sparse arrays"); +#endif + + printf ("arrays: %d = %ld bytes\n", narrays, + (long) ((size_t) narrays * sizeof (struct sarray))); + total += narrays * sizeof (struct sarray); + printf ("buckets: %d = %ld bytes\n", nbuckets, + (long) ((size_t) nbuckets * sizeof (struct sbucket))); + total += nbuckets * sizeof (struct sbucket); + + printf ("idxtables: %d = %ld bytes\n", + idxsize, (long) ((size_t) idxsize * sizeof (void *))); + total += idxsize * sizeof (void *); + printf ("-----------------------------------\n"); + printf ("total: %d bytes\n", total); + printf ("===================================\n"); + + objc_mutex_unlock (__objc_runtime_mutex); +} + +/* Returns the uninstalled dispatch table indicator. + If a class' dispatch table points to __objc_uninstalled_dtable + then that means it needs its dispatch table to be installed. */ +inline +struct sarray * +objc_get_uninstalled_dtable () +{ + return __objc_uninstalled_dtable; +} + +// This is an ugly hack to make sure that the compiler can do inlining into +// sendmsg2.c from here. It should be removed and the two compiled separately +// once we drop support for compilers that are too primitive to do cross-module +// inlining. +#include "sendmsg2.c" diff --git a/sendmsg.d b/sendmsg.d new file mode 100644 index 0000000..2d3cc72 --- /dev/null +++ b/sendmsg.d @@ -0,0 +1,25 @@ +sendmsg.o: sendmsg.c objc/runtime-legacy.h objc/objc.h objc/objc-api.h \ + objc/hash.h objc/Availability.h objc/thr.h objc/objc-decls.h \ + objc/objc-list.h objc/sarray.h objc/encoding.h sendmsg2.c + +objc/runtime-legacy.h: + +objc/objc.h: + +objc/objc-api.h: + +objc/hash.h: + +objc/Availability.h: + +objc/thr.h: + +objc/objc-decls.h: + +objc/objc-list.h: + +objc/sarray.h: + +objc/encoding.h: + +sendmsg2.c: diff --git a/sendmsg2.c b/sendmsg2.c new file mode 100644 index 0000000..47ca5dc --- /dev/null +++ b/sendmsg2.c @@ -0,0 +1,86 @@ +__thread id objc_msg_sender; + +static struct objc_slot nil_slot = { Nil, Nil, "", 1, (IMP)nil_method }; + +typedef struct objc_slot *Slot_t; + +Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender); + +// Default implementations of the two new hooks. Return NULL. +static id objc_proxy_lookup_null(id receiver, SEL op) { return nil; } +static Slot_t objc_msg_forward3_null(id receiver, SEL op) { return NULL; } + +id (*objc_proxy_lookup)(id receiver, SEL op) = objc_proxy_lookup_null; +Slot_t (*objc_msg_forward3)(id receiver, SEL op) = objc_msg_forward3_null; + +static inline +Slot_t objc_msg_lookup_internal(id *receiver, SEL selector, id sender) +{ + Slot_t result = sarray_get_safe((*receiver)->class_pointer->dtable, + (sidx)selector->sel_id); + if (0 == result) + { + /* Install the dtable if it hasn't already been initialized. */ + if ((*receiver)->class_pointer->dtable == __objc_uninstalled_dtable) + { + __objc_init_install_dtable (*receiver, selector); + result = sarray_get_safe((*receiver)->class_pointer->dtable, + (sidx)selector->sel_id); + } + else + { + // Check again incase another thread updated the dtable while we + // weren't looking + result = sarray_get_safe((*receiver)->class_pointer->dtable, + (sidx)selector->sel_id); + } + id newReceiver = objc_proxy_lookup(*receiver, selector); + // If some other library wants us to play forwarding games, try again + // with the new object. + if (nil != newReceiver) + { + *receiver = newReceiver; + return objc_msg_lookup_sender(receiver, selector, sender); + } + if (0 == result) + { + result = objc_msg_forward3(*receiver, selector); + } + } + return result; +} + + +Slot_t (*objc_plane_lookup)(id *receiver, SEL op, id sender) = + objc_msg_lookup_internal; + +/** + * New Objective-C lookup function. This permits the lookup to modify the + * receiver and also supports multi-dimensional dispatch based on the sender. + */ +Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender) +{ + // Returning a nil slot allows the caller to cache the lookup for nil too, + // although this is not particularly useful because the nil method can be + // inlined trivially. + if(*receiver == nil) + { + return &nil_slot; + } + + if (__builtin_expect(sender == nil + || + (sender->class_pointer->info & (*receiver)->class_pointer->info & _CLS_PLANE_AWARE),1)) + { + return objc_msg_lookup_internal(receiver, selector, sender); + } + // If we are in plane-aware code + void *senderPlaneID = *((void**)sender - 1); + void *receiverPlaneID = *((void**)receiver - 1); + if (senderPlaneID == receiverPlaneID) + { + //fprintf(stderr, "Intraplane message\n"); + return objc_msg_lookup_internal(receiver, selector, sender); + } + return objc_plane_lookup(receiver, selector, sender); +} diff --git a/sync.m b/sync.m new file mode 100644 index 0000000..1f4a4aa --- /dev/null +++ b/sync.m @@ -0,0 +1,105 @@ +#include "objc/runtime.h" +#include "lock.h" + +#include +#include + +@interface Fake ++ (void)dealloc; +@end + +static mutex_t at_sync_init_lock; +static unsigned long long lockClassId; + +void __objc_sync_init(void) +{ + INIT_LOCK(at_sync_init_lock); +} + +IMP objc_msg_lookup(id, SEL); + +static void deallocLockClass(id obj, SEL _cmd); + +static inline Class findLockClass(id obj) +{ + struct objc_object object = { obj->isa }; + SEL dealloc = @selector(dealloc); + // Find the first class where this lookup is correct + if (objc_msg_lookup((id)&object, dealloc) != (IMP)deallocLockClass) + { + do { + object.isa = class_getSuperclass(object.isa); + } while (Nil != object.isa && + objc_msg_lookup((id)&object, dealloc) != (IMP)deallocLockClass); + } + if (Nil == object.isa) { return Nil; } + // object->isa is now either the lock class, or a class which inherits from + // the lock class + Class lastClass; + do { + lastClass = object.isa; + object.isa = class_getSuperclass(object.isa); + } while (Nil != object.isa && + objc_msg_lookup((id)&object, dealloc) == (IMP)deallocLockClass); + return lastClass; +} + +static inline Class initLockObject(id obj) +{ + char nameBuffer[40]; + snprintf(nameBuffer, 39, "hiddenlockClass%lld", lockClassId++); + Class lockClass = objc_allocateClassPair(obj->isa, nameBuffer, + sizeof(mutex_t)); + const char *types = + method_getTypeEncoding(class_getInstanceMethod(obj->isa, + @selector(dealloc))); + class_addMethod(lockClass, @selector(dealloc), (IMP)deallocLockClass, + types); + objc_registerClassPair(lockClass); + + mutex_t *lock = object_getIndexedIvars(lockClass); + INIT_LOCK(*lock); + + obj->isa = lockClass; + return lockClass; +} + +static void deallocLockClass(id obj, SEL _cmd) +{ + Class lockClass = findLockClass(obj); + Class realClass = class_getSuperclass(lockClass); + // Free the lock + mutex_t *lock = object_getIndexedIvars(lockClass); + DESTROY_LOCK(lock); + // Free the class + objc_disposeClassPair(lockClass); + // Reset the class then call the real -dealloc + obj->isa = realClass; + [obj dealloc]; +} + +// TODO: This should probably have a special case for classes conforming to the +// NSLocking protocol, just sending them a -lock message. +void objc_sync_enter(id obj) +{ + Class lockClass = findLockClass(obj); + if (Nil == lockClass) + { + LOCK(&at_sync_init_lock); + // Test again in case two threads call objc_sync_enter at once + lockClass = findLockClass(obj); + if (Nil == lockClass) + { + lockClass = initLockObject(obj); + } + UNLOCK(&at_sync_init_lock); + } + mutex_t *lock = object_getIndexedIvars(lockClass); + LOCK(lock); +} +void objc_sync_exit(id obj) +{ + Class lockClass = findLockClass(obj); + mutex_t *lock = object_getIndexedIvars(lockClass); + LOCK(lock); +} diff --git a/thr.c b/thr.c new file mode 100644 index 0000000..1be4197 --- /dev/null +++ b/thr.c @@ -0,0 +1,81 @@ +/* GNU Objective C Runtime Thread Interface + Copyright (C) 1996, 1997, 2009 Free Software Foundation, Inc. + Contributed by Galen C. Hunt (gchunt@cs.rochester.edu) + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3, or (at your option) any later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +#include +#include "lock.h" +#include "objc/runtime-legacy.h" + +/* The hook function called when the runtime becomes multi threaded */ +// The runtime is now always in multithreaded mode as there was no benefit to +// not being in multithreaded mode. + +objc_thread_callback objc_set_thread_callback (objc_thread_callback func) +{ + static objc_thread_callback _objc_became_multi_threaded = NULL; + objc_thread_callback temp = _objc_became_multi_threaded; + // Call the function immediately and then ignore it forever because it is + // pointless. + func(); + _objc_became_multi_threaded = func; + return temp; +} + +/* Deprecated functions for creating and destroying mutexes. */ +objc_mutex_t objc_mutex_allocate (void) +{ + mutex_t *mutex = malloc(sizeof(mutex_t)); + INIT_LOCK(*mutex); + return mutex; +} +int objc_mutex_deallocate (objc_mutex_t mutex) +{ + DESTROY_LOCK(mutex); + free(mutex); + return 0; +} +int __objc_init_thread_system(void) { return 0; } + +/* External functions for locking and unlocking runtime mutexes. */ +int +objc_mutex_lock (objc_mutex_t mutex) +{ + return LOCK((mutex_t*)mutex); +} + +int +objc_mutex_unlock (objc_mutex_t mutex) +{ + return UNLOCK((mutex_t*)mutex); +} + + +// Legacy cruft that never did anything: +void +objc_thread_add (void) {} + +void +objc_thread_remove (void) {} + +/* End of File */ diff --git a/unwind-pe.h b/unwind-pe.h new file mode 100644 index 0000000..317caf0 --- /dev/null +++ b/unwind-pe.h @@ -0,0 +1,294 @@ +/* Exception handling and frame unwind runtime interface routines. + Copyright (C) 2001, 2002, 2003, 2004, 2008, 2009 Free Software Foundation, Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . */ + +/* @@@ Really this should be out of line, but this also causes link + compatibility problems with the base ABI. This is slightly better + than duplicating code, however. */ + +#ifndef GCC_UNWIND_PE_H +#define GCC_UNWIND_PE_H + +/* If using C++, references to abort have to be qualified with std::. */ +#if __cplusplus +#define __gxx_abort std::abort +#else +#define __gxx_abort abort +#endif + +/* Pointer encodings, from dwarf2.h. */ +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 + +#define DW_EH_PE_indirect 0x80 + + +#ifndef NO_SIZE_OF_ENCODED_VALUE +#include + +typedef intptr_t _sleb128_t; +typedef uintptr_t _uleb128_t; + + +/* Given an encoding, return the number of bytes the format occupies. + This is only defined for fixed-size encodings, and so does not + include leb128. */ + +static unsigned int +size_of_encoded_value (unsigned char encoding) __attribute__ ((unused)); + +static unsigned int +size_of_encoded_value (unsigned char encoding) +{ + if (encoding == DW_EH_PE_omit) + return 0; + + switch (encoding & 0x07) + { + case DW_EH_PE_absptr: + return sizeof (void *); + case DW_EH_PE_udata2: + return 2; + case DW_EH_PE_udata4: + return 4; + case DW_EH_PE_udata8: + return 8; + } + __gxx_abort (); +} + +#endif + +#ifndef NO_BASE_OF_ENCODED_VALUE + +/* Given an encoding and an _Unwind_Context, return the base to which + the encoding is relative. This base may then be passed to + read_encoded_value_with_base for use when the _Unwind_Context is + not available. */ + +static _Unwind_Ptr +base_of_encoded_value (unsigned char encoding, struct _Unwind_Context *context) +{ + if (encoding == DW_EH_PE_omit) + return 0; + + switch (encoding & 0x70) + { + case DW_EH_PE_absptr: + case DW_EH_PE_pcrel: + case DW_EH_PE_aligned: + return 0; + + case DW_EH_PE_textrel: + return _Unwind_GetTextRelBase (context); + case DW_EH_PE_datarel: + return _Unwind_GetDataRelBase (context); + case DW_EH_PE_funcrel: + return _Unwind_GetRegionStart (context); + } + __gxx_abort (); +} + +#endif + +/* Read an unsigned leb128 value from P, store the value in VAL, return + P incremented past the value. We assume that a word is large enough to + hold any value so encoded; if it is smaller than a pointer on some target, + pointers should not be leb128 encoded on that target. */ + +static const unsigned char * +read_uleb128 (const unsigned char *p, _uleb128_t *val) +{ + unsigned int shift = 0; + unsigned char byte; + _uleb128_t result; + + result = 0; + do + { + byte = *p++; + result |= ((_uleb128_t)byte & 0x7f) << shift; + shift += 7; + } + while (byte & 0x80); + + *val = result; + return p; +} + +/* Similar, but read a signed leb128 value. */ + +static const unsigned char * +read_sleb128 (const unsigned char *p, _sleb128_t *val) +{ + unsigned int shift = 0; + unsigned char byte; + _uleb128_t result; + + result = 0; + do + { + byte = *p++; + result |= ((_uleb128_t)byte & 0x7f) << shift; + shift += 7; + } + while (byte & 0x80); + + /* Sign-extend a negative value. */ + if (shift < 8 * sizeof(result) && (byte & 0x40) != 0) + result |= -(((_uleb128_t)1L) << shift); + + *val = (_sleb128_t) result; + return p; +} + +/* Load an encoded value from memory at P. The value is returned in VAL; + The function returns P incremented past the value. BASE is as given + by base_of_encoded_value for this encoding in the appropriate context. */ + +static const unsigned char * +read_encoded_value_with_base (unsigned char encoding, _Unwind_Ptr base, + const unsigned char *p, _Unwind_Ptr *val) +{ + union unaligned + { + void *ptr; + unsigned u2 __attribute__ ((mode (HI))); + unsigned u4 __attribute__ ((mode (SI))); + unsigned u8 __attribute__ ((mode (DI))); + signed s2 __attribute__ ((mode (HI))); + signed s4 __attribute__ ((mode (SI))); + signed s8 __attribute__ ((mode (DI))); + } __attribute__((__packed__)); + + const union unaligned *u = (const union unaligned *) p; + _Unwind_Internal_Ptr result; + + if (encoding == DW_EH_PE_aligned) + { + _Unwind_Internal_Ptr a = (_Unwind_Internal_Ptr) p; + a = (a + sizeof (void *) - 1) & - sizeof(void *); + result = *(_Unwind_Internal_Ptr *) a; + p = (const unsigned char *) (_Unwind_Internal_Ptr) (a + sizeof (void *)); + } + else + { + switch (encoding & 0x0f) + { + case DW_EH_PE_absptr: + result = (_Unwind_Internal_Ptr) u->ptr; + p += sizeof (void *); + break; + + case DW_EH_PE_uleb128: + { + _uleb128_t tmp; + p = read_uleb128 (p, &tmp); + result = (_Unwind_Internal_Ptr) tmp; + } + break; + + case DW_EH_PE_sleb128: + { + _sleb128_t tmp; + p = read_sleb128 (p, &tmp); + result = (_Unwind_Internal_Ptr) tmp; + } + break; + + case DW_EH_PE_udata2: + result = u->u2; + p += 2; + break; + case DW_EH_PE_udata4: + result = u->u4; + p += 4; + break; + case DW_EH_PE_udata8: + result = u->u8; + p += 8; + break; + + case DW_EH_PE_sdata2: + result = u->s2; + p += 2; + break; + case DW_EH_PE_sdata4: + result = u->s4; + p += 4; + break; + case DW_EH_PE_sdata8: + result = u->s8; + p += 8; + break; + + default: + __gxx_abort (); + } + + if (result != 0) + { + result += ((encoding & 0x70) == DW_EH_PE_pcrel + ? (_Unwind_Internal_Ptr) u : base); + if (encoding & DW_EH_PE_indirect) + result = *(_Unwind_Internal_Ptr *) result; + } + } + + *val = result; + return p; +} + +#ifndef NO_BASE_OF_ENCODED_VALUE + +/* Like read_encoded_value_with_base, but get the base from the context + rather than providing it directly. */ + +static inline const unsigned char * +read_encoded_value (struct _Unwind_Context *context, unsigned char encoding, + const unsigned char *p, _Unwind_Ptr *val) +{ + return read_encoded_value_with_base (encoding, + base_of_encoded_value (encoding, context), + p, val); +} + +#endif + +#endif /* unwind-pe.h */