pax_global_header00006660000000000000000000000064134221524160014512gustar00rootroot0000000000000052 comment=aab81d4474f91cb111119b3a5b85abe73e6f7d17 gcc-python-plugin-0.17/000077500000000000000000000000001342215241600150105ustar00rootroot00000000000000gcc-python-plugin-0.17/.gitignore000066400000000000000000000014341342215241600170020ustar00rootroot00000000000000*~ .*.sw[a-z] *.o *.so a.out *.pyc test.png # Generated during configuration: config-tests # Generated elsewhere in build: autogenerated* gcc-c-api/gcc-public-types.h gcc-c-api/gcc-semiprivate-types.h gcc-c-api/gcc-callgraph.h gcc-c-api/gcc-cfg.h gcc-c-api/gcc-constant.h gcc-c-api/gcc-declaration.h gcc-c-api/gcc-diagnostics.h gcc-c-api/gcc-function.h gcc-c-api/gcc-gimple.h gcc-c-api/gcc-location.h gcc-c-api/gcc-option.h gcc-c-api/gcc-rtl.h gcc-c-api/gcc-tree.h gcc-c-api/gcc-type.h gcc-c-api/gcc-variable.h gcc-with-python.1.gz print-gcc-version # Generated by Sphinx docs/_build # Logfiles generated during the selftest suite: *.c.cpychecker-log.txt *.*-refcount-errors.html *.*-refcount-errors.v2.html *.*-refcount-traces.html tests/plugin/dumpfiles/input.c.*t.test-pass pygments_c.css gcc-python-plugin-0.17/.travis.yml000066400000000000000000000033011342215241600171160ustar00rootroot00000000000000matrix: include: - os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.8 gcc-4.8-plugin-dev env: - MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8" # works on Precise and Trusty - os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-4.9 gcc-4.9-plugin-dev env: - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" # works on Precise and Trusty - os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-5 gcc-5-plugin-dev env: - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" # works on Precise and Trusty - os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-6 gcc-6-plugin-dev env: - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" # works on Precise and Trusty - os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-7 gcc-7-plugin-dev env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-8 gcc-8-plugin-dev env: - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" language: c compiler: - gcc before_install: - eval "${MATRIX_EVAL}" - sudo apt-get update -qq - sudo apt-get install -qq python-six python-pygments graphviz python-lxml script: - pwd=$(pwd -P) - mkdir build - cd build - make -f $pwd/Makefile srcdir=$pwd/ gcc-python-plugin-0.17/COPYING000066400000000000000000001045131342215241600160470ustar00rootroot00000000000000 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 . gcc-python-plugin-0.17/LICENSES.txt000066400000000000000000000017731342215241600167660ustar00rootroot00000000000000The C and Python code is licensed under the GPLv3 or later (see COPYING and the per-file license information). There are copies of various JavaScript and CSS libraries embedded within the source tree, which have their own licenses as follows: libcpychecker_html/extlib/jquery-1.7.1.min.js: jquery.org/license (MIT or GPLv2) libcpychecker_html/extlib/prefixfree-1.0.4.min.js: MIT license libcpychecker_html/extlib/reset-20110126.css: Public Domain There are also various images within: libcpychecke_/html/images These were taken from: http://code.google.com/p/fugue-icons-src/ and are: "© 2012 Yusuke Kamiyamane. All rights reserved. These icons are available under a Creative Commons Attribution 3.0 License. " http://creativecommons.org/licenses/by/3.0/ The test code contains historical examples of reference-count bugs: * libcpychecker_html/test/example2 contains an embedded copy of part of an old version of pylibmc, which is under a 3-clause BSD license gcc-python-plugin-0.17/Makefile000066400000000000000000000350761342215241600164630ustar00rootroot00000000000000# Copyright 2011-2013, 2017 David Malcolm # Copyright 2011-2013, 2017 Red Hat, Inc. # # This 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 # . ifneq ($(srcdir),) VPATH = $(srcdir) endif xmldir = $(srcdir)./gcc-c-api/ pwd = $(shell pwd -P) .PHONY: all clean debug dump_gimple plugin show-ssa tarball \ test-suite testcpychecker testcpybuilder testdejagnu \ man PLUGIN_SOURCE_FILES= \ gcc-python.c \ gcc-python-attribute.c \ gcc-python-callbacks.c \ gcc-python-callgraph.c \ gcc-python-cfg.c \ gcc-python-closure.c \ gcc-python-diagnostics.c \ gcc-python-function.c \ gcc-python-gimple.c \ gcc-python-location.c \ gcc-python-option.c \ gcc-python-parameter.c \ gcc-python-pass.c \ gcc-python-pretty-printer.c \ gcc-python-rtl.c \ gcc-python-tree.c \ gcc-python-variable.c \ gcc-python-version.c \ gcc-python-wrapper.c \ PLUGIN_GENERATED_SOURCE_FILES:= \ autogenerated-callgraph.c \ autogenerated-casts.c \ autogenerated-cfg.c \ autogenerated-option.c \ autogenerated-function.c \ autogenerated-gimple.c \ autogenerated-location.c \ autogenerated-parameter.c \ autogenerated-pass.c \ autogenerated-pretty-printer.c \ autogenerated-rtl.c \ autogenerated-tree.c \ autogenerated-variable.c PLUGIN_OBJECT_SOURCE_FILES:= $(patsubst %.c,%.o,$(PLUGIN_SOURCE_FILES)) PLUGIN_OBJECT_GENERATED_FILES:= $(patsubst %.c,%.o,$(PLUGIN_GENERATED_SOURCE_FILES)) PLUGIN_OBJECT_FILES:= $(PLUGIN_OBJECT_SOURCE_FILES) $(PLUGIN_OBJECT_GENERATED_FILES) GCCPLUGINS_DIR:= $(shell $(CC) --print-file-name=plugin) GENERATOR_DEPS=cpybuilder.py wrapperbuilder.py print-gcc-version # The plugin supports both Python 2 and Python 3 # # In theory we could have arbitrary combinations of python versions for each # of: # - python version used when running scripts during the build (e.g. to # generate code) # - python version we compile and link the plugin against # - when running the plugin with the cpychecker script, the python version # that the code is being compiled against # # However, to keep things simple, let's assume for now that all of these are # the same version: we're building the plugin using the same version of Python # as we're linking against, and that the cpychecker will be testing that same # version of Python # # By default, build against "python", using "python-config" to query for # compilation options. You can override this by passing other values for # PYTHON and PYTHON_CONFIG when invoking "make" (or by simply hacking up this # file): e.g. # make PYTHON=python3 PYTHON_CONFIG=python3-config all # The python interpreter to use: PYTHON=python # The python-config executable to use: PYTHON_CONFIG=python-config #PYTHON=python3 #PYTHON_CONFIG=python3-config #PYTHON=python-debug #PYTHON_CONFIG=python-debug-config #PYTHON=python3-debug #PYTHON_CONFIG=python3.3dm-config PYTHON_INCLUDES=$(shell $(PYTHON_CONFIG) --includes) PYTHON_LIBS=$(shell $(PYTHON_CONFIG) --libs) # Support having multiple named plugins # e.g. "python2.7" "python3.2mu" "python 3.2dmu" etc: PLUGIN_NAME := python PLUGIN_DSO := $(PLUGIN_NAME).so PLUGIN_DIR := $(PLUGIN_NAME) # For now, gcc-c-api is part of this project # (Eventually it will be moved to its own project) LIBGCC_C_API_SO := gcc-c-api/libgcc-c-api.so CPPFLAGS+= -I$(GCCPLUGINS_DIR)/include -I$(GCCPLUGINS_DIR)/include/c-family -I. $(PYTHON_INCLUDES) # Allow user to pick optimization, choose whether warnings are fatal, # and choose debugging information level. CFLAGS?=-O2 -Werror -g # Force these settings CFLAGS+= -fPIC -fno-strict-aliasing -Wall LIBS+= $(PYTHON_LIBS) ifneq "$(PLUGIN_PYTHONPATH)" "" CPPFLAGS+= -DPLUGIN_PYTHONPATH='"$(PLUGIN_PYTHONPATH)"' endif all: autogenerated-config.h testcpybuilder testdejagnu test-suite testcpychecker # What still needs to be wrapped? api-report: grep -nH -e "\.inner" gcc-*.c *.h generate-*.py plugin: autogenerated-config.h $(PLUGIN_DSO) # When running the plugin from a working copy, use LD_LIBARY_PATH=gcc-c-api # so that the plugin can find its libgcc-c-api.so there # INVOCATION_ENV_VARS := PYTHONPATH=$(srcdir)./ CC_FOR_CPYCHECKER=$(CC) LD_LIBRARY_PATH=gcc-c-api:$(LD_LIBRARY_PATH) CC=$(CC) # When installing, both the plugin and libgcc-c-api.so will be installed to # $(GCCPLUGINS_DIR), so we give the plugin an RPATH of $(GCCPLUGINS_DIR) # so that it finds the libgcc-c-api.so there (to support the case of having # multiple GCCs installed) # $(PLUGIN_DSO): $(PLUGIN_OBJECT_FILES) $(LIBGCC_C_API_SO) $(CC) \ $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \ -shared \ $(PLUGIN_OBJECT_FILES) \ -o $@ \ $(LIBS) \ -lgcc-c-api -Lgcc-c-api -Wl,-rpath=$(GCCPLUGINS_DIR) $(pwd)/gcc-c-api: mkdir -p $@ $(LIBGCC_C_API_SO): $(pwd)/gcc-c-api cd gcc-c-api && make $(if $(srcdir),-f $(srcdir)./gcc-c-api/Makefile) libgcc-c-api.so CC=$(CC) $(if $(srcdir),srcdir=$(srcdir)./gcc-c-api/) $(PLUGIN_OBJECT_GENERATED_FILES): CPPFLAGS+= $(if $(srcdir),-I$(srcdir)) # This is the standard .c->.o recipe, but it needs to be stated # explicitly to support the case that $(srcdir) is not blank. $(PLUGIN_OBJECT_FILES): %.o: %.c autogenerated-config.h gcc-python.h $(LIBGCC_C_API_SO) autogenerated-EXTRA_CFLAGS.txt $(COMPILE.c) $(shell cat autogenerated-EXTRA_CFLAGS.txt) $(OUTPUT_OPTION) -I$(srcdir)./ -I$(srcdir)./gcc-c-api -I./gcc-c-api $< print-gcc-version: print-gcc-version.c autogenerated-EXTRA_CFLAGS.txt $(CC) \ $(CPPFLAGS) $(CFLAGS) \ $(shell cat autogenerated-EXTRA_CFLAGS.txt) \ -o $@ \ $< clean: $(RM) *.so *.o gcc-c-api/*.o autogenerated* $(RM) -r docs/_build $(RM) -f gcc-with-$(PLUGIN_NAME).1 gcc-with-$(PLUGIN_NAME).1.gz $(RM) -f print-gcc-version cd gcc-c-api && make clean find tests -name "*.o" -delete autogenerated-config.h: generate-config-h.py configbuilder.py $(PYTHON) $< -o $@ --gcc="$(CC)" --plugindir="$(GCCPLUGINS_DIR)" autogenerated-%.txt: %.txt.in $(CPP) $(CPPFLAGS) -x c-header $^ -o $@ # autogenerated-EXTRA_CFLAGS.txt is a byproduct of making # autogenerated-config.h: autogenerated-EXTRA_CFLAGS.txt: autogenerated-config.h # autogenerated-casts.h is a byproduct of making autogenerated-casts.c autogenerated-casts.h: autogenerated-casts.c $(PLUGIN_GENERATED_SOURCE_FILES): autogenerated-%.c: generate-%-c.py $(GENERATOR_DEPS) $(PYTHON) $< > $@ autogenerated-casts.c: autogenerated-gimple-types.txt autogenerated-tree-types.txt autogenerated-rtl-types.txt generate-casts-c.py PYTHONPATH=$(srcdir)./gcc-c-api $(PYTHON) $(srcdir)generate-casts-c.py autogenerated-casts.c autogenerated-casts.h $(xmldir) autogenerated-gimple.c: autogenerated-gimple-types.txt autogenerated-tree-types.txt autogenerated-rtl-types.txt maketreetypes.py autogenerated-tree.c: autogenerated-tree-types.txt maketreetypes.py autogenerated-rtl.c: autogenerated-rtl-types.txt maketreetypes.py autogenerated-variable.c: autogenerated-gimple-types.txt maketreetypes.py bindir=/usr/bin mandir=/usr/share/man UpperPluginName = $(shell $(PYTHON) -c"print('$(PLUGIN_NAME)'.upper())") docs/_build/man/gcc-with-python.1: docs/gcc-with-python.rst cd docs && $(MAKE) man gcc-with-$(PLUGIN_NAME).1: docs/_build/man/gcc-with-python.1 # Fixup the generic manpage for this build: cp docs/_build/man/gcc-with-python.1 gcc-with-$(PLUGIN_NAME).1 sed \ -i \ -e"s|gcc-with-python|gcc-with-$(PLUGIN_NAME)|g" \ gcc-with-$(PLUGIN_NAME).1 sed \ -i \ -e"s|GCC-WITH-PYTHON|GCC-WITH-$(UpperPluginName)|g" \ gcc-with-$(PLUGIN_NAME).1 gcc-with-$(PLUGIN_NAME).1.gz: gcc-with-$(PLUGIN_NAME).1 rm -f gcc-with-$(PLUGIN_NAME).1.gz gzip gcc-with-$(PLUGIN_NAME).1 man: gcc-with-$(PLUGIN_NAME).1.gz install: $(PLUGIN_DSO) gcc-with-$(PLUGIN_NAME).1.gz mkdir -p $(DESTDIR)$(GCCPLUGINS_DIR) cd gcc-c-api && $(MAKE) install cp $(PLUGIN_DSO) $(DESTDIR)$(GCCPLUGINS_DIR) mkdir -p $(DESTDIR)$(GCCPLUGINS_DIR)/$(PLUGIN_DIR) cp -a gccutils $(DESTDIR)$(GCCPLUGINS_DIR)/$(PLUGIN_DIR) cp -a libcpychecker $(DESTDIR)$(GCCPLUGINS_DIR)/$(PLUGIN_DIR) # Create "gcc-with-" support script: mkdir -p $(DESTDIR)$(bindir) install -m 755 gcc-with-python $(DESTDIR)/$(bindir)/gcc-with-$(PLUGIN_NAME) # Fixup the reference to the plugin in that script, from being expressed as # a DSO filename with a path (for a working copy) to a name of an installed # plugin within GCC's search directory: sed \ -i \ -e"s|-fplugin=[^ ]*|-fplugin=$(PLUGIN_NAME)|" \ $(DESTDIR)$(bindir)/gcc-with-$(PLUGIN_NAME) # Fixup the plugin name within -fplugin-arg-PLUGIN_NAME-script to match the # name for this specific build: sed \ -i \ -e"s|-fplugin-arg-python-script|-fplugin-arg-$(PLUGIN_NAME)-script|" \ $(DESTDIR)$(bindir)/gcc-with-$(PLUGIN_NAME) mkdir -p $(DESTDIR)$(mandir)/man1 cp gcc-with-$(PLUGIN_NAME).1.gz $(DESTDIR)$(mandir)/man1 # Hint for debugging: add -v to the gcc options # to get a command line for invoking individual subprocesses # Doing so seems to require that paths be absolute, rather than relative # to this directory TEST_CFLAGS= \ -fplugin=$(CURDIR)/$(PLUGIN_DSO) \ -fplugin-arg-python-script=test.py # A catch-all test for quick experimentation with the API: test: plugin $(INVOCATION_ENV_VARS) $(CC) -v $(TEST_CFLAGS) $(CURDIR)/test.c # Selftest for the cpychecker.py code: testcpychecker: plugin $(INVOCATION_ENV_VARS) $(PYTHON) $(srcdir)./testcpychecker.py -v # Selftest for the cpybuilder code: testcpybuilder: $(PYTHON) $(srcdir)./testcpybuilder.py -v # Selftest for the dejagnu.py code: testdejagnu: $(PYTHON) $(srcdir)./dejagnu.py -v dump_gimple: $(CC) -fdump-tree-gimple $(CURDIR)/test.c debug: plugin $(INVOCATION_ENV_VARS) $(CC) -v $(TEST_CFLAGS) $(CURDIR)/test.c $(pwd)/gcc-with-cpychecker: gcc-with-cpychecker cp $< $@ # A simple demo, to make it easy to demonstrate the cpychecker: demo: demo.c plugin $(pwd)/gcc-with-cpychecker $(INVOCATION_ENV_VARS) ./gcc-with-cpychecker -c $(PYTHON_INCLUDES) $< # Run 'demo', and verify the output. testdemo: DEMO_REF=$(shell \ if [ $$(./print-gcc-version) -ge 7000 ]; then \ echo demo.expected.no-refcounts; \ else \ echo demo.expected; \ fi) testdemo: plugin print-gcc-version $(MAKE) -f $(srcdir)./Makefile demo > demo.out 2> demo.err egrep '^.*demo.c:( In function |[0-9][0-9]*:[0-9][0-9]*: warning:)' \ demo.err \ | sed 's/:[0-9][0-9]*: warning:/:: warning:/;s/ \[enabled by default\]//' \ | sed "s%$(srcdir)demo.c:%demo.c:%g" \ > demo.filtered diff $(srcdir)./$(DEMO_REF) demo.filtered rm demo.out demo.err demo.filtered json-examples: plugin $(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-cpychecker -I/usr/include/python2.7 -c libcpychecker_html/test/example1/bug.c test-suite: plugin print-gcc-version testdejagnu testdemo $(INVOCATION_ENV_VARS) $(PYTHON) $(srcdir)./run-test-suite.py $(if $(srcdir),--srcdir=$(srcdir)) show-ssa: plugin $(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-python examples/show-ssa.py test.c demo-show-lto-supergraph: plugin $(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-python \ examples/show-lto-supergraph.py \ -flto \ -flto-partition=none \ tests/examples/lto/input-*.c html: docs/tables-of-passes.rst docs/passes.svg cd docs && $(MAKE) html # We commit this generated file to SCM to allow the docs to be built without # needing to build the plugin: docs/tables-of-passes.rst: plugin generate-tables-of-passes-rst.py $(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-python generate-tables-of-passes-rst.py test.c > $@ # Likewise for this generated file: docs/passes.svg: plugin generate-passes-svg.py $(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-python generate-passes-svg.py test.c check-api: xmllint --noout --relaxng $(srcdir)./gcc-c-api/api.rng $(srcdir)./gcc-c-api/*.xml # Utility target, to help me to make releases # - creates a tag in git (but does not push it; see "Notes to self on # making a release" below) # - creates a tarball # # The following assumes that VERSION has been set e.g. # $ make tarball VERSION=0.4 $(HOME)/rpmbuild/SOURCES/%.tar.gz: test -n "$(VERSION)" -git tag -d v$(VERSION) git tag -a v$(VERSION) -m"$(VERSION)" git archive --format=tar --prefix=$*/ v$(VERSION) | gzip > $*.tar.gz sha256sum $*.tar.gz cp $*.tar.gz $@ tarball: $(HOME)/rpmbuild/SOURCES/gcc-python-plugin-$(VERSION).tar.gz # Notes to self on making a release # --------------------------------- # # Before tagging: # # * update the version/release in docs/conf.py # # * update the version in gcc-python-plugin.spec # # * add release notes to docs # # Test the candidate tarball via a scratch SRPM build locally (this # achieves test coverage against python 2 and 3, for both debug and # optimized python, on one arch, against the locally-installed version of # gcc): # # $ make srpm VERSION=fixme # # $ make rpm VERSION=fixme # # Test the candidate tarball via a scratch SRPM build in Koji (this # achieves test coverage against python 2 and 3, for both debug and # optimized python, on both i686 and x86_64, against another version of # gcc): # # $ make koji VERSION=fixme # # After successful testing of a candidate tarball: # # * push the tag: # # $ git push --tags # # * upload it to https://fedorahosted.org/releases/g/c/gcc-python-plugin/ # via: # # $ scp gcc-python-plugin-$(VERSION).tar.gz dmalcolm@fedorahosted.org:gcc-python-plugin # # * add version to Trac: https://fedorahosted.org/gcc-python-plugin/admin/ticket/versions # # * update release info at https://fedorahosted.org/gcc-python-plugin/wiki#Code # # * send release announcement: # # To: gcc@gcc.gnu.org, gcc-python-plugin@lists.fedorahosted.org, python-announce-list@python.org # Subject: ANN: gcc-python-plugin $(VERSION) # (etc) # # * build it into Fedora # Utility target, for building test rpms: srpm: rpmbuild -bs gcc-python-plugin.spec # Perform a test rpm build locally: rpm: rpmbuild -ba gcc-python-plugin.spec # Perform a test (scratch) build in Koji: # The following have been deleted from Koji: # f16 was gcc 4.6 # f17 was gcc 4.7 # f19 was gcc 4.8 koji-gcc-5: srpm koji build --scratch f23 ~/rpmbuild/SRPMS/gcc-python-plugin-$(VERSION)-1.fc20.src.rpm koji-gcc-6: srpm koji build --scratch f24 ~/rpmbuild/SRPMS/gcc-python-plugin-$(VERSION)-1.fc20.src.rpm koji: koji-gcc-5 koji-gcc-6 gcc-python-plugin-0.17/README.rst000066400000000000000000000101041342215241600164730ustar00rootroot00000000000000gcc-python ========== This is a plugin for GCC, which links against libpython, and (I hope) allows you to invoke arbitrary Python scripts from inside the compiler. The aim is to allow you to write GCC plugins in Python. The plugin is Free Software, licensed under the GPLv3 (or later). It's still at the "experimental proof-of-concept stage"; expect crashes and tracebacks (I'm new to insides of GCC, and I may have misunderstood things). It's already possible to use this to add additional compiler errors/warnings, e.g. domain-specific checks, or static analysis. One of my goals for this is to "teach" GCC about the common mistakes people make when writing extensions for CPython, but it could be used e.g. to teach GCC about GTK's reference-counting semantics, or about locking in the Linux kernel, or about signal-safety in APIs. Other ideas include visualizations of code structure. Given a ``gcc.CFG`` instance, ``gccutils.render_to_dot(cfg)`` and ``gccutils.invoke_dot(cfg)`` will use graphviz and eog to plot a handy visualization of a control flow graph, showing the source code interleaved with GCC's ``GIMPLE`` internal representation. The documentation can be seen at: http://gcc-python-plugin.readthedocs.io/en/latest/index.html Requirements ------------ * GCC: 4.6 or later (it uses APIs that weren't exposed to plugins in 4.5) * tested with 4.8, 4.9, 5, 6, 7, and 8. * GCC plugin development package: usually available in distribution packages such as ``gcc-N-plugin-dev`` or ``gcc-plugin-devel``. * Python: requires 2.7 or 3.2 or later * "six": The libcpychecker code uses the "six_" Python compatibility library to smooth over Python 2 vs Python 3 differences, both at build-time and run-time .. _six: http://pypi.python.org/pypi/six/ Usage ----- I use:: make to build the plugin and run the tests You can also use:: make demo to demonstrate the new compiler errors. Development has been on x86_64 and I don't know to what extent it will be compatible with other architectures. There isn't an installer yet. In theory you should be able to add these arguments to the gcc invocation:: gcc -fplugin=python.so -fplugin-arg-python-script=PATH_TO_SCRIPT.py OTHER_ARGS and have it run your script as the plugin starts up. The plugin automatically adds the absolute path to its own directory to the end of its `sys.path`, so that it can find support modules, such as gccutils.py and `libcpychecker`. The exact API is still in flux; you can currently connect to events by registering callbacks e.g. to be called for each function in the source at different passes. It exposes GCC's various types as Python objects, within a "gcc" module. You can see the API by running:: import gcc help(gcc) from within a script. Overview of the code -------------------- This is currently three projects in one: ``gcc-python-*``: the plugin for GCC. The entrypoint (``init_plugin``) is in ``gcc-python.c``. ``libcpychecker`` and ``cpychecker.py``: a Python library (and a driver script), written for the plugin, in which I'm building new compiler warnings to help people find bugs in CPython extension code. ``cpybuilder``: a handy module for programatically generating C source code for CPython extensions. I use this both to generate parts of the GCC plugin, and also in the selftests for the cpychecker script. (I initially attempted to use Cython for the former, but wrapping the "tree" type hierarchy required more programatic control) Coding style: Python and GCC each have their own coding style guide for C. I've chosen to follow Python's (PEP-7), as I prefer it (although my code is admittedly a mess in places). You'll find API documentation within the "docs" directory, written in the reStructuredText format (as is this file, in fact). If you have Sphinx_ installed, you can regenerate these docs using:: make html within the ``docs`` directory. Sphinx is the ``python-sphinx`` package on a Fedora/RHEL box. .. _Sphinx: http://sphinx.pocoo.org/ More detailed documentation can be seen within ``docs/getting-involved.rst``. Enjoy! David Malcolm gcc-python-plugin-0.17/configbuilder.py000066400000000000000000000143341342215241600202030ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . import os import shutil from subprocess import Popen, PIPE, check_output import sys def indent(prefix, text): text = str(text) return '\n'.join([prefix + line for line in text.splitlines()]) class ConfigurationFailure(Exception): pass class CheckFor: """ Context manager for wrapping a feature test The feature test should raise ConfigurationFailure to signify a failure """ def __init__(self, initmsg, mandatory, okmsg=None, failmsg='failed'): self.initmsg = initmsg self.mandatory = mandatory self.okmsg = okmsg self.failmsg = failmsg self.result = None # context manager hooks: def __enter__(self): sys.stdout.write('%s... ' % self.initmsg) # no newline return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: # exception occurred: self.result = False # is it one of ours, signifiying the failure of a test? if isinstance(exc_val, ConfigurationFailure): # Write the failure message: sys.stdout.write('%s\n' % self.failmsg) if self.mandatory: # Print diagnostic information: print(exc_val) # terminate the build sys.exit(1) else: return True # swallow the exception else: # some kind of unexpected error; propagate it: return False else: # Assume success: self.result = True # Write the success message: if self.okmsg: sys.stdout.write('%s\n' % self.okmsg) def succeeded(self): # Did this test succeed? return self.result class Result: pass class OptionFlag(Result): # the outcome of a feature test def __init__(self, description, flag, defn): self.description = description self.flag = flag self.defn = defn def write_to(self, f): f.write('/* %s */\n' % self.description) if self.flag: f.write('#define %s\n\n' % self.defn) else: f.write('#undef %s\n\n' % self.defn) class ConfigBuilder: def __init__(self, argv): import argparse parser = argparse.ArgumentParser() parser.add_argument('-o', '--output-file') args = parser.parse_args(argv[1:]) #print(args) self.filename = args.output_file self.dirname = 'config-tests' if os.path.exists(self.dirname): shutil.rmtree(self.dirname) os.mkdir(self.dirname) self.testid = 0 self.results = [] def make_test_dir(self, test): self.testid += 1 dirname = ('%05i-' % self.testid) + test.initmsg dirname = '-'.join(dirname.split()) dirpath = os.path.join(self.dirname, dirname) os.mkdir(dirpath) return dirpath def write_outcome(self): sys.stdout.write('writing %s\n' % self.filename) with open(self.filename, 'w') as f: f.write('/* autogenerated header file */\n\n') for r in self.results: r.write_to(f) def compile(self, test, src, extraargs): dirpath = self.make_test_dir(test) srcpath = os.path.join(dirpath, 'feature-test.c') with open(srcpath, 'w') as f: f.write(src) outpath = os.path.join(dirpath, 'feature-test.o') args= [os.environ.get('CC', 'gcc'), '-c', # don't run the linker (no main) '-o', outpath] args += extraargs args += [srcpath] p = Popen(args, stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() c = p.wait() if c != 0: class CompilationError(ConfigurationFailure): def __str__(self): result = 'Test compilation failed with exit code %i\n' % c result += ' The command was:\n' result += ' %s\n' % ' '.join(args) result += ' The source was: (in %s)\n' % srcpath result += indent(' ', src) + '\n' result += ' The stderr was:\n' result += indent(' ', stderr) + '\n' return result raise CompilationError() def capture_shell_output(self, initmsg, cmd): with CheckFor(initmsg, mandatory=True) as test: out = check_output(cmd, shell=True) # input must be trusted out = str(out.decode()) sys.stdout.write('%s\n' % out.strip()) return out def test_for_mandatory_c_header(self, header, extraargs): with CheckFor('checking for %s' % header, okmsg='found', failmsg='not found', mandatory=True) as test: self.compile(test, src='#include <%s>' % header, extraargs=extraargs) def test_c_compilation(self, initmsg, src, extraargs, description, defn): with CheckFor(initmsg, okmsg='yes', failmsg='no', mandatory=False) as test: self.compile(test, src, extraargs) self.results.append(OptionFlag(description, test.succeeded(), defn)) gcc-python-plugin-0.17/contributors.rst000066400000000000000000000000671342215241600203020ustar00rootroot00000000000000Alexandre Lissy David Malcolm David Narvaez Tom Tromey gcc-python-plugin-0.17/cpybuilder.py000066400000000000000000000462451342215241600175370ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from subprocess import Popen, PIPE import re # For the purpose of the GCC plugin, it's OK to assume that we're compiling # with GCC itself, and thus we can use GCC extensions with_gcc_extensions = True def camel_case(txt): return ''.join([word.title() for word in txt.split('_')]) def nullable_ptr(ptr): if ptr: return ptr else: return 'NULL' def simple_unaryfunc(identifier, typename, c_expression): """Define a simple unaryfunc, using a specifc PyObject subclass""" self.add_defn("static PyObject *\n" + "%s(%s *self)\n" % (identifier, typename) + "{\n" + " return %s;\n" % c_expression + "}\n\n") return identifier class NamedEntity: """A thing within C code that has an identifier""" def __init__(self, identifier): self.identifier = identifier def c_ptr_field(self, name, cast=None): if hasattr(self, name): val = getattr(self, name) else: val = None if cast: caststr = '(%s)' % cast else: caststr = '' if with_gcc_extensions: # Designate the initializer fields: return ' .%s = %s%s,\n' % (name, caststr, nullable_ptr(val)) else: return ' %s%s, /* %s */\n' % (caststr, nullable_ptr(val), name) def unaryfunc_field(self, name): return self.c_ptr_field(name, 'unaryfunc') def c_src_field(self, name): assert hasattr(self, name) val = getattr(self, name) if with_gcc_extensions: # Designate the initializer fields: return ' .%s = %s,\n' % (name, val) else: return ' %s, /* %s */\n' % (val, name) def c_src_field_value(self, name, val, cast=None): if cast: caststr = '(%s)' % cast else: caststr = '' if with_gcc_extensions: # Designate the initializer fields: return ' .%s = %s%s,\n' % (name, caststr, val) else: return ' %s%s, /* %s */\n' % (caststr, val, name) class PyGetSetDef: def __init__(self, name, get, set, doc, closure=None): self.name = name self.get = get self.set = set self.doc = doc self.closure = closure def c_defn(self): result = ' {(char*)"%s",\n' % self.name result += ' (getter)%s,\n' % nullable_ptr(self.get) result += ' (setter)%s,\n' % nullable_ptr(self.set) result += ' (char*)"%s",\n' % nullable_ptr(self.doc) result += ' %s},\n' % nullable_ptr(self.closure) return result class PyGetSetDefTable(NamedEntity): def __init__(self, identifier, gsdefs, identifier_prefix=None, typename=None): NamedEntity.__init__(self, identifier) self.gsdefs = gsdefs self.identifier_prefix = identifier_prefix self.typename = typename def c_defn(self): result = 'static PyGetSetDef %s[] = {\n' % self.identifier for gsdef in self.gsdefs: result += gsdef.c_defn() result += ' {NULL} /* Sentinel */\n' result += '};\n' return result def add_gsdef(self, name, getter, setter, doc, closure=None): self.gsdefs.append(PyGetSetDef(name, getter, setter, doc, closure=None)) def add_simple_getter(self, cu, name, c_expression, doc): assert self.identifier_prefix assert self.typename identifier = self.identifier_prefix + '_get_' + name cu.add_simple_getter(identifier, self.typename, c_expression) self.add_gsdef(name, identifier, None, doc) METH_VARARGS = 'METH_VARARGS' class PyMethodDef: def __init__(self, name, fn_name, args, docstring): self.name = name self.fn_name = fn_name #assert args in ('METH_VARARGS', ) # FIXME self.args = args self.docstring = docstring def c_defn(self): return (' {(char*)"%(name)s", %(fn_name)s, %(args)s,\n' ' (char*)"%(docstring)s"},\n' % self.__dict__) class PyMethodTable(NamedEntity): def __init__(self, identifier, methods): NamedEntity.__init__(self, identifier) self.methods = methods def c_defn(self): result = 'static PyMethodDef %s[] = {\n' % self.identifier for method in self.methods: result += method.c_defn() result += ' {NULL, NULL, 0, NULL} /* Sentinel */\n' result += '};\n' return result def add_method(self, name, fn_name, args, docstring): self.methods.append(PyMethodDef(name, fn_name, args, docstring)) # See http://docs.python.org/c-api/typeobj.html#number-structs class PyNumberMethods(NamedEntity): def __init__(self, identifier): NamedEntity.__init__(self, identifier) def c_defn(self): result = 'static PyNumberMethods %s = {\n' % self.identifier result += self.c_ptr_field('nb_add') result += self.c_ptr_field('nb_subtract') result += self.c_ptr_field('nb_multiply') result += '#if PY_MAJOR_VERSION < 3\n' result += self.c_ptr_field('nb_divide') result += '#endif\n' result += self.c_ptr_field('nb_remainder') result += self.c_ptr_field('nb_divmod') result += self.c_ptr_field('nb_power') result += self.unaryfunc_field('nb_negative') result += self.unaryfunc_field('nb_positive') result += self.unaryfunc_field('nb_absolute') result += '#if PY_MAJOR_VERSION >= 3\n' result += self.c_ptr_field('nb_bool') result += '#else\n' result += self.c_ptr_field('nb_nonzero') result += '#endif\n' result += self.unaryfunc_field('nb_invert') result += self.c_ptr_field('nb_lshift') result += self.c_ptr_field('nb_rshift') result += self.c_ptr_field('nb_and') result += self.c_ptr_field('nb_xor') result += self.c_ptr_field('nb_or') result += '#if PY_MAJOR_VERSION < 3\n' result += self.c_ptr_field('nb_coerce') result += '#endif\n' result += self.unaryfunc_field('nb_int') result += '#if PY_MAJOR_VERSION >= 3\n' result += self.c_ptr_field('nb_reserved') result += '#else\n' result += self.unaryfunc_field('nb_long') result += '#endif\n' result += self.unaryfunc_field('nb_float') result += '#if PY_MAJOR_VERSION < 3\n' result += self.unaryfunc_field('nb_oct') result += self.unaryfunc_field('nb_hex') result += '#endif\n' result += self.c_ptr_field('nb_inplace_add') result += self.c_ptr_field('nb_inplace_subtract') result += self.c_ptr_field('nb_inplace_multiply') result += '#if PY_MAJOR_VERSION < 3\n' result += self.c_ptr_field('nb_inplace_divide') result += '#endif\n' result += self.c_ptr_field('nb_inplace_remainder') result += self.c_ptr_field('nb_inplace_power') result += self.c_ptr_field('nb_inplace_lshift') result += self.c_ptr_field('nb_inplace_rshift') result += self.c_ptr_field('nb_inplace_and') result += self.c_ptr_field('nb_inplace_xor') result += self.c_ptr_field('nb_inplace_or') result += self.c_ptr_field('nb_floor_divide') result += self.c_ptr_field('nb_true_divide') result += self.c_ptr_field('nb_inplace_floor_divide') result += self.c_ptr_field('nb_inplace_true_divide') result += self.unaryfunc_field('nb_index') result += '};\n' return result def add_method(self, name, fn_name, args, docstring): self.methods.append(PyMethodDef(name, fn_name, args, docstring)) class PyTypeObject(NamedEntity): def __init__(self, identifier, localname, tp_name, struct_name, **kwargs): NamedEntity.__init__(self, identifier) self.localname = localname self.tp_name = tp_name self.struct_name = struct_name self.__dict__.update(kwargs) if not hasattr(self, 'tp_new'): self.tp_new = 'PyType_GenericNew' if not hasattr(self, 'tp_flags'): self.tp_flags = 'Py_TPFLAGS_DEFAULT' def c_defn(self): result = '\n' result += 'PyTypeObject %(identifier)s = {\n' % self.__dict__ result += self.c_initializer() result += '};\n' % self.__dict__ result +='\n' return result def c_initializer(self): if hasattr(self, 'ob_type'): ob_type_str = getattr(self, 'ob_type') else: ob_type_str = 'NULL' result = ' PyVarObject_HEAD_INIT(%s, 0)\n' % ob_type_str result += ' "%(tp_name)s", /*tp_name*/\n' % self.__dict__ result += ' sizeof(%(struct_name)s), /*tp_basicsize*/\n' % self.__dict__ result += ' 0, /*tp_itemsize*/\n' result += self.c_ptr_field('tp_dealloc') result += self.c_ptr_field('tp_print') result += self.c_ptr_field('tp_getattr') result += self.c_ptr_field('tp_setattr') result += '#if PY_MAJOR_VERSION < 3\n' % self.__dict__ result += ' 0, /*tp_compare*/\n' % self.__dict__ result += '#else\n' % self.__dict__ result += ' 0, /*reserved*/\n' % self.__dict__ result += '#endif\n' % self.__dict__ result += self.c_ptr_field('tp_repr') result += self.c_ptr_field('tp_as_number') result += self.c_ptr_field('tp_as_sequence') result += self.c_ptr_field('tp_as_mapping') result += self.c_ptr_field('tp_hash') result += self.c_ptr_field('tp_call') result += self.c_ptr_field('tp_str') result += self.c_ptr_field('tp_getattro') result += self.c_ptr_field('tp_setattro') result += self.c_ptr_field('tp_as_buffer') result += self.c_src_field('tp_flags') result += ' 0, /*tp_doc*/\n' result += self.c_ptr_field('tp_traverse') result += self.c_ptr_field('tp_clear') result += self.c_ptr_field('tp_richcompare') result += ' 0, /* tp_weaklistoffset */\n' result += self.c_ptr_field('tp_iter') result += self.c_ptr_field('tp_iternext') result += self.c_ptr_field('tp_methods') result += self.c_ptr_field('tp_members') result += self.c_ptr_field('tp_getset') result += self.c_ptr_field('tp_base', 'PyTypeObject*') result += self.c_ptr_field('tp_dict') result += self.c_ptr_field('tp_descr_get') result += self.c_ptr_field('tp_descr_set') result += ' 0, /* tp_dictoffset */\n' result += self.c_ptr_field('tp_init', 'initproc') result += self.c_ptr_field('tp_alloc') result += self.c_ptr_field('tp_new') result += self.c_ptr_field('tp_free') result += self.c_ptr_field('tp_is_gc') result += self.c_ptr_field('tp_bases') result += self.c_ptr_field('tp_mro') result += self.c_ptr_field('tp_cache') result += self.c_ptr_field('tp_subclasses') result += self.c_ptr_field('tp_weaklist') result += self.c_ptr_field('tp_del') result += '#if PY_VERSION_HEX >= 0x02060000\n' % self.__dict__ result += ' 0, /*tp_version_tag*/\n' % self.__dict__ result += '#endif\n' % self.__dict__ result += '\n' return result def c_invoke_type_ready(self): return (' if (PyType_Ready((PyTypeObject*)&%(identifier)s) < 0)\n' ' goto error;\n' '\n') % self.__dict__ def c_invoke_add_to_module(self): return (' Py_INCREF(&%(identifier)s);\n' ' PyModule_AddObject(m, "%(localname)s", (PyObject *)&%(identifier)s);\n' '\n') % self.__dict__ class PyModule: def __init__(self, modname, modmethods, moddoc): self.modname = modname self.moddoc = moddoc assert (modmethods is None) or isinstance(modmethods, PyMethodTable) self.modmethods = modmethods if self.modmethods: self.modmethods_as_ptr = self.modmethods.identifier else: self.modmethods_as_ptr = 'NULL' def c_initfn_decl(self): return (""" #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init%(modname)s(void); #else PyMODINIT_FUNC PyInit_%(modname)s(void); #endif """ % self.__dict__) def c_initfn_def_begin(self): return (""" #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init%(modname)s(void) #else PyMODINIT_FUNC PyInit_%(modname)s(void) #endif { PyObject *m = NULL; """ % self.__dict__) def c_initfn_def_end(self): return (""" #if PY_MAJOR_VERSION < 3 return; #else return m; #endif error: #if PY_MAJOR_VERSION < 3 return; #else Py_XDECREF(m); return NULL; #endif } """) def c_py3k_moduledef(self): return (""" #if PY_MAJOR_VERSION >= 3 static PyModuleDef %(modname)smodule = { PyModuleDef_HEAD_INIT, "%(modname)s", /* m_name */ "%(moddoc)s", /* m_doc */ -1, /* m_size */ %(modmethods_as_ptr)s, /* m_methods */ NULL, NULL, NULL, NULL }; #endif """ % self.__dict__) def c_invoke_ctor(self): return (""" #if PY_MAJOR_VERSION < 3 m = Py_InitModule3("%(modname)s", %(modmethods_as_ptr)s, "%(moddoc)s"); #else m = PyModule_Create(&%(modname)smodule); #endif if (!m) { goto error; } """ % self.__dict__) class CompilationUnit: """ A single C file """ def __init__(self): self._includes = '#include \n' self._prototypes = '' self._definitions = '' def add_include(self, path): self._includes += '#include "%s"\n' % path def add_decl(self, text): self._prototypes += text def add_defn(self, text): self._definitions += text def as_str(self): return ('/* Autogenerated by cpybuilder */\n' + self._includes + self.make_header('Prototypes') + self._prototypes + self.make_header('Definitions') + self._definitions) def make_header(self, text): return '\n/**** %s ****/\n\n' % text def add_simple_getter(self, identifier, typename, c_expression): """Define a simple getter, suitable for use by a PyGetSetDef""" self.add_defn("static PyObject *\n" + "%s(%s *self, void *closure)\n" % (identifier, typename) + "{\n" + " return %s;\n" % c_expression + "}\n\n") return identifier def add_simple_setter(self, identifier, typename, attrname, c_typecheck_fn, c_assignment): """Define a simple setter, suitable for use by a PyGetSetDef""" self.add_defn("static int\n" + "%s(%s *self, PyObject *value, void *closure)\n" % (identifier, typename) + "{\n" + " if (! %s(value)) {\n" % c_typecheck_fn + " PyErr_SetString(PyExc_TypeError,\n" + ' "%s must be an int");\n' % attrname + ' return -1;\n' ' }\n' + ' %s;\n' % c_assignment + " return 0;\n" "}\n\n") return identifier def add_simple_int_setter(self, identifier, typename, attrname, c_assignment): """ Define a simple setter for an int-valued attribute, suitable for use by a PyGetSetDef """ return self.add_simple_setter(identifier, typename, attrname, 'PyGccInt_Check', c_assignment) class SimpleModule: """ A C extension module built from a single C file """ def __init__(self): self.cu = CompilationUnit() self._modinit_preinit = '' self._modinit_postinit = '' def add_type_object(self, name, localname, tp_name, struct_name, **kwargs): pytype = PyTypeObject(name, localname, tp_name, struct_name, **kwargs) self.cu.add_defn(pytype.c_defn()) self._modinit_preinit += pytype.c_invoke_type_ready() self._modinit_postinit += pytype.c_invoke_add_to_module() def add_module_init(self, modname, modmethods, moddoc): pymod = PyModule(modname, modmethods, moddoc) self.cu.add_decl(pymod.c_initfn_decl()) self.cu.add_defn(pymod.c_py3k_moduledef()) self.cu.add_defn(pymod.c_initfn_def_begin()) self.cu.add_defn(self._modinit_preinit) self.cu.add_defn(pymod.c_invoke_ctor()) self.cu.add_defn(self._modinit_postinit) self.cu.add_defn(pymod.c_initfn_def_end()) class SimpleBuild: def __init__(self, sm, builddir='.'): self.sm #def generate_c(self): # with open(sm.name class CommandError(RuntimeError): def __init__(self, out, err, p): assert isinstance(out, str) assert isinstance(err, str) assert isinstance(p, Popen) self.out = out self.err = err self.p = p def __str__(self): result = '\n' result += 'returncode: %r\n' % self.p.returncode result += ' %s\n' % self._describe_activity() result += 'Stdout:\n' result += self._indent(self.out) result += '\nStderr:\n' result += self._indent(self.err, 4) result += self._extra_info() return result def _indent(self, txt, size=2): return '\n'.join([' '*size + line for line in txt.splitlines()]) def _extra_info(self): return '' class PyRuntimeError(CommandError): def __init__(self, runtime, cmd, out, err, p): CommandError.__init__(self, out, err, p) self.runtime = runtime self.cmd = cmd def _describe_activity(self): return 'running: %s -c %r' % (self.runtime.executable , self.cmd) from collections import namedtuple class PyVersionInfo(namedtuple('PyVersionInfo', 'major minor micro releaselevel serial')): @classmethod def from_text(cls, txt): # e.g.: # sys.version_info(major=2, minor=7, micro=1, releaselevel='final', serial=0) m = re.match('sys\.version_info\(major=([0-9]+), minor=([0-9]+), micro=([0-9]+), releaselevel=\'(.*)\', serial=([0-9]+)\)', txt) return PyVersionInfo(major=int(m.group(1)), minor=int(m.group(2)), micro=int(m.group(3)), releaselevel=m.group(4), serial=int(m.group(5))) gcc-python-plugin-0.17/cpychecker.py000066400000000000000000000014131342215241600175010ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . from libcpychecker import main main() gcc-python-plugin-0.17/dejagnu.py000066400000000000000000000354211342215241600170040ustar00rootroot00000000000000# Copyright 2017 David Malcolm # Copyright 2017 Red Hat, Inc. # # This 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 # . # A Python reimplementation of parts of DejaGnu import re import unittest class Directive: """ A "dg-*" directive within an input file. """ def __init__(self, inputfile, linenum, name, args): self.inputfile = inputfile self.linenum = linenum self.name = name self.args = self.parse_args(args) if len(self.args) == 4: m = re.match('\.(-?[0-9]+)', self.args[3]) offset = int(m.group(1)) self.linenum += offset @staticmethod def parse_args(args): quoted_group = '"([^"]*)"' ws = '\s+' m = re.match(quoted_group + ws + quoted_group + ws + '{(.*)}' + ws + '(.+)', args) if m: return list(m.groups()) m = re.match(quoted_group + ws + quoted_group + ws + '{(.*)}', args) if m: return list(m.groups()) m = re.match(quoted_group + ws + quoted_group, args) if m: return list(m.groups()) m = re.match(quoted_group, args) if m: return list(m.groups()) m = re.match('(\S+)', args) if m: return list(m.groups()) raise ValueError('unparseable directive args: %s' % args) def __repr__(self): return ('Directive(%r, %r, %r, %r)' % (self.inputfile, self.linenum, self.name, self.args)) class ExpectedDiagnostic: """ A dg-warning or dg-error, after parsing """ def __init__(self, kind, pattern, directive): self.kind = kind self.pattern = pattern self.directive = directive m = re.match('^([0-9]+): (.*)', self.pattern) linenum = directive.linenum if m: colnum_pattern = m.group(1) self.pattern = m.group(2) else: colnum_pattern = '[0-9]+' self.pattern = ('\S+:%i:%s: %s: %s\n' % (linenum, colnum_pattern, self.kind, self.pattern)) def __repr__(self): return ('ExpectedDiagnostic(%r, %r, %r)' % (self.kind, self.pattern, self.directive)) class ExpectedMultilineOutput: def __init__(self, directive, start, end, lines): self.directive = directive self.start = start self.end = end self.lines = lines self.pattern = ''.join([re.escape(line) + '\n' for line in self.lines]) def __repr__(self): return ('ExpectedMultilineOutput(%r, %r, %r, %r)' % (self.directive, self.start, self.end, self.lines)) class Result: """ A result of a test: a PASS/FAIL, with a message, and an optional directive that was being tested. """ def __init__(self, status, directive, message): self.status = status self.directive = directive self.message = message def __str__(self): result = '%s: ' % self.status if self.directive: result += ('%s:%i: ' % (self.directive.inputfile, self.directive.linenum)) if self.message: result += self.message return result def __repr__(self): return ('Result(%r, %r, %r)' % (self.status, self.directive, self.message)) class DgContext: """ A Python reimplementation of DejaGnu """ def __init__(self, inputfiles, verbosity=0): self.inputfiles = inputfiles self.options = [] self.verbosity = 0 self.expected_diagnostics = [] self.results = [] self.echo_results = False self._cur_multiline_output = None self.multiline_ranges = [] def parse_directives(self, inputfile): with open(inputfile, 'r') as f: lines = f.read().splitlines() for lineidx, line in enumerate(lines): directive = self.parse_line(inputfile, lineidx + 1, line) if directive: self.handle_directive(directive) def parse_line(self, inputfile, linenum, line): """ Look for line content of the form: "{ dg-FOO BAR }" """ m = re.match(r'.*{ (dg-\S+) (.+) }.*', line) if m: #print(m.groups()) return Directive(inputfile, linenum, m.group(1), m.group(2)) elif self._cur_multiline_output: self._cur_multiline_output.lines.append(line) def handle_directive(self, directive): if 0: print(directive) if directive.name == 'dg-message': self.expected_diagnostic('note', directive) elif directive.name == 'dg-options': self.options.append(directive.args[0]) elif directive.name == 'dg-do': # For now, skip dg-do pass elif directive.name == 'dg-begin-multiline-output': self.begin_multiline_output(directive) elif directive.name == 'dg-end-multiline-output': self.end_multiline_output(directive) else: self.on_fail(directive, 'unrecognized directive: %s' % directive.name) def expected_diagnostic(self, kind, directive): message = directive.args[0] ed = ExpectedDiagnostic(kind, message, directive) self.expected_diagnostics.append(ed) def begin_multiline_output(self, directive): self._cur_multiline_output = directive directive.lines = [] def end_multiline_output(self, directive): start = self._cur_multiline_output.linenum + 1 end = directive.linenum - 1 lines = self._cur_multiline_output.lines mr = ExpectedMultilineOutput(self._cur_multiline_output, start, end, lines) self.multiline_ranges.append(mr) self._cur_multiline_output = None def get_args(self): return self.options def check_result(self, stdout, stderr, exitcode): if 0: print(self.expected_diagnostics) if stdout != '': self.on_fail(None, 'non-empty stdout: %r' % stdout) # Prune stderr: if 0: print('Before pruning:\n%s' % stderr) stderr = self.prune_stderr(stderr) if 0: print('After pruning:\n%s' % stderr) if stderr != '': self.on_fail(None, 'unexpected output on stderr: %r' % stderr) # Check exitcode if exitcode != 0: self.on_fail(None, 'nonzero exit code') if 0: print(self.results) def prune_stderr(self, stderr): # Prune lines like this: # tests/plugin/rich-location/input.c: In function 'test_1': stderr = re.sub("(\S+: In function '.+':)\n", '', stderr) for d in self.expected_diagnostics: stderr, count = re.subn(d.pattern, '', stderr, 1) if count == 1: self.on_pass(d.directive, d.directive.name) else: self.on_fail(d.directive, 'diagnostic not found') for mr in self.multiline_ranges: stderr, count = re.subn(mr.pattern, '', stderr, 1) if count == 1: self.on_pass(mr.directive, 'multiline range') else: self.on_fail(mr.directive, 'multiline range not found') return stderr def on_pass(self, directive, issue): self.add_result(Result('PASS', directive, issue)) def on_fail(self, directive, issue): self.add_result(Result('FAIL', directive, issue)) def add_result(self, result): if self.echo_results: print(str(result)) self.results.append(result) def num_failures(self): count = 0 for r in self.results: if r.status == 'FAIL': count += 1 return count def uses_dg_directives(inputfiles): for inputfile in inputfiles: with open(inputfile, 'r') as f: code = f.read() if 'dg-do' in code: return True class Tests(unittest.TestCase): def test_parse_line(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) d = ctxt.parse_line(INPUT_FILE, 42, 'before /* { dg-something "arg1" "arg2" } */ after') self.assertEqual(d.inputfile, INPUT_FILE) self.assertEqual(d.linenum, 42) self.assertEqual(d.name, 'dg-something') self.assertEqual(d.args, ['arg1', 'arg2']) def test_nonempty_stdout(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) ctxt.check_result('stray text', '', 0) self.assertEqual(len(ctxt.results), 1) self.assertEqual(ctxt.results[0].status, 'FAIL') self.assertEqual(ctxt.results[0].message, "non-empty stdout: 'stray text'") self.assertEqual(ctxt.num_failures(), 1) def test_surplus_errors(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) ctxt.check_result('', 'stray text', 0) self.assertEqual(len(ctxt.results), 1) self.assertEqual(ctxt.results[0].status, 'FAIL') self.assertEqual(ctxt.results[0].message, "unexpected output on stderr: 'stray text'") self.assertEqual(ctxt.num_failures(), 1) def test_dg_message_found(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) d = ctxt.parse_line(INPUT_FILE, 23, 'before /* { dg-message "17: hello world" } */ after') ctxt.handle_directive(d) self.assertEqual(len(ctxt.expected_diagnostics), 1) ed = ctxt.expected_diagnostics[0] self.assertEqual(ed.kind, 'note') self.assertEqual(ed.pattern, '\\S+:23:17: note: hello world\n') ctxt.check_result('', INPUT_FILE + ':23:17: note: hello world\n', 0) self.assertEqual(len(ctxt.results), 1) self.assertEqual(ctxt.results[0].status, 'PASS') self.assertEqual(ctxt.results[0].message, 'dg-message') self.assertEqual(ctxt.num_failures(), 0) def test_dg_message_not_found(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) d = ctxt.parse_line(INPUT_FILE, 23, 'before /* { dg-message "17: hello world" } */ after') ctxt.handle_directive(d) self.assertEqual(len(ctxt.expected_diagnostics), 1) # Incorrect line/column: ctxt.check_result('', INPUT_FILE + ':24:18: note: hello world\n', 0) self.assertEqual(len(ctxt.results), 2) self.assertEqual(ctxt.results[0].status, 'FAIL') self.assertEqual(ctxt.results[0].message, 'diagnostic not found') self.assertEqual(ctxt.results[1].status, 'FAIL') self.assertEqual(ctxt.results[1].message, "unexpected output on stderr: 'foo.c:24:18: note: hello world\\n'") self.assertEqual(ctxt.num_failures(), 2) def test_directive_with_full_args(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) d = ctxt.parse_line(INPUT_FILE, 23, '/* { dg-message "14: hello world" "title" { target *-*-* } .-1 } */') self.assertEqual(d.inputfile, INPUT_FILE) self.assertEqual(d.linenum, 22) self.assertEqual(d.name, 'dg-message') self.assertEqual(d.args, ['14: hello world', 'title', ' target *-*-* ', '.-1']) ctxt.handle_directive(d) self.assertEqual(len(ctxt.expected_diagnostics), 1) ed = ctxt.expected_diagnostics[0] self.assertEqual(ed.kind, 'note') self.assertEqual(ed.pattern, '\\S+:22:14: note: hello world\n') self.assertEqual(len(ctxt.expected_diagnostics), 1) ctxt.check_result('', INPUT_FILE + ':22:14: note: hello world\n', 0) self.assertEqual(ctxt.num_failures(), 0) def test_directive_with_full_args_2(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) d = ctxt.parse_line(INPUT_FILE, 23, '/* { dg-message "14: hello world" "" { target *-*-* } .-1 } */') self.assertEqual(d.inputfile, INPUT_FILE) self.assertEqual(d.linenum, 22) self.assertEqual(d.name, 'dg-message') self.assertEqual(d.args, ['14: hello world', '', ' target *-*-* ', '.-1']) ctxt.handle_directive(d) self.assertEqual(len(ctxt.expected_diagnostics), 1) ed = ctxt.expected_diagnostics[0] self.assertEqual(ed.kind, 'note') self.assertEqual(ed.pattern, '\\S+:22:14: note: hello world\n') self.assertEqual(len(ctxt.expected_diagnostics), 1) ctxt.check_result('', INPUT_FILE + ':22:14: note: hello world\n', 0) self.assertEqual(ctxt.num_failures(), 0) def test_dg_options(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) d = ctxt.parse_line(INPUT_FILE, 23, '/* { dg-options "-fdiagnostics-show-caret" } */') ctxt.handle_directive(d) self.assertEqual(ctxt.options, ['-fdiagnostics-show-caret']) def test_dg_do(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) d = ctxt.parse_line(INPUT_FILE, 23, '/* { dg-do compile } */') ctxt.handle_directive(d) self.assertEqual(d.args, ['compile']) def test_multiline_ranges(self): INPUT_FILE = 'foo.c' ctxt = DgContext([INPUT_FILE]) lines = """ /* { dg-begin-multiline-output "" } { return foo + bar; } ~~~~^~~~~ { dg-end-multiline-output "" } */ """ for lineidx, line in enumerate(lines.splitlines()): directive = ctxt.parse_line(INPUT_FILE, lineidx + 1, line) if directive: ctxt.handle_directive(directive) self.assertEqual(len(ctxt.multiline_ranges), 1) mr = ctxt.multiline_ranges[0] self.assertEqual(mr.start, 3) self.assertEqual(mr.end, 4) self.assertEqual(mr.lines, [' { return foo + bar; }', ' ~~~~^~~~~']) stderr = (' { return foo + bar; }\n' + ' ~~~~^~~~~\n') ctxt.check_result('', stderr, 0) self.assertEqual(ctxt.num_failures(), 0) if __name__ == '__main__': unittest.main() gcc-python-plugin-0.17/demo.c000066400000000000000000000047271342215241600161120ustar00rootroot00000000000000/* Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . */ /* Examples of mistakes made using the Python API */ #include extern uint16_t htons(uint16_t hostshort); #if PY_MAJOR_VERSION >= 3 #define PYINT_FROMLONG(l) (PyLong_FromLong(l)) #else #define PYINT_FROMLONG(l) (PyInt_FromLong(l)) #endif PyObject * socket_htons(PyObject *self, PyObject *args) { unsigned long x1, x2; if (!PyArg_ParseTuple(args, "i:htons", &x1)) { return NULL; } x2 = (int)htons((short)x1); return PYINT_FROMLONG(x2); } PyObject * not_enough_varargs(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "i")) { return NULL; } Py_RETURN_NONE; } PyObject * too_many_varargs(PyObject *self, PyObject *args) { int i, j; if (!PyArg_ParseTuple(args, "i", &i, &j)) { return NULL; } Py_RETURN_NONE; } PyObject * kwargs_example(PyObject *self, PyObject *args, PyObject *kwargs) { double x, y; char *keywords[] = {"x", "y"}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "(ff):kwargs_example", keywords, &x, &y)) { return NULL; } Py_RETURN_NONE; } extern int convert_to_ssize(PyObject *, Py_ssize_t *); PyObject * buggy_converter(PyObject *self, PyObject *args) { int i; if (!PyArg_ParseTuple(args, "O&", convert_to_ssize, &i)) { return NULL; } Py_RETURN_NONE; } PyObject * make_a_list_of_random_ints_badly(PyObject *self, PyObject *args) { PyObject *list, *item; long count, i; if (!PyArg_ParseTuple(args, "i", &count)) { return NULL; } list = PyList_New(0); for (i = 0; i < count; i++) { item = PyLong_FromLong(random()); PyList_Append(list, item); } return list; } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/demo.expected000066400000000000000000000020301342215241600174520ustar00rootroot00000000000000demo.c: In function ‘socket_htons’: demo.c:36:: warning: Mismatching type in call to PyArg_ParseTuple with format code "i:htons" demo.c: In function ‘not_enough_varargs’: demo.c:46:: warning: Not enough arguments in call to PyArg_ParseTuple with format string "i" demo.c: In function ‘too_many_varargs’: demo.c:56:: warning: Too many arguments in call to PyArg_ParseTuple with format string "i" demo.c: In function ‘kwargs_example’: demo.c:68:: warning: Mismatching type in call to PyArg_ParseTupleAndKeywords with format code "(ff):kwargs_example" demo.c:68:: warning: Mismatching type in call to PyArg_ParseTupleAndKeywords with format code "(ff):kwargs_example" demo.c: In function ‘buggy_converter’: demo.c:82:: warning: Mismatching type in call to PyArg_ParseTuple with format code "O&" demo.c: In function ‘make_a_list_of_random_ints_badly’: demo.c:96:: warning: Mismatching type in call to PyArg_ParseTuple with format code "i" demo.c:104:: warning: calling PyList_Append with NULL as argument 1 (list) at demo.c:104 gcc-python-plugin-0.17/demo.expected.no-refcounts000066400000000000000000000016771342215241600221130ustar00rootroot00000000000000demo.c: In function ‘socket_htons’: demo.c:36:: warning: Mismatching type in call to PyArg_ParseTuple with format code "i:htons" demo.c: In function ‘not_enough_varargs’: demo.c:46:: warning: Not enough arguments in call to PyArg_ParseTuple with format string "i" demo.c: In function ‘too_many_varargs’: demo.c:56:: warning: Too many arguments in call to PyArg_ParseTuple with format string "i" demo.c: In function ‘kwargs_example’: demo.c:68:: warning: Mismatching type in call to PyArg_ParseTupleAndKeywords with format code "(ff):kwargs_example" demo.c:68:: warning: Mismatching type in call to PyArg_ParseTupleAndKeywords with format code "(ff):kwargs_example" demo.c: In function ‘buggy_converter’: demo.c:82:: warning: Mismatching type in call to PyArg_ParseTuple with format code "O&" demo.c: In function ‘make_a_list_of_random_ints_badly’: demo.c:96:: warning: Mismatching type in call to PyArg_ParseTuple with format code "i" gcc-python-plugin-0.17/docs/000077500000000000000000000000001342215241600157405ustar00rootroot00000000000000gcc-python-plugin-0.17/docs/0.10.rst000066400000000000000000000142651342215241600170600ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . .. covers up to 9f38feba0c69add5a39b69faba528de955ff71a2 0.10 ~~~~ Thanks to Buck Golemon, Daniele Varrazzo, David Narvaez, Eevee, Jason Mueller, Kevin Pyle, Matt Rice and Tom Tromey for their contributions to this release. Changes to the GCC Python Plugin ================================ * The plugin can now be used with Python 3.3 (fixing Unicode issues and dict-ordering assumptions). * The plugin now exposes inline assembler to Python scripts via :py:class:`gcc.GimpleAsm`. * There is a new `gccutils.sorted_callgraph()` function to get the callgraph in topologically-sorted order. * The test suite has been reworked to fix issues with checkouts on OS X case-insensitive filesystems. * C++ support: support for locating the global namespace (aka "::"), for locating declarations and child namespaces within a namespace, and aliases. * :py:class:`gcc.Declaration` now has an is_builtin attribute * Numerous improvements to the plugin's Makefile Improvements to :doc:`gcc-with-cpychecker ` ======================================================== * By default, the refcount checker is now only run on code that includes (implemented by checking if the "PyObject" typedef exists). This greatly speeds up compilation of large projects for which the Python extension modules are only a small subset of the source tree. * Added some custom attributes for marking functions that set an exception, either always, or when returning a negative value:: __attribute__((cpychecker_negative_result_sets_exception)) __attribute__((cpychecker_sets_exception)) * Improve descriptions of ranges: rather than emitting descriptions with the rather vague "value", such as:: when considering range: 1 <= value <= 0x7fffffff instead try to embed a descriptive name for the value, such as:: when considering range: 1 <= n <= 0x7fffffff Mass recompile of Fedora 17's Python extension code --------------------------------------------------- I ran the reference-count checker on all of the C/C++ Python extension modules in Fedora 17 and `reported hundreds of genuine problems `_, many of which have been fixed. In the process of doing this I found and fixed many problems in the checker itself. For example: * the checker now understand's GCC's `__builtin_expect`, fixing various false reports about dereferencing NULL pointers when running the checker on Cython-generated code in python-lxml-2.3 * added descriptions of part of SWIG and Cython's internal APIs to suppress some false positives seen with SWIG and Cython-generated code. * tweak the refcount rules to fix some false positives where the checker erroneously considered the case of a deallocation by: .. code-block:: c Py_DECREF(obj); where "obj" provably had other references not owned by the function being analyzed, and thus for the case where obj->ob_refcnt > 1 the deallocation could not happen. The plugin also now has a triaging script which can examine all of the errors within a build and provide a report, showing all of them in prioritized categories. The source tree now contains helper scripts for conducting such a mass recompile. Pyscopg support --------------- Daniele Varrazzo used the checker extensively on `psycopg `_, the popular Python interface to `PostgreSQL `_, and was able to find and fix numerous subtle errors: * https://fedorahosted.org/pipermail/gcc-python-plugin/2012-March/000229.html * http://initd.org/psycopg/articles/2012/03/29/psycopg-245-released/ Experimental new error visualization ------------------------------------ The checker can now dump its internal representation in JSON form, via a new `--dump-json` option, and an experimental new renderer can generate HTML from this. An example can be seen here: http://fedorapeople.org/~dmalcolm/gcc-python-plugin/2012-03-19/example/example.html This is still a work-in-progress C++ support ----------- The checker is now able to run on C++ code: support has been added for methods, references, "this", destructors, the :py:class:`gcc.GimpleNop` operation. Coverage of the CPython API --------------------------- The format code handling for Py_BuildValue was missing support for the following codes: * 'u' and 'u#' * 'f' and 'd' * 'D' * 'c' In addition, the handling for 's#' and 'z#' had a bug in which it erroneously expected an int* or Py_ssize_t*, rather than just a int or Py_ssize_t. This release fixes these issues, and gives full coverage of all valid format codes for Py_BuildValue in Python 2. This release adds heuristics for the behavior of the following CPython API entrypoints: * PyCode_New * PyCObject_FromVoidPtrAndDesc * PyDict_Size * PyErr_Clear * PyEval_CallMethod * Py_FatalError * PyFile_SoftSpace, PyFile_WriteObject, and PyFile_WriteString * PyFloat_AsDouble and PyFloat_FromDouble * PyFrame_New * Py_GetVersion * PyImport_AddModule * PyIter_Next * PyNumber_Int, PyNumber_Remainder * PyObject_CallObject, PyObject_GetAttr, PyObject_GetAttrString, PyObject_GetItem, PyObject_SetAttr, and PyObject_SetAttrString * PyOS_snprintf * PyString_InternFromString * PySequence_Concat, PySequence_GetSlice, PySequence_SetItem, PySequence_Size * PySys_GetObject * PyTraceBack_Here * PyTuple_GetItem * PyUnicodeUCS4_DecodeUTF8 * PyWeakref_GetObject along with various other bugfixes. gcc-python-plugin-0.17/docs/0.11.rst000066400000000000000000000053541342215241600170600ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . .. this covers up to 9bfec18f7eebf9af2dde8b8db132cf97c7d9c17d 0.11 ~~~~ Changes to the GCC Python Plugin ================================ The main change in this release is support for compiling the plugin with a C++ compiler. Recent versions of GCC 4.7 are now built with C++ rather than C, meaning that plugins must also be built with C++ (since all of GCC's internal symbols are name-mangled). This release fixes the plugin's Makefile so that it autodetects whether the plugin needs to be built with a C or C++ compiler and (I hope) does the right thing automatically. I've also made the necessary changes to the C source code of the plugin so that it is compilable as either language. This should enable the plugin to now be usable with recent builds of gcc 4.7.* (along with gcc 4.6). The plugin doesn't yet support gcc 4.8 prereleases. Other fixes: * there is now a unique :py:class:`gcc.Edge` wrapper for each underlying edge in GCC's control flow graphs, rather than the old erroneous behavior of having multiple identical duplicate wrappers. * fixed missing documentation for :py:class:`gcc.SsaName`, and :py:class:`gcc.Edge`'s `true_value` and `false_value` flags Internal improvements to :doc:`gcc-with-cpychecker ` ================================================================= The CPython static analysis code shipped with the plugin contains a detailed description of the behavior of the `CPython API `_ (e.g. which arguments will lead to a segfault if NULL, and why; the possible outcomes of a call and their impact on reference-counts; etc). However, these descriptions were tightly bound to implementation details of the checker. This release introduces a new internal API to the analyzer for describing the possible behaviors of CPython API entrypoints, in an attempt to decouple these descriptions from the checker, and ports many of the descriptions to using it. These changes shouldn't be visible to users of the checker, but should make future maintenance much easier. gcc-python-plugin-0.17/docs/0.12.rst000066400000000000000000000101331342215241600170500ustar00rootroot00000000000000.. Copyright 2013 David Malcolm Copyright 2013 Red Hat, Inc. This 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 . .. this covers up to cfd1a892b8e47ea18b757611c83528b22d85f4ce 0.12 ~~~~ Changes to the GCC Python Plugin ================================ GCC 4.8 compatibility --------------------- This release of the plugin adds support for gcc 4.8 (along with continued support for gcc 4.7 and gcc 4.6). gcc-c-api --------- The source tree contains a new component: gcc-c-api. This provides a wrapper library `libgcc-c-api.so` that hides much of the details of GCC's internals (such as the binary layout of structures, and the differences between GCC 4.6 through 4.8). I plan for this to eventually be its own project, aiming at providing a stable API and ABI for working with GCC, once it has proven itself in the context of the python plugin. The API provides an XML description of itself, which should greatly simplify the job of generating bindings for accessing GCC internals from other languages. Link-Time Optimization support ------------------------------ The plugin can now be used with GCC's Link-Time Optimization feature (LTO), allowing whole-program visualizations and analysis. For example, you can rendering a whole-program "supergraph" of control flow graphs using this invocation: .. code-block:: bash $ ./gcc-with-python \ examples/show-lto-supergraph.py \ -flto \ -flto-partition=none \ tests/examples/lto/input-*.c which will render a bitmap of the supergraph like this: .. figure:: sample-supergraph.png :scale: 50 % :alt: image of a supergraph API improvements ---------------- Sane repr() implementations have been added to the following classes: :py:class:`gcc.CaseLabelExpr` :py:class:`gcc.GimpleLabel` :py:class:`gcc.BasicBlock` :py:class:`gcc.SsaName` :py:class:`gcc.ArrayRef` :py:class:`gcc.ComponentRef` :py:class:`gcc.PointerType` :py:class:`gcc.IntegerType` :py:class:`gcc.Location` :py:class:`gcc.Location` instances can now be compared and sorted. They are ordered alphabetically by file, then by line number, then by column) Other fixes ----------- * the Makefile has a "make install" target (at last) * prevent forkbomb when running with CC=gcc-with-cpychecker * fixed memory leak within :py:meth:`gcc.Gimple.walk_tree` * ensure that the result of :py:attr:`gcc.Cfg.basic_blocks` can't contain any `None` items (which used to sometimes happen when certain optimizations had occurred). * run-test-suite.py now has a --show option, giving more verbose information on what the test suite is doing * fix hashing and equality for :py:class:`gcc.Function` and :py:class:`gcc.Gimple` * fix :py:meth:`gcc.IntegerCst.__hash__` and ensure it compares sanely against `int` * ensure that equivalent :py:class:`gcc.ComponentRef` objects have the same hash and are equal * ensure there is a unique gcc.CallgraphEdge for each underlying edge, and a unique gcc.Cfg for each underlying control flow graph * add a "label" attribute to gcc.GimpleLabel * add :py:data:`gcc.GCC_VERSION` Internal improvements to :doc:`gcc-with-cpychecker ` ================================================================= * fix exception on pointer comparisons * fix exception on int-to-float casts * fix traceback when analyzing a callsite that discards the LHS when an Outcome.returns() a value * fix two different exceptions when casting an integer value to a pointer * add example of refcounting bugs to "make demo" * fix a traceback seen on bogus uses of Py_XDECREF() gcc-python-plugin-0.17/docs/0.13.rst000066400000000000000000000077741342215241600170720ustar00rootroot00000000000000.. Copyright 2014 David Malcolm Copyright 2014 Red Hat, Inc. This 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 . .. this covers up to ef48966bc952d2af637e6a34e92846af2c2210bf 0.13 ~~~~ The major features in this release are: * gcc 4.9 compatibility * a major revamping to the HTML output from gcc-with-cpychecker New dependency: ``lxml``. The new HTML output format uses lxml internally. Changes to the GCC Python Plugin ================================ GCC 4.9 compatibility --------------------- This release of the plugin adds support for gcc 4.9 (along with continued support for gcc 4.6, 4.7 and gcc 4.8). Building against 4.9 requires a GCC 4.9 with the fix for `GCC bug 63410 `_ applied. Other fixes ----------- * fixed a build-time incompatibility with Python 3.3.0 * various internal bug fixes: * bug in garbage-collector integration (https://bugzilla.redhat.com/show_bug.cgi?id=864314) * the test suite is now parallelized (using multiprocessing) * improvements to Makefile * improvements to documentation * add gcc.Location.in_system_header attribute Improvements to :doc:`gcc-with-cpychecker ` ======================================================== The major improvement to :doc:`gcc-with-cpychecker ` is a big revamp of the output. A new "v2" HTML report is available, written to ``SOURCE_NAME.v2.html`` e.g. ``demo.c.v2.html``: .. figure:: new-html-error-report.png :alt: screenshot of the new kind of HTML report The new HTML report is easier to read in the presence of complicated control flow. It also include links to the API documentation for calls made to the CPython API. For both old-style and new-style reports, the wording of the messages has been clarified: * Reference-count tracking messages now largely eliminate the ``0 + N where N >=`` gobbledegook, since this was confusing to everyone (including me). Instead, error reports talk about references as owned vs borrowed references e.g. * "refs: 1 owned" * "refs: 0 owned 1 borrowed" resorting to ranges:: refs: 0 owned + B borrowed where 1 <= B <= 0x80000000 only where necessary. * Reports now add ``memory leak:`` and ``future use-after-free:`` prefixes where appropriate, to better indicate the issue. * Objects are referred to more in terms the user is likely to understand e.g. ``*dictA`` rather than ``PyDictObject``. The checker also reports better source locations in its messages e.g. in the presence of multiple ``return`` statements (https://fedorahosted.org/gcc-python-plugin/ticket/58). .. The v2 html output was first added in 0.10, but was "experimental", and required hacking to access. Other improvements ------------------ * Add a new test script: `tests/examples/find-global-state`, showing examples of finding global state in the code being compiled. * handle :c:func:`PySequence_DelItem` * fix bug in handling of :c:func:`PyRun_SimpleStringFlags` * fix issue with handling of :c:func:`PyArg_ParseTuple` (https://fedorahosted.org/gcc-python-plugin/ticket/50) * although we don't model the internals of C++ exceptions, fix things so we don't crash with a traceback in the absense of ``-fno-exceptions`` (https://fedorahosted.org/gcc-python-plugin/ticket/51) Contributors ============ Thanks to Buck Golemon, Denis Efremov, Philip Herron, and Tom Tromey for their contributions to this release. gcc-python-plugin-0.17/docs/0.14.rst000066400000000000000000000015041342215241600170540ustar00rootroot00000000000000.. Copyright 2015 David Malcolm Copyright 2015 Red Hat, Inc. This 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 . 0.14 ~~~~ This releases adds support for gcc 5 (along with continued support for gcc 4.6, 4.7, 4.8 and 4.9). gcc-python-plugin-0.17/docs/0.15.rst000066400000000000000000000023361342215241600170610ustar00rootroot00000000000000.. Copyright 2016 David Malcolm Copyright 2016 Red Hat, Inc. This 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 . 0.15 ~~~~ This releases adds support for gcc 6 (along with continued support for gcc 4.6, 4.7, 4.8, 4.9 and 5). Additionally, this release contains the following improvements (contributed by Tom Tromey; thanks Tom): * document :py:data:`gcc.PLUGIN_FINISH_TYPE` * document :py:class:`gcc.EnumeralType`; add 'values' attribute * add unqualified_equivalent to gcc.Type subclasses * preserve qualifiers when adding more qualifiers * fix include for gcc 4.9.2 * handle variadic function types gcc-python-plugin-0.17/docs/0.16.rst000066400000000000000000000024421342215241600170600ustar00rootroot00000000000000.. Copyright 2018 David Malcolm Copyright 2018 Red Hat, Inc. This 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 . 0.16 ~~~~ This releases adds support for gcc 7 and gcc 8 (along with continued support for gcc 4.6, 4.7, 4.8, 4.9, 5 and 6). The upstream location for the plugin has moved from fedorahosted.org to https://github.com/davidmalcolm/gcc-python-plugin Additionally, this release contains the following improvements: * add :py:class:`gcc.RichLocation` for GCC 6 onwards * :py:class:`gcc.Location` * add :py:attr:`~gcc.Location.caret`, :py:attr:`~gcc.Location.start`, :py:attr:`~gcc.Location.finish` attributes for GCC 7 onwards * add :py:meth:`gcc.Location.offset_column` method gcc-python-plugin-0.17/docs/0.17.rst000066400000000000000000000023201342215241600170540ustar00rootroot00000000000000.. Copyright 2019 David Malcolm Copyright 2019 Red Hat, Inc. This 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 . 0.17 ~~~~ This releases adds support for gcc 9 (along with continued support for gcc 4.6, 4.7, 4.8, 4.9, 5, 6, 7, and 8). Unfortunately, the reference-count checker no longer works for gcc 7 onwards, so it is disabled when embedded in those builds of gcc. Additionally, this release contains the following improvements: * the plugin can now be built in a separate build directory from the source directory (thanks to Tom de Vries) * gcc-with-cpychecker gained a :option:`--cpychecker-verbose` option gcc-python-plugin-0.17/docs/0.7.rst000066400000000000000000000263111342215241600170010ustar00rootroot000000000000000.7 ~~~ This is a major update to the GCC Python plugin. The main example script, cpychecker, has seen numerous improvements, and has now detected many reference-counting bugs in real-world CPython extension code. The usability and signal:noise ratio is greatly improved over previous releases. Changes to the GCC Python Plugin ================================ It's now possible to create custom GCC attributes from Python, allowing you to add custom high-level annotation to a C API, and to write scripts that will verify these properties. It's also possible to inject preprocessor macros from Python. Taken together, this allows code like this: .. literalinclude:: ../tests/examples/attributes-with-macros/input.c :lines: 22-45 :language: c Other improvements: * gcc's debug dump facilities are now exposed via a Python API * it's now possible to run Python commands in GCC (rather than scripts) using -fplugin-arg-python-command * improvements to the "source location" when reporting on an unhandled Python exception. Amongst other tweaks, it's now possible for a script to override this, which the cpychecker uses, so that if it can't handle a particular line of C code, the GCC error report gives that location before reporting the Python traceback (making debugging much easier). * "switch" statements are now properly wrapped at the Python level (gcc.GimpleSwitch) * C bitfields are now wrapped at the Python level * gcc.Type instances now have a "sizeof" attribute, and an "attributes" attribute. * added a gcc.Gimple.walk_tree method, to make it easy to visit all nodes relating to a statement * added a new example: spell-checking all string literals in code Improvements to "cpychecker" ============================ The "libcpychecker" Python code is a large example of using the plugin: it extends GCC with code that tries to detect various bugs in CPython extension modules. The cpychecker analyzes the paths that can be followed through a C function, and verifies various properties, including reference-count handling. As of this release, the pass has found many reference-counting bugs in real-world code. You can see a list of the bugs that it has detected at: http://gcc-python-plugin.readthedocs.org/en/latest/success.html The checker is now *almost* capable of fully handling the C code within the gcc python plugin itself. The checker has also been reorganized to (I hope) make it easy to add checking for other libraries and APIs. Major rewrite of reference-count tracking ----------------------------------------- I've rewritten the internals of how reference counts are tracked: the code now makes a distinction betweeen all of the reference that can be analysed within a single function, versus all of the other references that may exist in the rest of the program. This allows us to know for an object e.g. that the function doesn't directly own any references, but that the reference count is still > 0 (a "borrowed reference"), as compared to the case where the function owns a reference, but we don't know of any in the rest of the program (this is typical when receiving a "new reference" e.g. from a function call to a constructor). Within the reference-count checker, we now look for memory locations that store references to objects. If those locations not on the stack, then the references they store are now assumed to legally count towards the ob_refcnt that the function "owns". This is needed in order to correctly handle e.g. the PyList_SET_ITEM() macro, which directly writes to the list's ob_item field, "stealing" a reference: we can detect these references, and count them towards the ob_refcnt value. The checker can now detect types that look like PyObject subclasses at the C level (by looking at the top-most fields), and uses this information in various places. The checker now exposes custom GCC attributes allowing you to mark APIs that have non-standard reference-handling behavior: .. code-block:: c PyObject *foo(void) CPYCHECKER_RETURNS_BORROWED_REF; extern void bar(int i, PyObject *obj, int j, PyObject *other) CPYCHECKER_STEALS_REFERENCE_TO_ARG(2) CPYCHECKER_STEALS_REFERENCE_TO_ARG(4); It also exposes an attribute allowing you to the run-time and compile-time type information for a Python extension class: .. code-block:: c /* Define some PyObject subclass, as both a struct and a typedef */ struct OurObjectStruct { PyObject_HEAD /* other fields */ }; typedef struct OurObjectStruct OurExtensionObject; /* Declare the PyTypeObject, using the custom attribute to associate it with the typedef above: */ extern PyTypeObject UserDefinedExtension_Type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("OurExtensionObject"); Function calls with NULL-pointer arguments ------------------------------------------ The checker knows about various CPython API hooks that will crash on NULL pointer arguments, and will emit warnings when it can determine a path through the code that will lead to a definite call with a NULL value. Dereferences of uninitialized pointers -------------------------------------- The checker will now complain about paths through a function for which it can prove that an uninitialized pointer will be dereferenced. Error-reporting improvements ---------------------------- The error-reporting machinery can generate HTML reports: see e.g.: http://readthedocs.org/docs/gcc-python-plugin/en/latest/cpychecker.html#reference-count-checking and http://dmalcolm.livejournal.com/6560.html The checker can now annotate its HTML (and textual) reports with information showing how some pertinent aspect of the program's state changes during a particular path through a function. For example, when reporting on reference-counting errors, the HTML report showing the flow through the function will now display all changes to an object's ob_refcnt, together with all changes to what the value ought to be (e.g. due to pointers being stored to persistent memory locations): .. figure:: sample-html-error-report.png :alt: screenshot of the HTML report Similarly, when reporting on exception-handling errors, it now displays the "history" of changes to the thread-local exception state. There's also a debug mode which dumps _everything_ that changes within the report, which is helpful for debugging the checker itself. The error report will attempt to use the most representative name for a leaked object, using a variable name or a C expression fragment as appropriate. The checker will attempt to combine duplicate error reports, so that it will only emit one error for all of the various traces of execution that exhibit a particular reference-counting bug. Finally, when writing out an HTML report, the path to the HTML is now noted within gcc's regular stderr messages. Signal:noise ratio improvements ------------------------------- To suppress various false-positives that I commonly ran into on real code, the checker now makes certain assumptions: * When encountering an unknown function that returns a PyObject*, the checker assumes that it will either return a new reference to a sane object (with a sane ob_type), or return NULL and set the thread-local exception state. * The checker assumes that a PyObject* argument to a function is non-NULL and has a >0 refcount, and has a sane ob_type (e.g. with a sane refcount and tp_dealloc) * When dereferencing a pointer that it has no knowledge about (e.g. a pointer field in a structure), the checker now assumes that it's non-NULL, unless it knows that NULL is a definite possibility i.e. it optimistically assumes that you know what you're doing (this could be turned into a command-line option). Note that for the cases where we know that the pointer can _definitely_ be NULL, an error will still be reported (e.g. when considering the various possible return values for a function known to be able to return NULL). Coverage of the CPython API --------------------------- I've gone through much of the CPython API, "teaching" the checker about the reference-count semantics of each API call (and which calls will crash if fed a NULL pointer). This involves writing a simple fragment of Python code for each function, which describes the various different affects that the call can have on the internal state within the callee. This release adds support for calls to the following: * _PyObject_New * Py_{Initialize|Finalize} * Py_InitModule4 * PyArg_ParseTuple[AndKeywords], and the PY_SSIZE_T_CLEAN variants (only partial coverage so far: "O", "O!" should work though) * PyArg_UnpackTuple * PyBool_FromLong * Py_BuildValue and the PY_SSIZE_T_CLEAN variant (only partial coverage so far) * PyDict_{GetItem,GetItemString,New,SetItem,SetItemString} * PyErr_{Format,NoMemory,Occurred,Print,PrintEx,SetFromErrno[WithFilename], SetObject,SetString} * PyEval_InitThreads * PyGILState_{Ensure,Release} * PyImport_{AppendInittab,ImportModule} * PyInt_{AsLong,FromLong} * PyList_Append * PyLong_{FromString,FromVoidPtr} * PyMem_{Malloc,Free} * PyModule_Add{IntConstant,Object,StringConstant} * PyObject_{Call,CallMethod,HasAttrString,IsTrue,Repr,Str} * PyRun_{SimpleFileExFlags,SimpleStringFlags} * PySequence_GetItem * PyString_{AsString,FromFormat,FromString,FromStringAndSize} * PyStructSequence_{InitType,New} * PySys_SetObject * PyTuple_{New,SetItem,Size} * PyType_{IsSubtype,Ready} I've been targetting those API entrypoints that I use myself in the plugin; this is one area which is particularly amenable to patching, for anyone who wants to get involved. I've also added a (disabled) hook that complains about Python API entrypoints that weren't explicitly handled, to make it easy to find gaps in our coverage of the CPython API. Other user-visible improvments ------------------------------ * There's now a "gcc-with-cpychecker" harness, to make it easier to invoke GCC with the cpychecker code from e.g. Makefiles * The checker now respects `__attribute((nonnull))` on function arguments when detecting NULL pointers * Handle functions that don't return (e.g. "exit") * Number the unknown heap regions, to clarify things when there's more than one Internal improvements --------------------- * The cpychecker now has some protection against combinatorial explosion for functions that have very large numbers of possible routes through them. For such functions, the checker will emit a note on stderr and not attempt to find reference-counting bugs in the function. * The cpychecker is now done as a custom pass (rather than by wiring up a callback associated with every pass) * I've tuned the logging within the checker, eliminating some CPU/memory consumption issues seen when analysing complicated C code. In particular, the log message arguments are now only expanded when logging is enabled (previously this was happening all the time). * Lots of other internal improvements and bug fixes (e.g. handling of arrays vs pointers, static vs auto local variables, add missing handlers for various kinds of C expression, lots of work on improving the readability of error messages) gcc-python-plugin-0.17/docs/0.8.rst000066400000000000000000000207761342215241600170130ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . 0.8 ~~~ Thanks to David Narvaez and Tom Tromey for their code contributions to this release. Changes to the GCC Python Plugin ================================ Initial C++ support ------------------- This release adds the beginnings of C++ support: :py:class:`gcc.FunctionDecl` instances now have a "fullname" attribute, along with "is_public", "is_private", "is_protected", "is_static" booleans. For example, given this code: .. code-block:: c++ namespace Example { struct Coord { int x; int y; }; class Widget { public: void set_location(const struct Coord& coord); }; }; `set_location`'s fullname is:: 'void Example::Widget::set_location(const Example::Coord&)' This is only present when the plugin is invoked from the C++ frontend (`cc1plus`), gracefully handling the case when we're invoked from other language frontends. Similarly, :py:class:`gcc.MethodType` has gained an "argument_types" attribute. Unconditional warnings ---------------------- The :py:func:`gcc.warning()` function in previous versions of the plugin required an "option" argument, such as ``gcc.Option('-Wformat')`` It's now possible to emit an unconditional warning, by supplying `None` for this argument, which is now the default value:: gcc.warning(func.start, 'this is an unconditional warning') .. code-block:: bash $ ./gcc-with-python script.py input.c input.c:25:1: warning: this is an unconditional warning [enabled by default] which will be an error if `-Werror` is supplied as a command-line argument to gcc: .. code-block:: bash $ ./gcc-with-python script.py -Werror input.c input.c:25:1: error: this is an unconditional warning [-Werror] Improvements to :doc:`gcc-with-cpychecker ` ======================================================== The :doc:`"libcpychecker" ` Python code is a large example of using the plugin: it extends GCC with code that tries to detect various bugs in CPython extension modules. As of this release, all of the errors emitted by the tool have been converted to warnings. This should make `gcc-with-cpychecker` more usable as a drop-in replacement for `gcc`: the first source file with a refcounting error should no longer terminate the build (unless the program uses ``-Werror``, of course). Verification of PyMethodDef tables ---------------------------------- This release adds checking of tables of PyMethodDef initialization values, used by Python extension modules for binding C functions to Python methods. The checker will verify that the signatures of the callbacks match the flags, and that the such tables are NULL terminated:: input.c:48:22: warning: flags do not match callback signature for 'test' within PyMethodDef table input.c:48:22: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)" (2 arguments) input.c:48:22: note: actual type of underlying callback: struct PyObject * (struct PyObject *, struct PyObject *, struct PyObject *) (3 arguments) input.c:48:22: note: see http://docs.python.org/c-api/structures.html#PyMethodDef Coverage of the CPython API --------------------------- When the checker warns about code that can erroneously pass ``NULL`` to various CPython API entrypoints which are known to implicitly dereference those arguments, the checker will now add an explanatory note about why it is complaining. For example:: input.c: In function 'test': input.c:38:33: warning: calling PyString_AsString with NULL (gcc.VarDecl('repr_args')) as argument 1 at input.c:38 input.c:31:15: note: when PyObject_Repr() fails at: repr_args = PyObject_Repr(args); input.c:38:33: note: PyString_AsString() invokes Py_TYPE() on the pointer via the PyString_Check() macro, thus accessing (NULL)->ob_type input.c:27:1: note: graphical error report for function 'test' written out to 'input.c.test-refcount-errors.html' The checker will now verify the argument lists of invocations of `PyObject_CallFunctionObjArgs `_ and `PyObject_CallMethodObjArgs `_, checking that all of the arguments are of the correct type (PyObject* or subclasses), and that the list is NULL-terminated:: input.c: In function 'test': input.c:33:5: warning: argument 2 had type char[12] * but was expecting a PyObject* (or subclass) input.c:33:5: warning: arguments to PyObject_CallFunctionObjArgs were not NULL-terminated This release also adds heuristics for the behavior of the following CPython API entrypoints: * PyArg_Parse * PyCObject_{As,From}VoidPtr * PyCallable_Check * PyCapsule_GetPointer * PyErr_{NewException,SetNone,WarnEx} * PyEval_CallObjectWithKeywords * PyEval_{Save,Restore}Thread (and thus the Py_{BEGIN,END}_ALLOW_THREADS macros) * PyList_{GetItem,Size} * PyLong_FromLongLong * PyMapping_Size * PyModule_GetDict * PyObject_AsFileDescriptor * PyObject_Call{Function,FunctionObjArgs,MethodObjArgs} * PyObject_Generic{Get,Set}Attr * PyString_Size * PyTuple_Pack * PyUnicode_AsUTF8String * Py_AtExit Bug fixes --------- * gcc-with-cpychecker will now try harder on functions that are too complicated to fully handle. Previously, when a function was too complicated for the reference-count tracker to fully analyze, it would give up, performing no analysis. The checker will now try to obtain at least some subset of the list of all traces through the function, and analyze those. It will still note that the function was too complicated to fully analyze. Given that we do a depth-first traversal of the tree, and that "success" transitions are typically visited before "failure" transitions, this means that it should at least analyze the trace in which all functions calls succeed, together with traces in which some of the later calls fail. * the reference-count checker now correctly handles "static" `PyObject*` local variables: a `static PyObject *` local preserves its value from call to call, and can thus permanently own a reference. Fixes a false-positive seen in psycopg2-2.4.2 (`psycopg/psycopgmodule.c:psyco_GetDecimalType`) where the refcount checker erroneously reported that a reference was leaked. * the checker for Py_BuildValue("O") (and "S" and "N") was being too strict, requiring a (PyObject*). Although it's not explicitly documented, it's clear that these can also accept pointers to any PyObject subclass. Fixes a false positive seen when running gcc-with-cpychecker on coverage-3.5.1b1.tar.gz, in which `coverage/tracer.c:Tracer_trace` passes a PyFrameObject* as an argument to such a call. * the reference-count checker now correctly suppresses reports about "leaks" for traces that call a function that never return (such as `abort()`). Fixes a false positive seen in rpm-4.9.1.2 in a handler for fatal errors: (in python/rpmts-py.c:die) where the checker erroneously reported that a reference was leaked. * `tp_iternext` callbacks are allowed to return NULL without setting an exception. The reference-count checker will now notice if a function is used in such a role, and suppress warnings about such behavior. * fixed various Python tracebacks (tickets `#14 `_, `#19 `_, `#20 `_, `#22 `_, `#23 `_, `#24 `_, `#25 `_) * various other fixes gcc-python-plugin-0.17/docs/0.9.rst000066400000000000000000000135731342215241600170110ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . .. covers up to 4642a564e03c9e2c8114bca206205ad9c8fbc308 0.9 ~~~ Changes to the GCC Python Plugin ================================ The plugin now works with GCC 4.7 prereleases (ticket `#21 `_). The plugin is now integrated with GCC's garbage collector: Python wrapper objects keep their underlying GCC objects alive when GCC's garbage collector runs, preventing segfaults that could occur if the underlying objects were swept away from under us (ticket `#1 `_). It's now possible to attach Python callbacks to more GCC events: :py:data:`gcc.PLUGIN_FINISH`, :py:data:`gcc.PLUGIN_GGC_START`, :py:data:`gcc.PLUGIN_GGC_MARKING`, :py:data:`gcc.PLUGIN_GGC_FINISH`, :py:data:`gcc.PLUGIN_FINISH_DECL` (gcc 4.7) :py:class:`gcc.ArrayType` has gained a "range" attribute, allowing scripts to detect out-of-bounds conditions in array-handling. A number of memory leaks were fixed: these were found by `running the plugin on itself `_. Various documentation improvements (ticket `#6 `_, ticket `#31 `_). Improvements to :doc:`gcc-with-cpychecker ` ======================================================== The :doc:`gcc-with-cpychecker ` tool has received some deep internal improvements in this release. The logic for analyzing the outcome of comparisons has been rewritten for this release, fixing some significant bugs that could lead to the analyzer incorrectly deciding whether or not a block of code was reachable. Similarly, the logic for detecting loops has been rewritten, elimininating a bug in which the checker would prematurely stop analyzing loops with complicated termination conditions, and not analyze the body of the loop. Doing so extended the reach of the checker, and enabled it to find the memory leaks referred to above. In addition, the checker now emits more detailed information on the ranges of possible values it's considering when a comparison occurs against an unknown value:: input.c: In function 'test': input.c:41:5: warning: comparison against uninitialized data (item) at input.c:41 [enabled by default] input.c:34:12: note: when PyList_New() succeeds at: result = PyList_New(len); input.c:35:8: note: taking False path at: if (!result) { input.c:39:12: note: reaching: for (i = 0; i < len; i++) { input.c:39:5: note: when considering range: 1 <= value <= 0x7fffffff at: for (i = 0; i < len; i++) { input.c:39:5: note: taking True path at: for (i = 0; i < len; i++) { input.c:41:5: note: reaching: if (!item) { The checker should do a better job of identifying PyObject subclasses. Previously it was treating any struct beginning with "ob_refcnt" and "ob_type" as a Python object (with some tweaks for python 3 and debug builds). It now also covers structs that begin with a field that's a PyObject (or subclass), since these are likely to also be PyObject subclasses. Usage of deallocated memory --------------------------- Previously, the checker would warn about paths through a function that could return a pointer to deallocated memory, or which tried to read through such a pointer. With this release, the checker will now also warn about paths through a function in which a pointer to deallocated memory is passed to a function. For example, given this buggy code: .. code-block:: c extern void some_function(PyObject *); void test(PyObject *self, PyObject *args) { /* Create an object: */ PyObject *tmp = PyLong_FromLong(0x1000); if (!tmp) { return; } /* Now decref the object. Depending on what other references are owned on the object, it can reach a refcount of zero, and thus be deallocated: */ Py_DECREF(tmp); /* BUG: the object being returned may have been deallocated */ some_function(tmp); } the checker will emit this warning:: input.c: In function 'test': input.c:45: warning: passing pointer to deallocated memory as argument 1 of function at input.c:45: memory deallocated at input.c:42 [enabled by default] input.c:32: note: when PyLong_FromLong() succeeds at: PyObject *tmp = PyLong_FromLong(0x1000); input.c:34: note: taking False path at: if (!tmp) { input.c:42: note: reaching: Py_DECREF(tmp); input.c:42: note: when taking False path at: Py_DECREF(tmp); input.c:42: note: reaching: Py_DECREF(tmp); input.c:42: note: calling tp_dealloc on PyLongObject allocated at input.c:32 at: Py_DECREF(tmp); input.c:45: note: reaching: foo(tmp); input.c:30: note: graphical error report for function 'passing_dead_object_to_function' written out to 'input.c.test-refcount-errors.html' Coverage of the CPython API --------------------------- This release adds heuristics for the behavior of the following CPython API entrypoints: * PyString_Concat * PyString_ConcatAndDel along with various other bugfixes and documentation improvements. .. gcc-python-plugin-0.17/docs/Makefile000066400000000000000000000116201342215241600174000ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make a Texinfo document" @echo " info to make an info page" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/gcc-python-plugin.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/gcc-python-plugin.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/gcc-python-plugin" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/gcc-python-plugin" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo pages are in $(BUILDDIR)/texinfo." info: texinfo cd $(BUILDDIR)/texinfo && $(MAKE) info @echo @echo "Build finished. The info pages are in $(BUILDDIR)/texinfo." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." gcc-python-plugin-0.17/docs/appendices.rst000066400000000000000000000016001342215241600206020ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . Appendices ========== The following contain tables of reference material that may be useful when writing scripts. .. toctree:: tables-of-passes.rst operators.rst gcc-python-plugin-0.17/docs/attributes.rst000066400000000000000000000111211342215241600206540ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . Creating custom GCC attributes ============================== GNU C supports a non-standard `__attribute__(()) syntax `_ for marking declarations with additional information that may be of interest to the optimizer, and for checking the correctness of the code. The GCC Python plugin allows you to create custom attributes, which may be of use to your scripts: you can use this to annotate C code with additional information. For example, you could create a custom attribute for functions describing the interaction of a function on mutex objects: .. code-block:: c extern void some_function(void) __attribute__((claims_mutex("io"))); extern void some_other_function(void) __attribute__((releases_mutex("io"))); and use this in a custom code-checker. Custom attributes can take string and integer parameters. For example, the above custom attributes take a single string parameter. A custom attribute can take more than one parameter, or none at all. To create custom attributes from Python, you need to wire up a callback response to the :py:data:`gcc.PLUGIN_ATTRIBUTES` event: .. literalinclude:: ../tests/examples/attributes/script.py :lines: 39-40 :language: python This callback should then call :py:func:`gcc.register_attribute` to associate the name of the attribute with a Python callback to be called when the attribute is encountered in C code. .. py:function:: gcc.register_attribute(name, min_length, max_length, \ decl_required, type_required, \ function_type_required, \ callable) Registers a new GCC attribute with the given *name* , usable in C source code via ``__attribute__(())``. :param name: the name of the new attribute :type name: str :param min_length: the minimum number of arguments expected when the attribute is used :type min_length: int :param max_length: the maximum number of arguments expected when the attribute is used (-1 for no maximum) :type max_length: int :param decl_required: :type decl_required: :param type_required: :type type_required: :param function_type_required: :type function_type_required: :param callable: the callback to be invoked when the attribute is seen :type callable: a callable object, such as a function In this example, we can simply print when the attribute is seen, to verify that the callback mechanism is working: .. literalinclude:: ../tests/examples/attributes/script.py :lines: 22-36 :language: python Putting it all together, here is an example Python script for the plugin: .. literalinclude:: ../tests/examples/attributes/script.py :lines: 18- :language: python Compiling this test C source file: .. literalinclude:: ../tests/examples/attributes/input.c :lines: 22-29 :language: c leads to this output from the script: .. literalinclude:: ../tests/examples/attributes/stdout.txt :language: bash Using the preprocessor to guard attribute usage ----------------------------------------------- Unfortunately, the above C code will only work when it is compiled with the Python script that adds the custom attributes. You can avoid this by using :py:func:`gcc.define_macro()` to pre-define a preprocessor name (e.g. "WITH_ATTRIBUTE_CLAIMS_MUTEX") at the same time as when you define the attribute: .. literalinclude:: ../tests/examples/attributes-with-macros/script.py :lines: 18- :language: python This way the user can write this C code instead, and have it work both with and without the Python script: .. literalinclude:: ../tests/examples/attributes-with-macros/input.c :lines: 22-45 :language: c giving this output from the script: .. literalinclude:: ../tests/examples/attributes-with-macros/stdout.txt :language: bash gcc-python-plugin-0.17/docs/basics.rst000066400000000000000000000301031342215241600177330ustar00rootroot00000000000000.. Copyright 2011-2012, 2017 David Malcolm Copyright 2011-2012, 2017 Red Hat, Inc. This 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 . .. For notes on how to document Python in RST form, see e.g.: .. http://sphinx.pocoo.org/domains.html#the-python-domain Requirements ============ The plugin has the following requirements: * GCC: 4.6 or later (it uses APIs that weren't exposed to plugins in 4.5) * Python: requires 2.7 or 3.2 or later * "six": The libcpychecker code uses the "six" Python compatibility library to smooth over Python 2 vs Python 3 differences, both at build-time and run-time: http://pypi.python.org/pypi/six/ * "pygments": The libcpychecker code uses the "pygments" Python syntax-highlighting library when writing out error reports: http://pygments.org/ * "lxml": The libcpychecker code uses the "lxml" internally when writing out error reports. * graphviz: many of the interesting examples use "dot" to draw diagrams (e.g. control-flow graphs), so it's worth having graphviz installed. Prebuilt-packages ================= Various distributions ship with pre-built copies of the plugin. If you're using Fedora, you can install the plugin via RPM on Fedora 16 onwards using: .. code-block:: bash yum install gcc-python2-plugin as root for the Python 2 build of the plugin, or: .. code-block:: bash yum install gcc-python3-plugin for the Python 3 build of the plugin. On Gentoo, use `layman` to add the `dMaggot` overlay and `emerge` the `gcc-python-plugin` package. This will build the plugin for Python 2 and Python 3 should you have both of them installed in your system. A live ebuild is also provided to install the plugin from git sources. Building the plugin from source =============================== Build-time dependencies ----------------------- If you plan to build the plugin from scratch, you'll need the build-time dependencies. On a Fedora box you can install them by running the following as root: .. code-block:: bash yum install gcc-plugin-devel python-devel python-six python-pygments graphviz for building against Python 2, or: .. code-block:: bash yum install gcc-plugin-devel python3-devel python3-six python3-pygments graphviz when building for Python 3. Building the code ------------------ You can obtain the source code from git by using:: $ git clone git@github.com:davidmalcolm/gcc-python-plugin.git To build the plugin, run: .. code-block:: bash make plugin To build the plugin and run the selftests, run: .. code-block:: bash make You can also use:: make demo to demonstrate the new compiler errors. By default, the `Makefile` builds the plugin using the first ``python-config`` tool found in `$PATH` (e.g. `/usr/bin/python-config`), which is typically the system copy of Python 2. You can override this (e.g. to build against Python 3) by overriding the `PYTHON` and `PYTHON_CONFIG` Makefile variables with: .. code-block:: bash make PYTHON=python3 PYTHON_CONFIG=python3-config There isn't a well-defined process yet for installing the plugin (though the rpm specfile in the source tree contains some work-in-progress towards this). Some notes on GCC plugins can be seen at http://gcc.gnu.org/wiki/plugins and http://gcc.gnu.org/onlinedocs/gccint/Plugins.html .. note:: Unfortunately, the layout of the header files for GCC plugin development has changed somewhat between different GCC releases. In particular, older builds of GCC flattened the "c-family" directory in the installed plugin headers. This was fixed in this GCC commit: http://gcc.gnu.org/viewcvs?view=revision&revision=176741 So if you're using an earlier build of GCC using the old layout you'll need to apply the following patch (reversed with "-R") to the plugin's source tree to get it to compile: .. code-block:: bash $ git show 215730cbec40a6fe482fabb7f1ecc3d747f1b5d2 | patch -p1 -R If you have a way to make the plugin's source work with either layout, please email the plugin's `mailing list `_ Basic usage of the plugin ========================= Once you've built the plugin, you can invoke a Python script like this: .. code-block:: bash gcc -fplugin=./python.so -fplugin-arg-python-script=PATH_TO_SCRIPT.py OTHER_ARGS and have it run your script as the plugin starts up. Alternatively, you can run a one-shot Python command like this: .. code-block:: bash gcc -fplugin=./python.so -fplugin-arg-python-command="python code" OTHER_ARGS such as: .. code-block:: bash gcc -fplugin=./python.so -fplugin-arg-python-command="import sys; print(sys.path)" OTHER_ARGS The plugin automatically adds the absolute path to its own directory to the end of its `sys.path`, so that it can find support modules, such as gccutils.py and `libcpychecker`. There is also a helper script, `gcc-with-python`, which expects a python script as its first argument, then regular gcc arguments: .. code-block:: bash ./gcc-with-python PATH_TO_SCRIPT.py other args follow For example, this command will use graphviz to draw how GCC "sees" the internals of each function in `test.c` (within its SSA representation): .. code-block:: bash ./gcc-with-python examples/show-ssa.py test.c Most of the rest of this document describes the Python API visible for scripting. The plugin GCC's various types as Python objects, within a "gcc" module. You can see the API by running the following within a script:: import gcc help(gcc) To make this easier, there's a script to do this for you: .. code-block:: bash ./gcc-python-docs from where you can review the built-in documentation strings (this document may be easier to follow though). The exact API is still in flux: and may well change (this is an early version of the code; we may have to change things as GCC changes in future releases also). Debugging your script --------------------- You can place a forced breakpoint in your script using this standard Python one-liner:: import pdb; pdb.set_trace() If Python reaches this location it will interrupt the compile and put you within the `pdb` interactive debugger, from where you can investigate. See http://docs.python.org/library/pdb.html#debugger-commands for more information. If an exception occurs during Python code, and isn't handled by a try/except before returning into the plugin, the plugin prints the traceback to stderr and treats it as an error: .. code-block:: pytb /home/david/test.c: In function ‘main’: /home/david/test.c:28:1: error: Unhandled Python exception raised within callback Traceback (most recent call last): File "test.py", line 38, in my_pass_execution_callback dot = gccutils.tree_to_dot(fun) NameError: global name 'gccutils' is not defined (In this case, it was a missing `import` statement in the script) GCC reports errors at a particular location within the source code. For an unhandled exception such as the one above, by default, the plugin reports the error as occurring as the top of the current source function (or the last location within the current source file for passes and callbacks that aren't associated with a function). You can override this using gcc.set_location: .. py:function:: gcc.set_location(loc) Temporarily overrides the error-reporting location, so that if an exception occurs, it will use this `gcc.Location`, rather than the default. This may be of use when debugging tracebacks from scripts. The location is reset each time after returning from Python back to the plugin, after printing any traceback. Accessing parameters -------------------- .. py:data:: gcc.argument_dict Exposes the arguments passed to the plugin as a dictionary. For example, running: .. code-block:: bash gcc -fplugin=python.so \ -fplugin-arg-python-script=test.py \ -fplugin-arg-python-foo=bar with `test.py` containing:: import gcc print(gcc.argument_dict) has output:: {'script': 'test.py', 'foo': 'bar'} .. py:data:: gcc.argument_tuple Exposes the arguments passed to the plugin as a tuple of (key, value) pairs, so you have ordering. (Probably worth removing, and replacing :py:data:`argument_dict` with an OrderedDict instead; what about duplicate args though?) Adding new passes to the compiler --------------------------------- You can create new compiler passes by subclassing the appropriate :py:class:`gcc.Pass` subclasss. For example, here's how to wire up a new pass that displays the control flow graph of each function: .. literalinclude:: ../examples/show-gimple.py :lines: 19- :language: python For more information, see :ref:`creating-new-passes` Wiring up callbacks ------------------- The other way to write scripts is to register callback functions to be called when various events happen during compilation, such as using :py:data:`gcc.PLUGIN_PASS_EXECUTION` to piggyback off of an existing GCC pass. .. literalinclude:: ../examples/show-passes.py :lines: 19- :language: python For more information, see :ref:`callbacks` Global data access ================== .. py:function:: gcc.get_variables() Get all variables in this compilation unit as a list of :py:class:`gcc.Variable` .. py:class:: gcc.Variable Wrapper around GCC's `struct varpool_node`, representing a variable in the code being compiled. .. py:attribute:: decl The declaration of this variable, as a :py:class:`gcc.Tree` .. py:function:: gccutils.get_variables_as_dict() Get a dictionary of all variables, where the keys are the variable names (as strings), and the values are instances of :py:class:`gcc.Variable` .. py:function:: gcc.maybe_get_identifier(str) Get the :py:class:`gcc.IdentifierNode` with this name, if it exists, otherwise None. (However, after the front-end has run, the identifier node may no longer point at anything useful to you; see :py:func:`gccutils.get_global_typedef` for an example of working around this) .. py:function:: gcc.get_translation_units() Get a list of all :py:class:`gcc.TranslationUnitDecl` for the compilation units within this invocation of GCC (that's "source code files" for the layperson). .. py:class:: gcc.TranslationUnitDecl Subclass of :py:class:`gcc.Tree` representing a compilation unit .. py:attribute:: block The :py:class:`gcc.Block` representing global scope within this source file. .. py:attribute:: language The source language of this translation unit, as a string (e.g. "GNU C") .. py:function:: gcc.get_global_namespace() C++ only: locate the :py:class:`gcc.NamespaceDecl` for the global namespace (a.k.a. "::") .. py:function:: gccutils.get_global_typedef(name) Given a string `name`, look for a C/C++ `typedef` in global scope with that name, returning it as a :py:class:`gcc.TypeDecl`, or None if it wasn't found .. py:function:: gccutils.get_global_vardecl_by_name(name) Given a string `name`, look for a C/C++ variable in global scope with that name, returning it as a :py:class:`gcc.VarDecl`, or None if it wasn't found .. py:function:: gccutils.get_field_by_name(decl, name) Given one of a :py:class:`gcc.RecordType`, :py:class:`gcc.UnionType`, or :py:class:`gcc.QualUnionType`, along with a string `name`, look for a field with that name within the given struct or union, returning it as a :py:class:`gcc.FieldDecl`, or None if it wasn't found gcc-python-plugin-0.17/docs/callbacks.rst000066400000000000000000000246641342215241600204250ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . .. _callbacks: Working with callbacks ====================== One way to work with GCC from the Python plugin is via callbacks. It's possible to register callback functions, which will be called when various events happen during compilation. For example, it's possible to piggyback off of an existing GCC pass by using :py:data:`gcc.PLUGIN_PASS_EXECUTION` to piggyback off of an existing GCC pass. .. py:function:: gcc.register_callback(event_id, function, [extraargs,] **kwargs) Wire up a python function as a callback. It will be called when the given event occurs during compilation. For some events, the callback will be called just once; for other events, the callback is called once per function within the source code being compiled. In the latter case, the plugin passes a :py:class:`gcc.Function` instance as a parameter to your callback, so that you can work on it:: import gcc def my_pass_execution_callback(*args, **kwargs): print('my_pass_execution_callback was called: args=%r kwargs=%r' % (args, kwargs)) gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION, my_pass_execution_callback) The exact arguments passed to your callback vary: consult the documentation for the particular event you are wiring up to (see below). You can pass additional arguments when registering the callback - they will be passed to the callback after any normal arguments. This is denoted in the descriptions of events below by `*extraargs`. You can also supply keyword arguments: they will be passed on as keyword arguments to the callback. This is denoted in the description of events below by `**kwargs`. The various events are exposed as constants within the `gcc` module and directly wrap GCC's plugin mechanism. The following GCC events are currently usable from the Python plugin via :py:func:`gcc.register_callback()`: =============================================== ========= ID Meaning =============================================== ========= :py:data:`gcc.PLUGIN_ATTRIBUTES` For :doc:`creating custom GCC attributes ` :py:data:`gcc.PLUGIN_PRE_GENERICIZE` For working with the AST in the C and C++ frontends :py:data:`gcc.PLUGIN_PASS_EXECUTION` Called before each pass is executed :py:data:`gcc.PLUGIN_FINISH_UNIT` At the end of working with a translation unit (aka source file) :py:data:`gcc.PLUGIN_FINISH_TYPE` After a type has been parsed :py:data:`gcc.PLUGIN_FINISH_DECL` After a declaration has been parsed (GCC 4.7 or later) :py:data:`gcc.PLUGIN_FINISH` Called before GCC exits =============================================== ========= .. py:data:: gcc.PLUGIN_ATTRIBUTES Called when GCC is creating attributes for use with its non-standard `__attribute__(()) syntax `_. If you want to create custom GCC attributes, you should register a callback on this event and call :py:func:`gcc.register_attribute()` from within that callback, so that they are created at the same time as the GCC's built-in attributes. No arguments are passed to your callback other than those that you supply yourself when registering it: (`*extraargs`, `**kwargs`) See :doc:`creating custom GCC attributes ` for examples and more information. .. py:data:: gcc.PLUGIN_PASS_EXECUTION Called when GCC is about to run one of its passes. Arguments passed to the callback are: (`ps`, `fun`, `*extraargs`, `**kwargs`) where `ps` is a :py:class:`gcc.Pass` and `fun` is a :py:class:`gcc.Function`. Your callback will typically be called many times: there are many passes, and each can be invoked zero or more times per function (in the code being compiled) More precisely, some passes have a "gate check": the pass first checks a condition, and only executes if the condition is true. Any callback registered with `gcc.PLUGIN_PASS_EXECUTION` will get called if this condition succeeds. The actual work of the pass is done after the callbacks return. In pseudocode:: if pass.has_gate_condition: if !pass.test_gate_condition(): return invoke_all_callbacks() actually_do_the_pass() For passes working on individual functions, all of the above is done per-function. To connect to a specific pass, you can simply add a conditional based on the name of the pass:: import gcc def my_callback(ps, fun): if ps.name != '*warn_function_return': # Not the pass we want return # Do something here print(fun.decl.name) gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION, my_callback) .. py:data:: gcc.PLUGIN_PRE_GENERICIZE Arguments passed to the callback are: (`fndecl`, `*extraargs`, `**kwargs`) where `fndecl` is a :py:class:`gcc.Tree` representing a function declaration within the source code being compiled. .. py:data:: gcc.PLUGIN_FINISH_UNIT Called when GCC has finished compiling a particular translation unit. Arguments passed to the callback are: (`*extraargs`, `**kwargs`) .. py:data:: gcc.PLUGIN_FINISH_DECL .. note:: Only available in GCC 4.7 onwards. Called when GCC has finished compiling a declaration (variables, functions, parameters to functions, types, etc) Arguments passed to the callback are: (`decl`, `*extraargs`, `**kwargs`) where `decl` is a :py:class:`gcc.Declaration`. .. py:data:: gcc.PLUGIN_FINISH_TYPE Called when GCC has finished parsing a type. Arguments to the callback are: (`type`, `*extraargs`, `**kwargs`) where `type` is a :py:class:`gcc.Type`. .. py:data:: gcc.PLUGIN_FINISH Called before GCC exits. Arguments passed to the callback are: (`*extraargs`, `**kwargs`) The remaining GCC events aren't yet usable from the plugin; an attempt to register a callback on them will lead to an exception being raised. Email the `gcc-python-plugin's mailing list `_ if you're interested in working with these): =============================================== ========= ID Meaning =============================================== ========= :py:data:`gcc.PLUGIN_PASS_MANAGER_SETUP` To hook into pass manager :py:data:`gcc.PLUGIN_INFO` Information about the plugin :py:data:`gcc.PLUGIN_GGC_START` For interacting with GCC's garbage collector :py:data:`gcc.PLUGIN_GGC_MARKING` (ditto) :py:data:`gcc.PLUGIN_GGC_END` (ditto) :py:data:`gcc.PLUGIN_REGISTER_GGC_ROOTS` (ditto) :py:data:`gcc.PLUGIN_REGISTER_GGC_CACHES` (ditto) :py:data:`gcc.PLUGIN_START_UNIT` Called before processing a translation unit (aka source file) :py:data:`gcc.PLUGIN_PRAGMAS` For registering pragmas :py:data:`gcc.PLUGIN_ALL_PASSES_START` Called before the first pass of the :ref:`"all other passes" gcc.Pass catchall ` :py:data:`gcc.PLUGIN_ALL_PASSES_END` Called after last pass of the :ref:`"all other passes" gcc.Pass catchall ` :py:data:`gcc.PLUGIN_ALL_IPA_PASSES_START` Called before the first IPA pass :py:data:`gcc.PLUGIN_ALL_IPA_PASSES_END` Called after last IPA pass :py:data:`gcc.PLUGIN_OVERRIDE_GATE` Provides a way to disable a built-in pass :py:data:`gcc.PLUGIN_EARLY_GIMPLE_PASSES_START` :py:data:`gcc.PLUGIN_EARLY_GIMPLE_PASSES_END` :py:data:`gcc.PLUGIN_NEW_PASS` =============================================== ========= .. Notes on the other callback events .. py:data:: gcc.PLUGIN_PRAGMAS gcc_data=0x0 Called from: c_common_init () at ../../gcc/c-family/c-opts.c:1052 .. py:data:: gcc.PLUGIN_START_UNIT gcc_data=0x0 Called from: compile_file () at ../../gcc/toplev.c:573 .. py:data:: gcc.PLUGIN_PRE_GENERICIZE gcc_data is: tree fndecl; Called from: finish_function () at ../../gcc/c-decl.c:8323 .. py:data:: gcc.PLUGIN_OVERRIDE_GATE gcc_data:: &gate_status bool gate_status; Called from : execute_one_pass (pass=0x1011340) at ../../gcc/passes.c:1520 .. py:data:: gcc.PLUGIN_ALL_IPA_PASSES_START gcc_data=0x0 Called from: ipa_passes () at ../../gcc/cgraphunit.c:1779 .. py:data:: gcc.PLUGIN_EARLY_GIMPLE_PASSES_START gcc_data=0x0 Called from: execute_ipa_pass_list (pass=0x1011fa0) at ../../gcc/passes.c:1927 .. py:data:: gcc.PLUGIN_EARLY_GIMPLE_PASSES_END gcc_data=0x0 Called from: execute_ipa_pass_list (pass=0x1011fa0) at ../../gcc/passes.c:1930 .. py:data:: gcc.PLUGIN_ALL_IPA_PASSES_END gcc_data=0x0 Called from: ipa_passes () at ../../gcc/cgraphunit.c:1821 .. py:data:: gcc.PLUGIN_ALL_PASSES_START gcc_data=0x0 Called from: tree_rest_of_compilation (fndecl=0x7ffff16b1f00) at ../../gcc/tree-optimize.c:420 .. py:data:: gcc.PLUGIN_ALL_PASSES_END gcc_data=0x0 Called from: tree_rest_of_compilation (fndecl=0x7ffff16b1f00) at ../../gcc/tree-optimize.c:425 .. py:data:: gcc.PLUGIN_FINISH gcc_data=0x0 Called from: toplev_main (argc=17, argv=0x7fffffffdfc8) at ../../gcc/toplev.c:1970 .. py:data:: gcc.PLUGIN_FINISH_TYPE gcc_data=tree Called from c_parser_declspecs (parser=0x7fffef559730, specs=0x15296d0, scspec_ok=1 '\001', typespec_ok=1 '\001', start_attr_ok=, la=cla_nonabstract_decl) at ../../gcc/c-parser.c:2111 .. py:data:: gcc.PLUGIN_PRAGMA gcc_data=0x0 Called from: init_pragma at ../../gcc/c-family/c-pragma.c:1321 to "Allow plugins to register their own pragmas." gcc-python-plugin-0.17/docs/callgraph.rst000066400000000000000000000042771342215241600204410ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . Interprocedural analysis (IPA) ============================== GCC builds a "call graph", recording which functions call which other functions, and it uses this for various optimizations. It is constructed by the `"*build_cgraph_edges"` pass. In case it's of interest, it is available via the following Python API: .. py:function:: gcc.get_callgraph_nodes() Get a list of all :py:class:`gcc.CallgraphNode` instances .. py:function:: gccutils.callgraph_to_dot() Return the GraphViz source for a rendering of the current callgraph, as a string. Here's an example of such a rendering: .. figure:: sample-callgraph.png :alt: image of a call graph .. py:class:: gcc.CallgraphNode .. py:attribute:: decl The :py:class:`gcc.FunctionDecl` for this node within the callgraph .. py:attribute:: callees The function calls made by this function, as a list of :py:class:`gcc.CallgraphEdge` instances .. py:attribute:: callers The places that call this function, as a list of :py:class:`gcc.CallgraphEdge` instances Internally, this wraps a `struct cgraph_node *` .. py:class:: gcc.CallgraphEdge .. py:attribute:: caller The function that makes this call, as a :py:class:`gcc.CallgraphNode` .. py:attribute:: callee The function that is called here, as a :py:class:`gcc.CallgraphNode` .. py:attribute:: call_stmt The :py:class:`gcc.GimpleCall` statememt for the function call Internally, this wraps a `struct cgraph_edge *` gcc-python-plugin-0.17/docs/cfg.rst000066400000000000000000000121011342215241600172240ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . Working with functions and control flow graphs ============================================== Many of the plugin events are called for each function within the source code being compiled. Each time, the plugin passes a :py:class:`gcc.Function` instance as a parameter to your callback, so that you can work on it. You can get at the control flow graph of a :py:class:`gcc.Function` via its ``cfg`` attribute. This is an instance of :py:class:`gcc.Cfg`. .. py:class:: gcc.Function Wrapper around one of GCC's ``struct function *`` .. py:attribute:: cfg An instance of :py:class:`gcc.Cfg` for this function (or None during early passes) .. py:attribute:: decl The declaration of this function, as a :py:class:`gcc.FunctionDecl` .. py:attribute:: local_decls List of :py:class:`gcc.VarDecl` for the function's local variables. It does not contain arguments; for those see the `arguments` property of the function's `decl`. Note that for locals with initializers, `initial` only seems to get set on those `local_decls` that are static variables. For other locals, it appears that you have to go into the gimple representation to locate assignments. .. py:attribute:: start The :py:class:`gcc.Location` of the beginning of the function .. py:attribute:: end The :py:class:`gcc.Location` of the end of the function .. py:attribute:: funcdef_no Integer: a sequence number for profiling, debugging, etc. .. py:class:: gcc.Cfg A ``gcc.Cfg`` is a wrapper around GCC's `struct control_flow_graph`. .. py:attribute:: basic_blocks List of :py:class:`gcc.BasicBlock`, giving all of the basic blocks within this CFG .. py:attribute:: entry Instance of :py:class:`gcc.BasicBlock`: the entrypoint for this CFG .. py:attribute:: exit Instance of :py:class:`gcc.BasicBlock`: the final one within this CFG .. py:method:: get_block_for_label(labeldecl) Given a :py:class:`gcc.LabelDecl`, get the corresponding :py:class:`gcc.BasicBlock` You can use ``gccutils.cfg_to_dot`` to render a gcc.Cfg as a graphviz diagram. It will render the diagram, showing each basic block, with source code on the left-hand side, interleaved with the "gimple" representation on the right-hand side. Each block is labelled with its index, and edges are labelled with appropriate flags. For example, given this sample C code: .. literalinclude:: ../test.c :lines: 33-48 :language: c then the following Python code:: dot = gccutils.cfg_to_dot(fun.cfg) gccutils.invoke_dot(dot) will render a CFG bitmap like this: .. figure:: sample-gimple-cfg.png :scale: 50 % :alt: image of a control flow graph .. py:class:: gcc.BasicBlock A ``gcc.BasicBlock`` is a wrapper around GCC's `basic_block` type. .. py:attribute:: index The index of the block (an int), as seen in the cfg_to_dot rendering. .. py:attribute:: preds The list of predecessor :py:class:`gcc.Edge` instances leading into this block .. py:attribute:: succs The list of successor :py:class:`gcc.Edge` instances leading out of this block .. py:attribute:: phi_nodes The list of :py:class:`gcc.GimplePhi` phoney functions at the top of this block, if appropriate for this pass, or None .. py:attribute:: gimple The list of :py:class:`gcc.Gimple` instructions, if appropriate for this pass, or None .. py:attribute:: rtl The list of :py:class:`gcc.Rtl` expressions, if appropriate for this pass, or None .. py:class:: gcc.Edge A wrapper around GCC's `edge` type. .. py:attribute:: src The source :py:class:`gcc.BasicBlock` of this edge .. py:attribute:: dest The destination :py:class:`gcc.BasicBlock` of this edge .. py:attribute:: true_value Boolean: `True` if this edge is taken when a :py:class:`gcc.GimpleCond` conditional is true, `False` otherwise .. py:attribute:: false_value Boolean: `True` if this edge is taken when a :py:class:`gcc.GimpleCond` conditional is false, `False` otherwise .. py:attribute:: complex Boolean: `True` if this edge is "special" e.g. due to exception-handling, or some other kind of "strange" control flow transfer, `False` otherwise .. various other EDGE_ booleans, though it's not clear that they should be documented gcc-python-plugin-0.17/docs/conf.py000066400000000000000000000200361342215241600172400ustar00rootroot00000000000000# Copyright 2011, 2012, 2013 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . # -*- coding: utf-8 -*- # # gcc-python-plugin documentation build configuration file, created by # sphinx-quickstart on Wed Jun 1 15:53:48 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'gcc-python-plugin' copyright = u'2011-2019, David Malcolm' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.17' # The full version, including alpha/beta/rc tags. release = '0.17' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'gcc-python-plugindoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'gcc-python-plugin.tex', u'gcc-python-plugin Documentation', u'David Malcolm', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'gcc-python-plugin', u'gcc-python-plugin Documentation', [u'David Malcolm'], 1), ('gcc-with-python', 'gcc-with-python', u'Run GCC whilst invoking a Python script', [u'David Malcolm'], 1) ] # -- Options for Texinfo output ------------------------------------------------ texinfo_documents = [ (master_doc, 'gcc-python-plugin', 'gcc-python-plugin Documentation', 'David Malcolm', 'gcc-python-plugin', 'gcc-python-plugin Documentation', 'Software development', False) ] gcc-python-plugin-0.17/docs/cpychecker.rst000066400000000000000000001110321342215241600206100ustar00rootroot00000000000000.. Copyright 2011, 2012 David Malcolm Copyright 2011, 2012 Red Hat, Inc. This 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 . .. _cpychecker: Usage example: a static analysis tool for CPython extension code ================================================================ .. note:: This code is under heavy development, and still contains bugs. It is not unusual to see Python tracebacks when running the checker. You should verify what the checker reports before acting on it: it could be wrong. An example of using the plugin is a static analysis tool I'm working on which checks the C source of CPython extension modules for common coding errors. This was one of my main motivations for writing the GCC plugin, and I often need to extend the plugin to support this use case. For this reason, the checker is embedded within the gcc-python source tree itself for now: * `gcc-with-cpychecker` is a harness script, which invokes GCC, adding the arguments necessary to use the Python plugin, using the `libcpychecker` Python code * the `libcpychecker` subdirectory contains the code that does the actual work * various test cases (in the source tree, below `tests/cpychecker`) gcc-with-cpychecker ------------------- `gcc-with-cpychecker` is a harness script, which invokes GCC, adding the arguments necessary to use the Python plugin, using the `libcpychecker` Python code You should be able to use the checker on arbitrary CPython extension code by replacing "gcc" with "gcc-with-cpychecker" in your build with something like:: make CC=/path/to/built/plugin/gcc-with-cpychecker to override the Makefile variable `CC`. You may need to supply an absolute path, especially if the "make" recursively invokes "make" within subdirectories (thus having a different working directory). Similarly, for projects that use `distutils `_, the code is typically built with an invocation like this:: python setup.py build This respects the environment variable `CC`, so typically you can replace the above with something like this in order to add the additional checks:: CC=/path/to/built/plugin/gcc-with-cpychecker python setup.py build Additional arguments for `gcc-with-cpychecker` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. program:: gcc-with-cpychecker .. cmdoption:: --maxtrans Set the maximum number of transitions to consider within each function before pruning the analysis tree. You may need to increase this limit for complicated functions. .. cmdoption:: --dump-json Dump a JSON representation of any problems. For example, given a function `foo.c`, if any warnings or errors are found in function `bar`, a file `foo.c.bar.json` will be written out in JSON form. Reference-count checking ------------------------ The checker attempts to analyze all possible paths through each function, tracking the various ``PyObject*`` objects encountered. For each path through the function and ``PyObject*``, it determines what the reference count ought to be at the end of the function, issuing warnings for any that are incorrect. The warnings are in two forms: the classic textual output to GCC's standard error stream, together with an HTML report indicating the flow through the function, in graphical form. For example, given this buggy C code: .. code-block:: c PyObject * test(PyObject *self, PyObject *args) { PyObject *list; PyObject *item; list = PyList_New(1); if (!list) return NULL; item = PyLong_FromLong(42); /* This error handling is incorrect: it's missing an invocation of Py_DECREF(list): */ if (!item) return NULL; /* This steals a reference to item; item is not leaked when we get here: */ PyList_SetItem(list, 0, item); return list; } the checker emits these messages to stderr:: input.c: In function 'test': input.c:38:1: warning: ob_refcnt of '*list' is 1 too high [enabled by default] input.c:38:1: note: was expecting final ob_refcnt to be N + 0 (for some unknown N) input.c:38:1: note: but final ob_refcnt is N + 1 input.c:27:10: note: PyListObject allocated at: list = PyList_New(1); input.c:27:10: note: when PyList_New() succeeds at: list = PyList_New(1); input.c:27:10: note: ob_refcnt is now refs: 1 + N where N >= 0 input.c:28:8: note: taking False path at: if (!list) input.c:30:10: note: reaching: item = PyLong_FromLong(42); input.c:30:10: note: when PyLong_FromLong() fails at: item = PyLong_FromLong(42); input.c:33:8: note: taking True path at: if (!item) input.c:34:9: note: reaching: return NULL; input.c:38:1: note: returning input.c:24:1: note: graphical error report for function 'test' written out to 'input.c.test-refcount-errors.html' along with this HTML report (as referred to by the final line on stderr): .. figure:: sample-html-error-report.png :alt: screenshot of the HTML report The HTML report is intended to be relatively self-contained, and thus easy to attach to bug tracking systems (it embeds its own CSS inline, and references the JavaScript it uses via URLs to the web). .. note:: The arrow graphics in the HTML form of the report are added by using the JSPlumb JavaScript library to generate HTML 5 elements. You may need a relatively modern browser to see them. .. note:: The checker tracks reference counts in an abstract way, in two parts: a part of the reference count that it knows about within the context of the function, along with a second part: all of the other references held by the rest of the program. For example, in a call to PyInt_FromLong(0), it is assumed that if the call succeeds, the object has a reference count of 1 + N, where N is some unknown amount of other references held by the rest of the program. The checker knows that N >= 0. If the object is then stored in an opaque container which is known to increment the reference count, the checker can say that the reference count is then 1 + (N+1). If the function then decrements the reference count (to finish transferring the reference to the opaque container), the checker now treats the object as having a reference count of 0 + (N+1): it no longer owns any references on the object, but the reference count is actually unchanged relative to the original 1 + N amount. It also knows, given that N >= 0 that the actual reference count is >= 1, and thus the object won't (yet) be deallocated. Assumptions and configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For any function returning a ``PyObject*``, it assumes that the ``PyObject*`` should be either a new reference to an object, or NULL (with an exception set) - the function's caller should "own" a reference to that object. For all other ``PyObject*``, it assumes that there should be no references owned by the function when the function terminates. It will assume this behavior for any function (or call through a function pointer) that returns a ``PyObject*``. It is possible to override this behavior using custom compiler attributes as follows: Marking functions that return borrowed references ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The checker provides a custom GCC attribute: .. code-block:: c __attribute__((cpychecker_returns_borrowed_ref)) which can be used to mark function declarations: .. code-block:: c /* The checker automatically defines this preprocessor name when creating the custom attribute: */ #if defined(WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE) #define CPYCHECKER_RETURNS_BORROWED_REF \ __attribute__((cpychecker_returns_borrowed_ref)) #else #define CPYCHECKER_RETURNS_BORROWED_REF #endif PyObject *foo(void) CPYCHECKER_RETURNS_BORROWED_REF; Given the above, the checker will assume that invocations of ``foo()`` are returning a borrowed reference (or NULL), rather than a new reference. It will also check that this is that case when verifying the implementation of ``foo()`` itself. Marking functions that steal references to their arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The checker provides a custom GCC attribute: .. code-block:: c __attribute__((cpychecker_steals_reference_to_arg(n))) which can be used to mark function declarations: .. code-block:: c /* The checker automatically defines this preprocessor name when creating the custom attribute: */ #if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE) #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) \ __attribute__((cpychecker_steals_reference_to_arg(n))) #else #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) #endif extern void foo(PyObject *obj) CPYCHECKER_STEALS_REFERENCE_TO_ARG(1); Given the above, the checker will assume that invocations of ``foo()`` steal a reference to the first argument (``obj``). It will also verify that this is the case when analyzing the implementation of ``foo()`` itself. More then one argument can be marked: .. code-block:: c extern void bar(int i, PyObject *obj, int j, PyObject *other) CPYCHECKER_STEALS_REFERENCE_TO_ARG(2) CPYCHECKER_STEALS_REFERENCE_TO_ARG(4); The argument indices are 1-based (the above example is thus referring to ``obj`` and to ``other``). All such arguments to the attribute should be ``PyObject*`` (or a pointer to a derived structure type). It is assumed that such references are stolen for all possible outcomes of the function - if a function can either succeed or fail, the reference is stolen in both possible worlds. Error-handling checking ----------------------- The checker has knowledge of much of the CPython C API, and will generate a trace tree containing many of the possible error paths. It will issue warnings for code that appears to not gracefully handle an error. (TODO: show example) As noted above, it assumes that any function that returns a ``PyObject*`` can return can either NULL (setting an exception), or a new reference. It knows about much of the other parts of the CPython C API, including many other functions that can fail. The checker will emit warnings for various events: * if it detects a dereferencing of a ``NULL`` value * if a ``NULL`` value is erroneously passed to various CPython API entrypoints which are known to implicitly dereference those arguments (which would lead to a segmentation fault if that code path were executed):: input.c: In function 'test': input.c:38:33: warning: calling PyString_AsString with NULL (gcc.VarDecl('repr_args')) as argument 1 at input.c:38 input.c:31:15: note: when PyObject_Repr() fails at: repr_args = PyObject_Repr(args); input.c:38:33: note: PyString_AsString() invokes Py_TYPE() on the pointer via the PyString_Check() macro, thus accessing (NULL)->ob_type input.c:27:1: note: graphical error report for function 'test' written out to 'input.c.test-refcount-errors.html' * if it detects that an uninitialized local variable has been used * if it detects access to an object that has been deallocated, or such an object being returned:: input.c: In function 'test': input.c:43:1: warning: returning pointer to deallocated memory input.c:29:15: note: when PyLong_FromLong() succeeds at: PyObject *tmp = PyLong_FromLong(0x1000); input.c:31:8: note: taking False path at: if (!tmp) { input.c:39:5: note: reaching: Py_DECREF(tmp); input.c:39:5: note: when taking False path at: Py_DECREF(tmp); input.c:39:5: note: reaching: Py_DECREF(tmp); input.c:39:5: note: calling tp_dealloc on PyLongObject allocated at input.c:29 at: Py_DECREF(tmp); input.c:42:5: note: reaching: return tmp; input.c:43:1: note: returning input.c:39:5: note: memory deallocated here input.c:27:1: note: graphical error report for function 'returning_dead_object' written out to 'input.c.test.html' Errors in exception-handling ---------------------------- The checker keeps track of the per-thread exception state. It will issue a warning about any paths through functions returning a ``PyObject*`` that return NULL for which the per-thread exception state has not been set:: input.c: In function 'test': input.c:32:5: warning: returning (PyObject*)NULL without setting an exception The checker does not emit the warning for cases where it is known that such behavior is acceptable. Currently this covers functions used as `tp_iternext `_ callbacks of a ``PyTypeObject``. If you have a helper function that always sets an exception, you can mark this property using a custom GCC attribute: .. code-block:: c __attribute__((cpychecker_sets_exception)) which can be used to mark function declarations. .. code-block:: c /* The checker automatically defines this preprocessor name when creating the custom attribute: */ #if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE) #define CPYCHECKER_SETS_EXCEPTION \ __attribute__((cpychecker_sets_exception)) #else #define CPYCHECKER_SETS_EXCEPTION #endif extern void raise_error(const char *msg) CPYCHECKER_SETS_EXCEPTION; Given the above, the checker will know that an exception is set whenever a call to `raise_error()` occurs. It will also verify that `raise_error()` actually behaves this way when compiling the implementation of `raise_error`. There is an analogous attribute for the case where a function returns a negative value to signify an error, where the exception state is set whenever a **negative** value is returned: .. code-block:: c __attribute__((cpychecker_negative_result_sets_exception)) which can be used to mark function declarations. .. code-block:: c /* The checker automatically defines this preprocessor name when creating the custom attribute: */ #if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE) #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \ __attribute__((cpychecker_negative_result_sets_exception)) #else #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION #endif extern int foo(void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; Given the above, the checker will know that an exception is raised whenever a call to `foo` returns a negative value. It will also verify that `foo` actually behaves this way when compiling the implementation of `foo`. The checker already knows about many of the functions within the CPython API which behave this way. Format string checking ---------------------- The checker will analyze some `Python APIs that take format strings `_ and detect mismatches between the number and types of arguments that are passed in, as compared with those described by the format string. It currently verifies the arguments to the following API entrypoints: * `PyArg_ParseTuple `_ * `PyArg_ParseTupleAndKeywords `_ * `PyArg_Parse `_ * `Py_BuildValue `_ * `PyObject_CallFunction `_ * `PyObject_CallMethod `_ along with the variants that occur if you define `PY_SSIZE_T_CLEAN` before `#include `. For example, type mismatches between ``int`` vs ``long`` can lead to flaws when the code is compiled on big-endian 64-bit architectures, where ``sizeof(int) != sizeof(long)`` and the in-memory layout of those types differs from what you might expect. The checker will also issue a warning if the list of keyword arguments in a call to PyArg_ParseTupleAndKeywords is not NULL-terminated. .. note:: All of the various "#" codes in these format strings are affected by the presence of the macro `PY_SSIZE_T_CLEAN`. If the macro was defined before including Python.h, the various lengths for these format codes are of C type `Py_ssize_t` rather than `int`. This behavior was clarified in the Python 3 version of the C API documentation, though the Python 2 version of the API docs leave the matter of which codes are affected somewhat ambiguous. Nevertheless, the API *does* work this way in Python 2: all format codes with a "#" do work this way. Internally, the C preprocessor converts such function calls into invocations of: * `_PyArg_ParseTuple_SizeT` * `_PyArg_ParseTupleAndKeywords_SizeT` The checker handles this behavior correctly, by checking "#" codes in the regular functions against `int` and those in the modified functions against `Py_ssize_t`. Associating PyTypeObject instances with compile-time types ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The "O!" format code to ``PyArg_ParseTuple`` takes a ``PyTypeObject`` followed by the address of an object. This second argument can point to a ``PyObject*``, but it can also point to a pointer to a derived class. For example, CPython's own implementation contains code like this: .. code-block:: c static PyObject * unicodedata_decomposition(PyObject *self, PyObject *args) { PyUnicodeObject *v; /* ...snip... */ if (!PyArg_ParseTuple(args, "O!:decomposition", &PyUnicode_Type, &v)) /* ...etc... */ in which the input argument is written out into the ``PyUnicodeObject*``, provided that it is indeed a unicode instance. When the cpychecker verifies the types in this format string it verifies that the run-time type of the ``PyTypeObject`` matches the compile-time type (``PyUnicodeObject *``). It is able to do this since it contains hard-coded associations between these worlds for all of Python's built-in types: for the above case, it "knows" that ``PyUnicode_Type`` is associated with ``PyUnicodeObject``. If you need to provide a similar association for an extension type, the checker provides a custom GCC attribute: .. code-block:: c __attribute__((cpychecker_type_object_for_typedef(typename))) which can be used to mark PyTypeObject instance, giving the name of the typedef that PyObject instances of that type can be safely cast to. .. code-block:: c /* The checker automatically defines this preprocessor name when creating the custom attribute: */ #if defined(WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE) #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) \ __attribute__((cpychecker_type_object_for_typedef(typename))) #else /* This handles the case where we're compiling with a "vanilla" compiler that doesn't supply this attribute: */ #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) #endif /* Define some PyObject subclass, as both a struct and a typedef */ struct OurObjectStruct { PyObject_HEAD /* other fields */ }; typedef struct OurObjectStruct OurExtensionObject; /* Declare the PyTypeObject, using the custom attribute to associate it with the typedef above: */ extern PyTypeObject UserDefinedExtension_Type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("OurExtensionObject"); Given the above, the checker will associate the given ``PyTypeObject`` with the given typedef. Verification of PyMethodDef tables ---------------------------------- The checker will verify the types within tables of `PyMethodDef `_ initializers: the callbacks are typically cast to ``PyCFunction``, but the exact type needs to correspond to the flags given. For example ``(METH_VARARGS | METH_KEYWORDS)`` implies a different function signature to the default, which the vanilla C compiler has no way of verifying. .. code-block:: c /* BUG: there's a mismatch between the signature of the callback and that implied by ml_flags below. */ static PyObject *widget_display(PyObject *self, PyObject *args); static PyMethodDef widget_methods[] = { {"display", (PyCFunction)widget_display, (METH_VARARGS | METH_KEYWORDS), /* ml_flags */ NULL}, {NULL, NULL, 0, NULL} /* terminator */ }; Given the above, the checker will emit an error like this:: input.c:59:6: warning: flags do not match callback signature for 'widget_display' within PyMethodDef table input.c:59:6: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag (3 arguments) input.c:59:6: note: actual type of underlying callback: struct PyObject * (struct PyObject *, struct PyObject *) (2 arguments) input.c:59:6: note: see http://docs.python.org/c-api/structures.html#PyMethodDef It will also warn about tables of ``PyMethodDef`` initializers that are lacking a ``NULL`` sentinel value to terminate the iteration: .. code-block:: c static PyMethodDef widget_methods[] = { {"display", (PyCFunction)widget_display, 0, /* ml_flags */ NULL}, /* BUG: this array is missing a NULL value to terminate the list of methods, leading to a possible segfault at run-time */ }; Given the above, the checker will emit this warning:: input.c:39:6: warning: missing NULL sentinel value at end of PyMethodDef table Additional tests ---------------- * the checker will verify the argument lists of invocations of `PyObject_CallFunctionObjArgs `_ and `PyObject_CallMethodObjArgs `_, checking that all of the arguments are of the correct type (PyObject* or subclasses), and that the list is NULL-terminated:: input.c: In function 'test': input.c:33:5: warning: argument 2 had type char[12] * but was expecting a PyObject* (or subclass) input.c:33:5: warning: arguments to PyObject_CallFunctionObjArgs were not NULL-terminated Limitations and caveats ----------------------- Compiling with the checker is significantly slower than with "vanilla" gcc. I have been focussing on correctness and features, rather than optimization. I hope that it will be possible to greatly speed up the checker via ahead-of-time compilation of the Python code (e.g. using Cython). The checker does not yet fully implement all of C: expect to see Python tracebacks when it encounters less common parts of the language. (We'll fix those bugs as we come to them) The checker has a rather simplistic way of tracking the flow through a function: it builds a tree of all possible traces of execution through a function. This brings with it some shortcomings: * In order to guarantee that the analysis terminates, the checker will only track the first time through any loop, and stop analysing that trace for subsequent iterations. This appears to be good enough for detecting many kinds of reference leaks, especially in simple wrapper code, but is clearly suboptimal. * In order to avoid combinatorial explosion, the checker will stop analyzing a function once the trace tree gets sufficiently large. When it reaches this cutoff, a warning is issued:: input.c: In function 'add_module_objects': input.c:31:1: note: this function is too complicated for the reference-count checker to analyze To increase this limit, see the :option:`--maxtrans` option. * The checker doesn't yet match up similar traces, and so a single bug that affects multiple traces in the trace tree can lead to duplicate error reports. Only a subset of the CPython API has been modelled so far. The functions known to the checker are: `PyArg_Parse and _PyArg_Parse_SizeT `_, `PyArg_ParseTuple and _PyArg_ParseTuple_SizeT `_, `PyArg_ParseTupleAndKeywords and _PyArg_ParseTupleAndKeywords_SizeT `_, `PyArg_UnpackTuple `_, `Py_AtExit `_, `PyBool_FromLong `_, `Py_BuildValue and _Py_BuildValue_SizeT `_, `PyCallable_Check `_, `PyCapsule_GetPointer `_, `PyCObject_AsVoidPtr `_, `PyCObject_FromVoidPtr `_, `PyCObject_FromVoidPtrAndDesc `_, `PyCode_New `_, `PyDict_GetItem `_, `PyDict_GetItemString `_, `PyDict_New `_, `PyDict_SetItem `_, `PyDict_SetItemString `_, `PyDict_Size `_, `PyErr_Format `_, `PyErr_NewException `_, `PyErr_NoMemory `_, `PyErr_Occurred `_, `PyErr_Print `_, `PyErr_PrintEx `_, `PyErr_SetFromErrno `_, `PyErr_SetFromErrnoWithFilename `_, `PyErr_SetNone `_, `PyErr_SetObject `_, `PyErr_SetString `_, `PyErr_WarnEx `_, `PyEval_CallMethod`, `PyEval_CallObjectWithKeywords`, `PyEval_InitThreads `_, `PyEval_RestoreThread `_, `PyEval_SaveThread `_, `Py_FatalError `_, `PyFile_SoftSpace `_, `PyFile_WriteObject `_, `PyFile_WriteString `_, `Py_Finalize `_, `PyFrame_New`, `Py_GetVersion `_, `PyGILState_Ensure `_, `PyGILState_Release `_, `PyImport_AddModule `_, `PyImport_AppendInittab `_, `PyImport_ImportModule `_, `Py_Initialize `_, `Py_InitModule4_64`, `PyInt_AsLong `_, `PyInt_FromLong `_, `PyList_Append `_, `PyList_GetItem `_, `PyList_New `_, `PyList_SetItem `_, `PyList_Size `_, `PyLong_FromLong `_, `PyLong_FromLongLong `_, `PyLong_FromString `_, `PyLong_FromVoidPtr `_, `PyMapping_Size `_, `PyMem_Free `_, `PyMem_Malloc `_, `PyModule_AddIntConstant `_, `PyModule_AddObject `_, `PyModule_AddStringConstant `_,_, `PyModule_GetDict `_, `PyNumber_Int `_, `PyNumber_Remainer `_, `PyObject_AsFileDescriptor `_, `PyObject_Call `_, `PyObject_CallFunction and _PyObject_CallFunction_SizeT `_, `PyObject_CallFunctionObjArgs `_, `PyObject_CallMethod and _PyObject_CallMethod_SizeT `_, `PyObject_CallMethodObjArgs `_, `PyObject_CallObject `_, `PyObject_GetAttr `_, `PyObject_GetAttrString `_, `PyObject_GetItem `_, `PyObject_GenericGetAttr `_, `PyObject_GenericSetAttr `_, `PyObject_HasAttrString `_, `PyObject_IsTrue `_, `_PyObject_New`, `PyObject_Repr `_, `PyObject_SetAttr `_, `PyObject_SetAttrString `_, `PyObject_Str `_, `PyOS_snprintf `_, `PyRun_SimpleFileExFlags `_, `PyRun_SimpleStringFlags `_, `PySequence_Concat `_, `PySequence_DelItem `_, `PySequence_GetItem `_, `PySequence_GetSlice `_, `PySequence_SetItem `_, `PySequence_Size `_, `PyString_AsString `_, `PyString_Concat `_, `PyString_ConcatAndDel `_, `PyString_FromFormat `_, `PyString_FromString `_, `PyString_FromStringAndSize `_, `PyString_InternFromString `_, `PyString_Size `_, `PyStructSequence_InitType`, `PyStructSequence_New`, `PySys_GetObject `_, `PySys_SetObject `_, `PyTraceBack_Here`, `PyTuple_GetItem `_, `PyTuple_New `_, `PyTuple_Pack `_, `PyTuple_SetItem `_, `PyTuple_Size `_, `PyType_IsSubtype `_, `PyType_Ready `_, `PyUnicodeUCS4_AsUTF8String `_, `PyUnicodeUCS4_DecodeUTF8 `_, `PyWeakref_GetObject `_ The checker also has some knowledge about these SWIG-generated functions: `SWIG_Python_ErrorType`, `SWIG_Python_SetErrorMsg` and of this Cython-generated function: `__Pyx_GetStdout` Ideas for future tests ---------------------- Here's a list of some other C coding bugs I intend for the tool to detect: * tp_traverse errors (which can mess up the garbage collector); missing it altogether, or omitting fields * errors in GIL-handling * lock/release mismatches * missed opportunities to release the GIL (e.g. compute-intensive functions; functions that wait on IO/syscalls) Ideas for other tests are most welcome (patches even more so!) We will probably need various fallbacks and suppression modes for turning off individual tests (perhaps pragmas, perhaps compile-line flags, etc) Reusing this code for other projects ------------------------------------ It may be possible to reuse the analysis engine from cpychecker for other kinds of analysis - hopefully the python-specific parts are relatively self-contained. Email the `gcc-python-plugin's mailing list `_ if you're interested in adding verifiers for other kinds of code. Common mistakes --------------- Here are some common mistakes made using the CPython extension API, along with the fixes. Missing `Py_INCREF()` on `Py_None` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following is typically incorrect: a method implementation is required to return a new reference, but this code isn't incrementing the reference count on Py_None. .. code-block:: c PyObject* some_method(PyObject *self, PyObject *args) { [...snip...] /* BUG: loses a reference to Py_None */ return Py_None; } If called enough, this could cause Py_None to be deallocated, crashing the interpreter:: Fatal error: deallocating None The `Py_RETURN_NONE `_ macro takes care of incrementing the reference count for you: .. code-block:: c PyObject* some_method(PyObject *self, PyObject *args) { [...snip...] /* Fixed version of the above: */ Py_RETURN_NONE; } Reference leak in Py_BuildValue ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Py_BuildValue `_ with "O" adds a new reference on the object for use by the new tuple, hence the following code leaks the reference already owned on the object: .. code-block:: c /* BUG: reference leak: */ return Py_BuildValue("O", some_object_we_own_a_ref_on); `Py_BuildValue `_ with "N" steals the reference (and copes with it being NULL by propagating the exception): .. code-block:: c /* Fixed version of the above: */ return Py_BuildValue("N", some_object_we_own_a_ref_on); .. TODO: other examples? - constructing a list gcc-python-plugin-0.17/docs/diagnostics.rst000066400000000000000000000105261342215241600210050ustar00rootroot00000000000000.. Copyright 2011-2012, 2017 David Malcolm Copyright 2011-2012, 2017 Red Hat, Inc. This 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 . Generating custom errors and warnings ===================================== .. py:function:: gcc.warning(location, message, option=None) Emits a compiler warning at the given :py:class:`gcc.Location`, potentially controlled by a :py:class:`gcc.Option`. If no option is supplied (or `None` is supplied), then the warning is an unconditional one, always issued:: gcc.warning(func.start, 'this is an unconditional warning') .. code-block:: bash $ ./gcc-with-python script.py input.c input.c:25:1: warning: this is an unconditional warning [enabled by default] and will be an error if `-Werror` is supplied as a command-line argument to GCC: .. code-block:: bash $ ./gcc-with-python script.py -Werror input.c input.c:25:1: error: this is an unconditional warning [-Werror] It's possible to associate the warning with a command-line option, so that it is controlled by that option. For example, given this Python code:: gcc.warning(func.start, 'Incorrect formatting', gcc.Option('-Wformat')) if the given warning is enabled, a warning will be printed to stderr: .. code-block:: bash $ ./gcc-with-python script.py input.c input.c:25:1: warning: incorrect formatting [-Wformat] If the given warning is being treated as an error (through the usage of `-Werror`), then an error will be printed: .. code-block:: bash $ ./gcc-with-python script.py -Werror input.c input.c:25:1: error: incorrect formatting [-Werror=format] cc1: all warnings being treated as errors .. code-block:: bash $ ./gcc-with-python script.py -Werror=format input.c input.c:25:1: error: incorrect formatting [-Werror=format] cc1: some warnings being treated as errors If the given warning is disabled, the warning will not be printed: .. code-block:: bash $ ./gcc-with-python script.py -Wno-format input.c .. note:: Due to the way GCC implements some options, it's not always possible for the plugin to fully disable some warnings. See :py:attr:`gcc.Option.is_enabled` for more information. The function returns a boolean, indicating whether or not anything was actually printed. .. py:function:: gcc.error(location, message) Emits a compiler error at the given :py:class:`gcc.Location`. For example:: gcc.error(func.start, 'something bad was detected') would lead to this error being printed to stderr: .. code-block:: bash $ ./gcc-with-python script.py input.c input.c:25:1: error: something bad was detected .. py:function:: gcc.permerror(loc, str) This is a wrapper around GCC's `permerror` function. Expects an instance of :py:class:`gcc.Location` (not None) and a string Emit a "permissive" error at that location, intended for things that really ought to be errors, but might be present in legacy code. In theory it's suppressable using "-fpermissive" at the GCC command line (which turns it into a warning), but this only seems to be legal for C++ source files. Returns True if the warning was actually printed, False otherwise .. py:function:: gcc.inform(location, message) This is a wrapper around GCC's `inform` function. Expects an instance of :py:class:`gcc.Location` or :py:class:`gcc.RichLocation`, (not None) and a string Emit an informational message at that location. For example:: gcc.inform(stmt.loc, 'this is where X was defined') would lead to this informational message being printed to stderr: .. code-block:: bash $ ./gcc-with-python script.py input.c input.c:23:3: note: this is where X was defined gcc-python-plugin-0.17/docs/examples.rst000066400000000000000000000105551342215241600203160ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . Example scripts =============== There are various sample scripts located in the `examples` subdirectory. Once you've built the plugin (with `make`), you can run them via: .. code-block:: bash $ ./gcc-with-python examples/NAME-OF-SCRIPT.py test.c `show-docs.py` -------------- A trivial script to make it easy to read the builtin documentation for the gcc API: .. code-block:: bash $ ./gcc-with-python examples/show-docs.py test.c with this source: .. literalinclude:: ../examples/show-docs.py :lines: 17- :language: python giving output:: Help on built-in module gcc: NAME gcc FILE (built-in) CLASSES __builtin__.object BasicBlock Cfg Edge Function Gimple (truncated) `show-passes.py` ---------------- You can see the passes being executed via: .. code-block:: bash $ ./gcc-with-python examples/show-passes.py test.c This is a simple script that registers a trivial callback: .. literalinclude:: ../examples/show-passes.py :lines: 17- :language: python Sample output, showing passes being called on two different functions (`main` and `helper_function`):: (gcc.GimplePass(name='*warn_unused_result'), gcc.Function('main')) (gcc.GimplePass(name='omplower'), gcc.Function('main')) (gcc.GimplePass(name='lower'), gcc.Function('main')) (gcc.GimplePass(name='eh'), gcc.Function('main')) (gcc.GimplePass(name='cfg'), gcc.Function('main')) (gcc.GimplePass(name='*warn_function_return'), gcc.Function('main')) (gcc.GimplePass(name='*build_cgraph_edges'), gcc.Function('main')) (gcc.GimplePass(name='*warn_unused_result'), gcc.Function('helper_function')) (gcc.GimplePass(name='omplower'), gcc.Function('helper_function')) (gcc.GimplePass(name='lower'), gcc.Function('helper_function')) (gcc.GimplePass(name='eh'), gcc.Function('helper_function')) (gcc.GimplePass(name='cfg'), gcc.Function('helper_function')) [...truncated...] `show-gimple.py` ---------------- A simple script for viewing each function in the source file after it's been converted to "GIMPLE" form, using GraphViz to visualize the control flow graph: .. code-block:: bash $ ./gcc-with-python examples/show-gimple.py test.c It will generate a file `test.png` for each function, and opens it in an image viewer. .. figure:: sample-gimple-cfg.png :scale: 50 % :alt: image of a control flow graph in GIMPLE form The Python code for this is: .. literalinclude:: ../examples/show-gimple.py :lines: 19- :language: python `show-ssa.py` ------------- This is similar to `show-gimple.py`, but shows each function after the GIMPLE has been converted to Static Single Assignment form ("SSA"): .. code-block:: bash $ ./gcc-with-python examples/show-ssa.py test.c As before, it generates an image file for each function and opens it in a viewer. .. figure:: sample-gimple-ssa-cfg.png :scale: 50 % :alt: image of a control flow graph in GIMPLE SSA form The Python code for this is: .. literalinclude:: ../examples/show-ssa.py :lines: 17- :language: python `show-callgraph.py` ------------------- This simple script sends GCC's interprocedural analysis data through GraphViz. .. code-block:: bash $ ./gcc-with-python examples/show-callgraph.py test.c It generates an image file showing which functions call which other functions, and opens it in a viewer. .. figure:: sample-callgraph.png :alt: image of a call graph The Python code for this is: .. literalinclude:: ../examples/show-callgraph.py :lines: 18- :language: python gcc-python-plugin-0.17/docs/gcc-overview.rst000066400000000000000000000155321342215241600211000ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . Overview of GCC's internals =========================== .. TODO: Subprocesses .. TODO: Passes .. TODO: could use a diagram here showing all the passes To add a new compiler warning to GCC, it's helpful to have a high-level understanding of how GCC works, so here's the 10,000 foot view of how GCC turns source code into machine code. The short version is that GCC applies a series of optimization passes to your code, gradually converting it from a high-level representation into machine code, via several different internal representations. Each programming language supported by GCC has a "frontend", which parses the source files. For the case of C and C++, the preprocessor manipulates the code first before the frontend sees it. You can see the preprocessor output with the `-E` option. Exactly what happens in each frontend varies by language: some language frontends emit language-specific trees, and some convert to a language-independent tree representation known as `GENERIC`. In any case, we eventually we reach a representation known as `GIMPLE`. The GIMPLE representation contains simplified operations, with temporary variables added as necessary to avoid nested sub-expressions. For example, given this C code: .. literalinclude:: ../test.c :lines: 33-48 :language: c we can see a dump of a C-like representation of the GIMPLE form by passing `-fdump-tree-gimple` to the command-line: .. code-block:: bash $ gcc -fdump-tree-gimple test.c $ cat test.c.004t.gimple giving something like this: .. code-block:: c main (int argc, char * * argv) { const char * restrict D.3258; long unsigned int D.3259; long unsigned int D.3260; char * * D.3261; char * D.3262; const char * restrict D.3263; int D.3264; int i; D.3258 = (const char * restrict) &"argc: %i\n"[0]; printf (D.3258, argc); i = 0; goto ; : D.3259 = (long unsigned int) i; D.3260 = D.3259 * 8; D.3261 = argv + D.3260; D.3262 = *D.3261; D.3263 = (const char * restrict) &"argv[%i]: %s\n"[0]; printf (D.3263, D.3262); i = i + 1; : if (i < argc) goto ; else goto ; : helper_function (); D.3264 = 0; return D.3264; } It's far easier to see the GIMPLE using: .. code-block:: bash ./gcc-with-python examples/show-gimple.py test.c which generates bitmaps showing the "control flow graph" of the functions in the file, with source on the left-hand side, and GIMPLE on the right-hand side: .. figure:: sample-gimple-cfg.png :scale: 50 % :alt: image of a control flow graph in GIMPLE form Each function is divided into "basic blocks". Each basic block consists of a straight-line sequence of code with a single entrypoint and exit: all branching happens between basic blocks, not within them. The basic blocks form a "control flow graph" of basic blocks, linked together by edges. Each block can contain a list of :py:class:`gcc.Gimple` statements. You can work with this representation from Python using :py:class:`gcc.Cfg` Once the code is in GIMPLE form, GCC then attempts a series of optimizations on it. Some of these optimizations are listed here: http://gcc.gnu.org/onlinedocs/gccint/Tree-SSA-passes.html If you're looking to add new compiler warnings, it's probably best to hook your code into these early passes. The GIMPLE representation actually has several forms: * an initial "high gimple" form, potentially containing certain high-level operations (e.g. control flow, exception handling) * the lower level gimple forms, as each of these operations are rewritten in lower-level terms (turning control flow from jumps into a CFG etc) * the SSA form of GIMPLE. In Static Single Assignment form, every variable is assigned to at most once, with additional versions of variables added to help track the impact of assignments on the data flowing through a function. See http://gcc.gnu.org/onlinedocs/gccint/SSA.html You can tell what form a function is in by looking at the flags of the current pass. For example:: if ps.properties_provided & gcc.PROP_cfg: # ...then this gcc.Function ought to have a gcc.Cfg: do_something_with_cfg(fn.cfg) if ps.properties_provided & gcc.PROP_ssa: # ...then we have SSA data do_something_with_ssa(fn) Here's our example function, after conversion to GIMPLE SSA: .. code-block:: bash ./gcc-with-python examples/show-ssa.py test.c .. figure:: sample-gimple-ssa-cfg.png :scale: 50 % :alt: image of a control flow graph in GIMPLE SSA form You can see that the local variable `i` has been split into three versions: * `i_4`, assigned to in block 2 * `i_11`, assigned to at the end of block 3 * `i_1`, assigned to at the top of block 4. As is normal with SSA, GCC inserts fake functions known as "PHI" at the start of basic blocks where needed in order to merge the multiple possible values of a variable. You can see one in our example at the top of the loop in block 4: .. code-block:: c i_1 = PHI where i_1 either gets the value of i_4, or of i_11, depending on whether we reach here via block 2 (at the start of the iteration) or block 3 (continuing the "for" loop). After these optimizations passes are done, GCC converts the GIMPLE SSA representation into a lower-level representation known as Register Transfer Language (RTL). This is probably too low-level to be of interest to those seeking to add new compiler warnings: at this point it's attempting to work with the available opcodes and registers on the target CPU with the aim of generating efficient machine code. See http://gcc.gnu.org/onlinedocs/gccint/RTL.html The RTL form uses the same Control Flow Graph machinery as the GIMPLE representation, but with RTL expressions within the basic blocks. Once in RTL, GCC applies a series of further optimizations, before finally generating assembly language (which it submits to `as`, the GNU assembler): http://gcc.gnu.org/onlinedocs/gccint/RTL-passes.html You can see the assembly language using the `-S` command line option. .. code-block:: bash $ ./gcc -S test.c $ cat test.s gcc-python-plugin-0.17/docs/gcc-with-python.rst000066400000000000000000000003721342215241600215200ustar00rootroot00000000000000Usage:: gcc-with-python PATH_TO_PYTHON_SCRIPT GCC PARAMETERS `gcc-with-python` is a helper script which invokes GCC, whilst loading the Python plugin for GCC, running the given python script. Example:: gcc-with-python show-ssa.py example.c gcc-python-plugin-0.17/docs/getting-involved.rst000066400000000000000000000430461342215241600217660ustar00rootroot00000000000000.. Copyright 2012, 2017 David Malcolm Copyright 2012, 2017 Red Hat, Inc. This 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 . Getting Involved ================ The plugin's web site is this GitHub repository: https://github.com/davidmalcolm/gcc-python-plugin The primary place for discussion of the plugin is the mailing list: https://fedorahosted.org/mailman/listinfo/gcc-python-plugin A pre-built version of the HTML documentation can be seen at: http://readthedocs.org/docs/gcc-python-plugin/en/latest/index.html The project's mailing list is here: https://fedorahosted.org/mailman/listinfo/gcc-python-plugin Ideas for using the plugin -------------------------- Here are some ideas for possible uses of the plugin. Please email the plugin's mailing list if you get any of these working (or if you have other ideas!). Some guesses as to the usefulness and difficulty level are given in parentheses after some of the ideas. Some of them might require new attributes, methods and/or classes to be added to the plugin (to expose more of GCC internals), but you can always ask on the mailing list if you need help. * extend the libcpychecker code to add checking for the standard C library. For example, given this buggy C code: .. code-block:: c int foo() { FILE *src, *dst; src = fopen("source.txt", "r"); if (!src) return -1; dst = fopen("dest.txt", "w"); if (!dst) return -1; /* <<<< BUG: this error-handling leaks "src" */ /* etc, copy src to dst (or whatever) */ } it would be great if the checker could emit a compile-time warning about the buggy error-handling path above (or indeed any paths through functions that leak `FILE*`, file descriptors, or other resources). The way to do this (I think) is to add a new `Facet` subclass to libcpychecker, analogous to the `CPython` facet subclass that already exists (though the facet handling is probably rather messy right now). (useful but difficult, and a lot of work) * extend the libcpychecker code to add checking for other libraries. For example: * reference-count checking within `glib `_ and gobject (useful for commonly-used C libraries but difficult, and a lot of work) * detection of C++ variables with non-trivial constructors that will need to be run before `main` - globals and static locals (useful, ought to be fairly easy) * finding unused parameters in definitions of non-virtual functions, so that they can be removed - possibly removing further dead code. Some care would be needed for function pointers. (useful, ought to be fairly easy) * detection of bad format strings (see e.g. https://lwn.net/Articles/478139/ ) * compile gcc's own test suite with the cpychecker code, to reuse their coverage of C and thus shake out more bugs in the checker (useful and easy) * a new `PyPy gc root finder `_, running inside GCC (useful for PyPy, but difficult) * reimplement `GCC-XML `_ in Python (probably fairly easy, but does anyone still use GCC-XML now that GCC supports plugins?) * .gir generation for `GObject Introspection `_ (unknown if the GNOME developers are actually interested in this though) * create an interface that lets you view the changing internal representation of each function as it's modified by the various optimization pases: lets you see which passes change a given function, and what the changes are (might be useful as a teaching tool, and for understanding GCC) * add array bounds checking to C (to what extent can GCC already do this?) * `taint mode `_ for GCC! e.g. detect usage of data from network/from disk/etc; identify certain data as untrusted, and track how it gets used; issue a warning (very useful, but very difficult: how does untainting work? what about pointers and memory regions? is it just too low-level?) * implement something akin to PyPy's pygame-based viewer, for viewing control flow graphs and tree structures: an OpenGL-based GUI giving a fast, responsive UI for navigating the data - zooming, panning, search, etc. (very useful, and fairly easy) * `generation of pxd files for Cython `_ (useful for Cython, ought to be fairly easy) * reverse-engineering a .py or .pyx file from a .c file: turning legacy C Python extension modules back into Python or Cython sources (useful but difficult) Tour of the C code ------------------ The plugin's C code heavily uses Python's extension API, and so it's worth knowing this API if you're going to hack on this part of the project. A good tutorial for this can be seen here: http://docs.python.org/extending/index.html and detailed notes on it are here: http://docs.python.org/c-api/index.html Most of the C "glue" for creating classes and registering their methods and attributes is autogenerated. Simple C one-liners tend to appear in the autogenerated C files, whereas longer implementations are broken out into a hand-written C file. Adding new methods and attributes to the classes requires editing the appropriate generate-\*.py script to wire up the new entrypoint. For very simple attributes you can embed the C code directly there, but anything that's more than a one-liner should have its implementation in the relevant C file. For example, to add new methods to a :py:class:`gcc.Cfg` you'd edit: * `generate-cfg-c.py` to add the new methods and attributes to the relevant tables of callbacks * `gcc-python-wrappers.h` to add declarations of the new C functions * `gcc-python-cfg.c` to add the implementations of the new C functions Please try to make the API "Pythonic". My preference with getters is that if the implementation is a simple field lookup, it should be an attribute (the "getter" is only implicit, existing at the C level):: print(bb.loopcount) whereas if getting the result involves some work, it should be an explicit method of the class (where the "getter" is explicit at the Python level):: print(bb.get_loop_count()) Using the plugin to check itself -------------------------------- Given that the `cpychecker` code implements new error-checking for Python C code, and that the underlying plugin is itself an example of such code, it's possible to build the plugin once, then compile it with itself (using CC=gcc-with-cpychecker as a Makefile variable:: $ make CC=/path/to/a/clean/build/of/the/plugin/gcc-with-cpychecker Unfortunately it doesn't quite compile itself cleanly right now. .. TODO: add notes on the current known problems Test suite ---------- There are three test suites: * `testcpybuilder.py`: a minimal test suite which is used before the plugin itself is built. This verifies that the `cpybuilder` code works. * `make test-suite` (aka `run-test-suite.py`): a test harness and suite which was written for this project. See the notes below on patches. * `make testcpychecker` and `testcpychecker.py`: a suite based on Python's `unittest` module Debugging the plugin's C code ----------------------------- The `gcc` binary is a harness that launches subprocesses, so it can be fiddly to debug. Exactly what it launches depend on the inputs and options. Typically, the subprocesses it launches are (in order): * `cc1` or `cc1plus`: The C or C++ compiler, generating a .s assember file. * `as`: The assembler, converting a .s assembler file to a .o object file. * `collect2`: The linker, turning one or more .o files into an executable (if you're going all the way to building an `a.out`-style executable). The easiest way to debug the plugin is to add these parameters to the gcc command line (e.g. to the end):: -wrapper gdb,--args Note the lack of space between the comma and the `--args`. e.g.:: ./gcc-with-python examples/show-docs.py test.c -wrapper gdb,--args This will invoke each of the subprocesses in turn under gdb: e.g. `cc1`, `as` and `collect2`; the plugin runs with `cc1` (`cc1plus` for C++ code). For example:: $ ./gcc-with-cpychecker -c -I/usr/include/python2.7 demo.c -wrapper gdb,--args GNU gdb (GDB) Fedora 7.6.50.20130731-19.fc20 [...snip...] Reading symbols from /usr/libexec/gcc/x86_64-redhat-linux/4.8.2/cc1...Reading symbols from /usr/lib/debug/usr/libexec/gcc/x86_64-redhat-linux/4.8.2/cc1.debug...done. done. (gdb) run [...etc...] Another way to do it is to add "-v" to the gcc command line (verbose), so that it outputs the commands that it's running. You can then use this to launch:: $ gdb --args ACTUAL PROGRAM WITH ACTUAL ARGS to debug the subprocess that actually loads the Python plugin. For example:: $ gcc -v -fplugin=$(pwd)/python.so -fplugin-arg-python-script=test.py test.c on my machine emits this:: Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.6.1/lto-wrapper Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux Thread model: posix gcc version 4.6.1 20110908 (Red Hat 4.6.1-9) (GCC) COLLECT_GCC_OPTIONS='-v' '-fplugin=/home/david/coding/gcc-python/gcc-python/contributing/python.so' '-fplugin-arg-python-script=test.py' '-mtune=generic' '-march=x86-64' /usr/libexec/gcc/x86_64-redhat-linux/4.6.1/cc1 -quiet -v -iplugindir=/usr/lib/gcc/x86_64-redhat-linux/4.6.1/plugin test.c -iplugindir=/usr/lib/gcc/x86_64-redhat-linux/4.6.1/plugin -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -version -fplugin=/home/david/coding/gcc-python/gcc-python/contributing/python.so -fplugin-arg-python-script=test.py -o /tmp/cc1Z3b95.s (output of the script follows) This allows us to see the line in which `cc1` is invoked: in the above example, it's the final line before the output from the script:: /usr/libexec/gcc/x86_64-redhat-linux/4.6.1/cc1 -quiet -v -iplugindir=/usr/lib/gcc/x86_64-redhat-linux/4.6.1/plugin test.c -iplugindir=/usr/lib/gcc/x86_64-redhat-linux/4.6.1/plugin -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -version -fplugin=/home/david/coding/gcc-python/gcc-python/contributing/python.so -fplugin-arg-python-script=test.py -o /tmp/cc1Z3b95.s We can then take this line and rerun this subprocess under gdb by adding `gdb --args` to the front like this:: $ gdb --args /usr/libexec/gcc/x86_64-redhat-linux/4.6.1/cc1 -quiet -v -iplugindir=/usr/lib/gcc/x86_64-redhat-linux/4.6.1/plugin test.c -iplugindir=/usr/lib/gcc/x86_64-redhat-linux/4.6.1/plugin -quiet -dumpbase test.c -mtune=generic -march=x86-64 -auxbase test -version -fplugin=/home/david/coding/gcc-python/gcc-python/contributing/python.so -fplugin-arg-python-script=test.py -o /tmp/cc1Z3b95.s This approach to obtaining a debuggable process doesn't seem to work in the presence of `ccache`, in that it writes to a temporary directory with a name that embeds the process ID each time, which then gets deleted. I've worked around this by uninstalling ccache, but apparently setting:: CCACHE_DISABLE=1 before invoking `gcc -v` ought to also work around this. I've also been running into this error from gdb:: [Thread debugging using libthread_db enabled] Cannot find new threads: generic error Apparently this happens when debugging a process that uses dlopen to load a library that pulls in libpthread (as does gcc when loading in my plugin), and a workaround is to link cc1 with -lpthread The workaround I've been using (to avoid the need to build my own gcc) is to use LD_PRELOAD, either like this:: LD_PRELOAD=libpthread.so.0 gdb --args ARGS GO HERE... or this:: (gdb) set environment LD_PRELOAD libpthread.so.0 Handy tricks ++++++++++++ Given a (PyGccTree*) named "self":: (gdb) call debug_tree(self->t) will use GCC's prettyprinter to dump the embedded (tree*) and its descendants to stderr; it can help to put a breakpoint on that function too, to explore the insides of that type. Patches ------- The project doesn't have any copyright assignment requirement: you get to keep copyright in any contributions you make, though AIUI there's an implicit licensing of such contributions under the GPLv3 or later, given that any contribution is a derived work of the plugin, which is itself licensed under the GPLv3 or later. I'm not a lawyer, though. The Python code within the project is intended to be usable with both Python 2 and Python 3 without running 2to3: please stick to the common subset of the two languages. For example, please write print statements using parentheses:: print(42) Under Python 2 this is a `print` statement with a parenthesized number: (42) whereas under Python 3 this is an invocation of the `print` function. Please try to stick `PEP-8 `_ for Python code, and to `PEP-7 `_ for C code (rather than the GNU coding conventions). In C code, I strongly prefer to use multiline blocks throughout, even where single statements are allowed (e.g. in an "if" statement):: if (foo()) { bar(); } as opposed to:: if (foo()) bar(); since this practice prevents introducing bugs when modifying such code, and the resulting "diff" is much cleaner. A good patch ought to add test cases for the new code that you write, and documentation. The test cases should be grouped in appropriate subdirectories of "tests". Each new test case is a directory with an: * `input.c` (or `input.cc` for C++) * `script.py` exercising the relevant Python code * `stdout.txt` containing the expected output from the script. For more realistic examples of test code, put them below `tests/examples`; these can be included by reference from the docs, so that we have documentation that's automatically verified by `run-test-suite.py`, and users can use this to see the relationship between source-code constructs and the corresponding Python objects. More information can be seen in `run-test-suite.py` By default, `run-test-suite.py` will invoke all the tests. You can pass it a list of paths and it run all tests found in those paths and below. You can generate the "gold" stdout.txt by hacking up this line in run-test-suite.py:: out.check_for_diff(out.actual, err.actual, p, args, 'stdout', 0) so that the final 0 is a 1 (the "writeback" argument to `check_for_diff`). There may need to be a non-empty stdout.txt file in the directory for this to take effect though. Unfortunately, this approach over-specifies the selftests, making them rather "brittle". Improvements to this approach would be welcome. To directly see the GCC command line being invoked for each test, and to see the resulting stdout and stderr, add `--show` to the arguments of `run-test-suite.py`. For example:: $ python run-test-suite.py tests/plugin/diagnostics --show tests/plugin/diagnostics: gcc -c -o tests/plugin/diagnostics/output.o -fplugin=/home/david/coding/gcc-python-plugin/python.so -fplugin-arg-python-script=tests/plugin/diagnostics/script.py -Wno-format tests/plugin/diagnostics/input.c tests/plugin/diagnostics/input.c: In function 'main': tests/plugin/diagnostics/input.c:23:1: error: this is an error (with positional args) tests/plugin/diagnostics/input.c:23:1: error: this is an error (with keyword args) tests/plugin/diagnostics/input.c:25:1: warning: this is a warning (with positional args) [-Wdiv-by-zero] tests/plugin/diagnostics/input.c:25:1: warning: this is a warning (with keyword args) [-Wdiv-by-zero] tests/plugin/diagnostics/input.c:23:1: error: a warning with some embedded format strings %s and %i tests/plugin/diagnostics/input.c:25:1: warning: this is an unconditional warning [enabled by default] tests/plugin/diagnostics/input.c:25:1: warning: this is another unconditional warning [enabled by default] expected error was found: option must be either None, or of type gcc.Option tests/plugin/diagnostics/input.c:23:1: note: This is the start of the function tests/plugin/diagnostics/input.c:25:1: note: This is the end of the function OK 1 success; 0 failures; 0 skipped Documentation ============= We use Sphinx for documentation, which makes it easy to keep the documentation up-to-date. For notes on how to document Python in the .rst form accepted by Sphinx, see e.g.: http://sphinx.pocoo.org/domains.html#the-python-domain gcc-python-plugin-0.17/docs/gimple.rst000066400000000000000000000330041342215241600177470ustar00rootroot00000000000000.. Copyright 2011, 2012, 2013 David Malcolm Copyright 2011, 2012, 2013 Red Hat, Inc. This 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 . Gimple statements ================= .. py:class:: gcc.Gimple A statement, in GCC's Gimple representation. The __str__ method is implemented using GCC's own pretty-printer for gimple, so e.g.:: str(stmt) might return:: 'D.3259 = (long unsigned int) i;' .. py:attribute:: loc Source code location of this statement, as a :py:class:`gcc.Location` (or None) .. py:attribute:: block The lexical block holding this statement, as a :py:class:`gcc.Tree` .. py:attribute:: exprtype The type of the main expression computed by this statement, as a :py:class:`gcc.Tree` (which might be :py:class:`gcc.VoidType`) .. py:attribute:: str_no_uid A string representation of this statement, like str(), but without including any internal UIDs. This is intended for use in selftests that compare output against some expected value, to avoid embedding values that change into the expected output. For example, given an assignment to a temporary, the `str(stmt)` for the gcc.GimpleAssign might be:: 'D.3259 = (long unsigned int) i;' where the UID "3259" is liable to change from compile to compile, whereas the `stmt.str_no_uid` has value:: 'D.xxxx = (long unsigned int) i;' which won't arbitrarily change each time. .. py:method:: walk_tree(callback, *args, **kwargs) Visit all :py:class:`gcc.Tree` nodes associated with this statement, potentially more than once each. This will visit both the left-hand-side and right-hand-side operands of the statement (if any), and recursively visit any of their child nodes. For each node, the callback is invoked, supplying the node, and any extra positional and keyword arguments passed to `walk_tree`:: callback(node, *args, **kwargs) If the callback returns a true value, the traversal stops, and that :py:class:`gcc.Tree` is the result of the call to `walk_tree`. Otherwise, the traversal continues, and `walk_tree` eventually returns `None`. .. Note that gimple.def contains useful summaries of what each gimple code means :py:class:`gcc.Gimple` has various subclasses, each corresponding to the one of the kinds of statement within GCC's internal representation. The following subclasses have been wrapped for use from Python scripts: ======================================= =================================== Subclass Meaning ======================================= =================================== :py:class:`gcc.GimpleAsm` One or more inline assembly statements :py:class:`gcc.GimpleAssign` An assignment of an expression to an l-value:: LHS = RHS1 EXPRCODE RHS2; :py:class:`gcc.GimpleCall` A function call:: [ LHS = ] FN(ARG1, ..., ARGN); :py:class:`gcc.GimpleCond` A conditional jump, of the form:: if (LHS EXPRCODE RHS) goto TRUE_LABEL else goto FALSE_LABEL; :py:class:`gcc.GimpleLabel` A label statement (jump target):: LABEL: :py:class:`gcc.GimpleNop` The "do nothing" statement :py:class:`gcc.GimplePhi` Used in the SSA passes:: LHS = PHI ; :py:class:`gcc.GimpleReturn` A "return" statement:: RETURN [RETVAL]; :py:class:`gcc.GimpleSwitch` A switch statement:: switch (INDEXVAR) { case LAB1: ...; break; ... case LABN: ...; break; default: ... } ======================================= =================================== There are some additional subclasses that have not yet been fully wrapped by the Python plugin (email the `gcc-python-plugin's mailing list `_ if you're interested in working with these): ======================================= =================================== Subclass Meaning ======================================= =================================== :py:class:`gcc.GimpleBind` A lexical scope :py:class:`gcc.GimpleCatch` An exception handler :py:class:`gcc.GimpleDebug` A debug statement :py:class:`gcc.GimpleEhDispatch` Used in exception-handling :py:class:`gcc.GimpleEhFilter` Used in exception-handling :py:class:`gcc.GimpleEhMustNotThrow` Used in exception-handling :py:class:`gcc.GimpleErrorMark` A dummy statement used for handling internal errors :py:class:`gcc.GimpleGoto` An unconditional jump :py:class:`gcc.GimpleOmpAtomicLoad` Used for implementing OpenMP :py:class:`gcc.GimpleOmpAtomicStore` (ditto) :py:class:`gcc.GimpleOmpContinue` (ditto) :py:class:`gcc.GimpleOmpCritical` (ditto) :py:class:`gcc.GimpleOmpFor` (ditto) :py:class:`gcc.GimpleOmpMaster` (ditto) :py:class:`gcc.GimpleOmpOrdered` (ditto) :py:class:`gcc.GimpleOmpParallel` (ditto) :py:class:`gcc.GimpleOmpReturn` (ditto) :py:class:`gcc.GimpleOmpSection` (ditto) :py:class:`gcc.GimpleOmpSections` (ditto) :py:class:`gcc.GimpleOmpSectionsSwitch` (ditto) :py:class:`gcc.GimpleOmpSingle` (ditto) :py:class:`gcc.GimpleOmpTask` (ditto) :py:class:`gcc.GimplePredict` A hint for branch prediction :py:class:`gcc.GimpleResx` Resumes execution after an exception :py:class:`gcc.GimpleTry` A try/catch or try/finally statement :py:class:`gcc.GimpleWithCleanupExpr` Internally used when generating GIMPLE ======================================= =================================== .. py:class:: gcc.GimpleAsm Subclass of :py:class:`gcc.Gimple`: a fragment of `inline assembler code `_. .. py:attribute:: string The inline assembler code, as a `str`. .. GIMPLE_ASM .. STRING is the string containing the assembly statements. .. I1 ... IN are the N input operands. .. O1 ... OM are the M output operands. .. C1 ... CP are the P clobber operands. .. L1 ... LQ are the Q label operands. */ .. py:class:: gcc.GimpleAssign Subclass of :py:class:`gcc.Gimple`: an assignment of an expression to an l-value:: LHS = RHS1 EXPRCODE RHS2; .. py:attribute:: lhs Left-hand-side of the assignment, as a :py:class:`gcc.Tree` .. py:attribute:: rhs The operands on the right-hand-side of the expression, as a list of :py:class:`gcc.Tree` instances (either of length 1 or length 2, depending on the expression). .. py:attribute:: exprcode The kind of the expression, as an :py:class:`gcc.Tree` subclass (the type itself, not an instance) .. py:class:: gcc.GimpleCall Subclass of :py:class:`gcc.Gimple`: an invocation of a function, potentially assigning the result to an l-value:: [ LHS = ] FN(ARG1, ..., ARGN); .. py:attribute:: lhs Left-hand-side of the assignment, as a :py:class:`gcc.Tree`, or `None` .. py:attribute:: rhs The operands on the right-hand-side of the expression, as a list of :py:class:`gcc.Tree` instances .. py:attribute:: fn The function being called, as a :py:class:`gcc.Tree` .. py:attribute:: fndecl The declaration of the function being called (if any), as a :py:class:`gcc.Tree` .. py:attribute:: args The arguments for the call, as a list of :py:class:`gcc.Tree` .. py:attribute:: noreturn (boolean) Has this call been marked as not returning? (e.g. a call to `exit`) .. py:class:: gcc.GimpleReturn Subclass of :py:class:`gcc.Gimple`: a "return" statement, signifying the end of a :py:class:`gcc.BasicBlock`:: RETURN [RETVAL]; .. py:attribute:: retval The return value, as a :py:class:`gcc.Tree`, or `None`. .. py:class:: gcc.GimpleCond Subclass of :py:class:`gcc.Gimple`: a conditional jump, of the form:: if (LHS EXPRCODE RHS) goto TRUE_LABEL else goto FALSE_LABEL .. py:attribute:: lhs Left-hand-side of the comparison, as a :py:class:`gcc.Tree` .. py:attribute:: exprcode The comparison predicate, as a :py:class:`gcc.Comparison` subclass (the type itself, not an instance). For example, the gcc.GimpleCond statement for this fragment of C code:: if (a == b) would have stmt.exprcode == gcc.EqExpr .. py:attribute:: rhs The right-hand-side of the comparison, as a :py:class:`gcc.Tree` .. py:attribute:: true_label The :py:class:`gcc.LabelDecl` node used as the jump target for when the comparison is true .. py:attribute:: false_label The :py:class:`gcc.LabelDecl` node used as the jump target for when the comparison is false Note that a C conditional of the form:: if (some_int) {suiteA} else {suiteB} is implicitly expanded to:: if (some_int != 0) {suiteA} else {suiteB} and this becomes a gcc.GimpleCond with `lhs` as the integer, `exprcode` as ``, and `rhs` as `gcc.IntegerCst(0)`. .. py:class:: gcc.GimplePhi Subclass of :py:class:`gcc.Gimple` used in the SSA passes: a "PHI" or "phoney" function, for merging the various possible values a variable can have based on the edge that we entered this :py:class:`gcc.BasicBlock` on:: LHS = PHI ; .. py:attribute:: lhs Left-hand-side of the assignment, as a :py:class:`gcc.SsaName` .. py:attribute:: args A list of (:py:class:`gcc.Tree`, :py:class:`gcc.Edge`) pairs representing the possible (expr, edge) inputs. Each `expr` is either a :py:class:`gcc.SsaName` or a :py:class:`gcc.Constant` .. py:class:: gcc.GimpleSwitch Subclass of :py:class:`gcc.Gimple`: a switch statement, signifying the end of a :py:class:`gcc.BasicBlock`:: switch (INDEXVAR) { case LAB1: ...; break; ... case LABN: ...; break; default: ... } .. py:attribute:: indexvar The index variable used by the switch statement, as a :py:class:`gcc.Tree` .. py:attribute:: labels The labels of the switch statement, as a list of :py:class:`gcc.CaseLabelExpr`. The initial label in the list is always the default. .. py:class:: gcc.GimpleLabel Subclass of :py:class:`gcc.Gimple`, representing a "label" statement:: .. py:attribute:: labels The underlying :py:class:`gcc.LabelDecl` node representing this jump target .. py:class:: gcc.GimpleAssign Subclass of :py:class:`gcc.Gimple`: an assignment of an expression to an l-value:: LHS = RHS1 EXPRCODE RHS2; .. py:attribute:: lhs Left-hand-side of the assignment, as a :py:class:`gcc.Tree` .. py:attribute:: rhs The operands on the right-hand-side of the expression, as a list of :py:class:`gcc.Tree` instances (either of length 1 or length 2, depending on the expression). .. py:attribute:: exprcode The kind of the expression, as an :py:class:`gcc.Tree` subclass (the type itself, not an instance) .. py:class:: gcc.GimpleNop Subclass of :py:class:`gcc.Gimple`, representing a "do-nothing" statement (a.k.a. "no operation"). .. Here's a dump of the class hierarchy, from help(gcc): .. Gimple .. GimpleAsm .. GimpleAssign .. GimpleBind .. GimpleCall .. GimpleCatch .. GimpleCond .. GimpleDebug .. GimpleEhDispatch .. GimpleEhFilter .. GimpleEhMustNotThrow .. GimpleErrorMark .. GimpleGoto .. GimpleLabel .. GimpleNop .. GimpleOmpAtomicLoad .. GimpleOmpAtomicStore .. GimpleOmpContinue .. GimpleOmpCritical .. GimpleOmpFor .. GimpleOmpMaster .. GimpleOmpOrdered .. GimpleOmpParallel .. GimpleOmpReturn .. GimpleOmpSection .. GimpleOmpSections .. GimpleOmpSectionsSwitch .. GimpleOmpSingle .. GimpleOmpTask .. GimplePhi .. GimplePredict .. GimpleResx .. GimpleReturn .. GimpleSwitch .. GimpleTry .. GimpleWithCleanupExpr gcc-python-plugin-0.17/docs/index.rst000066400000000000000000000056101342215241600176030ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . .. gcc-python-plugin documentation master file, created by sphinx-quickstart on Wed Jun 1 15:53:48 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. GCC Python plugin ================= Contents: .. toctree:: :maxdepth: 2 basics.rst gcc-overview.rst examples.rst working-with-c.rst locations.rst diagnostics.rst cfg.rst tree.rst gimple.rst passes.rst callbacks.rst attributes.rst cpychecker.rst success.rst getting-involved.rst misc.rst release-notes.rst appendices.rst This document describes the Python plugin I've written for GCC. In theory the plugin allows you to write Python scripts that can run inside GCC as it compiles code, exposing GCC's internal data structures as a collection of Python classes and functions. The bulk of the document describes the Python API it exposes. Hopefully this will be of use for writing domain-specific warnings, static analysers, and the like, and for rapid prototyping of new GCC features. I've tried to stay close to GCC's internal representation, but using classes. I hope that the resulting API is pleasant to work with. The plugin is a work-in-progress; the API may well change. Bear in mind that writing this plugin has been the first time I have worked with the insides of GCC. I have only wrapped the types I have needed, and within them, I've only wrapped properties that seemed useful to me. There may well be plenty of interesting class and properties for instances that can be added (patches most welcome!). I may also have misunderstood how things work. Most of my development has been against Python 2 (2.7, actually), but I've tried to make the source code of the plugin buildable against both Python 2 and Python 3 (3.2), giving separate python2.so and python3.so plugins. (I suspect that it's only possible to use one or the other within a particular invocation of "gcc", due to awkward dynamic-linker symbol collisions between the two versions of Python). The plugin is Free Software, licensed under the GPLv3 (or later). Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` gcc-python-plugin-0.17/docs/locations.rst000066400000000000000000000066631342215241600205000ustar00rootroot00000000000000.. Copyright 2011-2012, 2017-2018 David Malcolm Copyright 2011-2012, 2017-2018 Red Hat, Inc. This 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 . Locations ========= .. py:function:: gccutils.get_src_for_loc(loc) Given a :py:class:`gcc.Location`, get the source line as a string (without trailing whitespace or newlines) .. py:class:: gcc.Location Wrapper around GCC's `location_t`, representing a location within the source code. Use :py:func:`gccutils.get_src_for_loc` to get at the line of actual source code. The output from __repr__ looks like this:: gcc.Location(file='./src/test.c', line=42) The output from__str__ looks like this:: ./src/test.c:42 .. py:attribute:: file (string) Name of the source file (or header file) .. py:attribute:: line (int) Line number within source file (starting at 1, not 0) .. py:attribute:: column (int) Column number within source file (starting at 1, not 0) .. py:attribute:: in_system_header (bool) This attribute flags locations that are within a system header file. It may be of use when writing custom warnings, so that you can filter out issues in system headers, leaving just those within the user's code:: # Don't report on issues found in system headers: if decl.location.in_system_header: return .. py:method:: offset_column(self, offset) Generate a new :py:class:`gcc.Location` based on the caret location of this location, offsetting the column by the given amount. From GCC 6 onwards, these values can represent both a caret and a range, e.g.:: a = (foo && bar) ~~~~~^~~~~~~ .. py:method:: __init__(self, caret, start, finish) Construct a location, using the caret location of caret as the caret, and the start/finish of start and finish respectively:: compound_loc = gcc.Location(caret, start, finish) .. py:attribute:: caret (:py:class:`gcc.Location`) The caret location within this location. In the above example, the caret is on the first '&' character. .. py:attribute:: start (:py:class:`gcc.Location`) The start location of this range. In the above example, the start is on the opening parenthesis. .. py:attribute:: finish (:py:class:`gcc.Location`) The finish location of this range. In the above example, the finish is on the closing parenthesis. .. py:class:: gcc.RichLocation Wrapper around GCC's `rich_location`, representing one or more locations within the source code, and zero or more fix-it hints. .. note:: gcc.RichLocation is only available from GCC 6 onwards .. method:: add_fixit_replace(self, new_content) Add a fix-it hint, suggesting replacement of the content covered by range 0 of the rich location with new_content. gcc-python-plugin-0.17/docs/lto.rst000066400000000000000000000064451342215241600173010ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . Whole-program Analysis via Link-Time Optimization (LTO) ======================================================= You can enable GCC's "link time optimization" feature by passing `-flto`. When this is enabled, gcc adds extra sections to the compiled .o file containing the SSA-Gimple internal representation of every function, so that this SSA representation is available at link-time. This allows gcc to inline functions defined in one source file into functions defined in another source file at link time. Although the feature is intended for optimization, we can also use it for code analysis, and it's possible to run the Python plugin at link time. This means we can do interprocedural analysis across multiple source files. .. warning:: Running a gcc plugin from inside link-time optimization is rather novel, and you're more likely to run into bugs. See e.g. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54962 An invocation might look like this: .. code-block:: bash gcc \ -flto \ -flto-partition=none \ -v \ -fplugin=PATH/TO/python.so \ -fplugin-arg-python-script=PATH/TO/YOUR/SCRIPT.py \ INPUT-1.c \ INPUT-2.c \ ... INPUT-n.c Looking at the above options in turn: * `-flto` enables link-time optimization * `-flto-partition=none` : by default, gcc with LTO partitions the code and generates summary information for each partition, then combines the results of the summaries (known as "WPA" and "LTRANS" respectively). This appears to be of use for optimization, but to get at the function bodies, for static analysis, you should pass this option, which instead gathers all the code into one process. * `-v` means "verbose" and is useful for seeing all of the subprograms that gcc invokes, along with their command line options. Given the above options, you should see invocations of `cc1` (the C compiler), `collect2` (the linker) and `lto1` (the link-time optimizer). For example, .. code-block:: bash $ ./gcc-with-python \ examples/show-lto-supergraph.py \ -flto \ -flto-partition=none \ tests/examples/lto/input-*.c will render a bitmap of the supergraph like this: .. figure:: sample-supergraph.png :scale: 50 % :alt: image of a supergraph .. py:function:: gcc.is_lto() :rtype: bool Determine whether or not we're being invoked during link-time optimization (i.e. from within the `lto1` program) .. warning:: The underlying boolean is not set up until passes are being invoked: it is always `False` during the initial invocation of the Python script. gcc-python-plugin-0.17/docs/misc.rst000066400000000000000000000020301342215241600174200ustar00rootroot00000000000000.. Copyright 2011, 2012 David Malcolm Copyright 2011, 2012 Red Hat, Inc. This 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 . Miscellanea =========== The following odds and ends cover the more esoteric aspects of GCC, and are documented here for completeness. They may or may not be useful when writing scripts. .. toctree:: callgraph.rst lto.rst options.rst parameters.rst preprocessor.rst versions.rst rtl.rst gcc-python-plugin-0.17/docs/new-html-error-report.png000066400000000000000000001440411342215241600226450ustar00rootroot00000000000000PNG  IHDR}+c pHYs+ IDATxw\_)T)ٰ;z')y6N^ODOݛq=H'#cGZxF;s уG\jZs̪g^-Omnjh_H6cY֩T`fsM/-=˵ 3'ڹn&O˃AkCŮk]&z]}<GwOS?*ǭ:ѣظ*c69k˚D9gVt/9Zgq,,\[ZGjq>Bڰu3 E1P; уZSo!4!Sޗo: `x|}WhXr-]9+AX_{Y\Fe<_;\#V;2x<,3D_o6aU[ "xUU&.'.?r:j:?rPR>Kh/+ElIN2.MU3U퇳 MV0q+S.Wid 1> `= 3 G4q b tPpA{aŔc!c" TJ%>ޠ~?!z4Tg{e!bʿV r#dN,v2Wȟi倩SAG>] C|q'x84D:3 ;SI |aa;Ȟ|_+999IGIvtwSD. _1FhBk{`0VkdMCB k\ {Yt"`m,ёd4aibm(ф(VKK=z[bѤ'ISُVx$@$i/~Ŕ)GyN#T΀h+ji`M݁.2uk_«gUEm-Joõϥކk9pjk*w [d;£/8;Op<Smo}sX[CxP]|kX3b]>@ӦiÁnָ?Сef\RK,֭hAM={0o!dTRK8±裘޷=zRMMMݻ)b6z hk~` =o+KKև32ۻsIYZfO9k].Oޓ)((T| #GOk'un$!0 iΝ.:ɉ4PX "vj:'Ϋ;ww᥮r: ~]gQsK mw=*)`&"g)-LcK4IIQEm݆{CN=9~P򸝳Kϣp1cc94gB(dyny@ 7|>*p"atnq'vvyN: 4m} L.nV~61%Ux,E+j#jo䷎rr4ڒSE]zJF[QJ-ɲ;.=PVv |>!fy@U T(Kw$t29mpV8gkڬ-.?p^骤mEM?2t> _:R|%,<>fHTK3sWlHs'++gyN]}Ձbˊ0X;\cZrQKh1|c)e ;- b=~I*>gq+7dEדn$!s`o!CDhSHs " 9%^v$k\I_(cwslUvk)[xakF? iy&i b4(PmYϧzN%v*[hS z/w96ۼn.YO*̱>gBv y@vL /6m?$tj}J9Tk@u~K|'X cžy8:YY϶<[[Y<.ILړq/S3 YeRCoe4W2|ye育'Ѧ_Vp}P%5qB*d5i Ͷ5#.:GK~XAUGJ#mۏbr5@[CUX̋Jupe ]@Ե7՚4e e) V>]gz;<]- V@LVI|4-RNdVz6[:=RP亍AhM@6Lt4K&bMPa kEUӮJ߇gRr B?T#'&0tB1lkbʊ3T}JDK%bе1XPMŠi ;/4f)OFxꍼ Kmu=DIBٖ9xiD-<~MPENQH V#ʛii]kJ8Fhl@ PV0,'?i˲ -MM0um @ZE^_Pob-y)e4=&b+#,ж:C(@YPy enс6:eF,0CYCYuVӣNT9hJ i2t[D^^WPd|i ѯݩ޵aIjjjM U-8 Reee(((h4yz {XWO\߃=jŖm.?\-&3dggXۈqssstGη.G<|i(Eq?PTIBz7~Ji)j ꓗ'MMjjjT_H7Bc;~\T(ex"ob^(t&D&q6h$]b6x ,uboMe`ii){999ozdJuf"ԯ4#N>P/%9g.tYS }(ܙk ƪܙ{dc\V.ǢR{0I"!L%E{ū;LTDvvu<'XnRDc;H*RQ,V"2WD*eXFvI/|6hRRR`%]xm@K uoXIKj4aS-8/%mȯJFY :ʠF/S1\ACM}oWKNZf^b߲̋wT\"A]?z&_)pF*X5*w(zk^CoRU92XY8$ή7?Ѓk\< Q%Bm A>nt톂 (( SC\O5@ 4ࠣxqS3C}É[|>P+39)ux0ԥm+ň=aD(ˆ'^a{6.!g/w>ΕARHˆJ"":4h"|$]b6l%,< &&'gY0dhy{{kVďOu~۴xhk4_lXv21@jѢE!T?EfZ#~0Xkoș~Wsl 5bק0N&!$HgPXz f ph   âyk[UOD]TSzb i ӜDDž 6=矍pV]#*O@Sg = 4>h_͖˟1/@A TW=_T1KN n3ӵSfh8U,f~xXI}v\|~V9x~?.vSfhG U-qIQ: OG(sw:NW+f{S[Gp9/$UYY٥Cj[2T h߽]_ĖmgrXRv?TQ,}C-m_®[&H7}9~E%!TB4M=عƍ)$(@ed V~+c`dl`dnMdz|ec06{a  X"5 RKlXSvQvIQPrç+*)KmzVeT Mف2S`g$IߒM{w1{ΓZQllj>>A0hfZG/ 8y|ڞ`O $&@*kZx\.|\ 7 4is5dV8 49TAm跅vC@ P9P Z ~h[2e&'#lEy@UJ d<y:]_.%, !McUN hB^UYŨu>eOuԔ TPSq_+R$PP'U)9{2@YF>kڂi0u~'vP'J.ޝ{WI=j; Cm I 6m}yd+*****JJJ&=O+X2~٦M6mڈ!ϟw|ޡy5,//$5$/&vti d1nPSV u}(/RqFEPѪNUja-1m  Sӱeu6:8L\ *))(qתꠦLSb: UV.mUl *j_6-BǯR'ZdWi}6j'o\ןh4jhX)))Nr 't%%%2gDYYY!}Wx SQUg@%[CK’m7{Կ,F۲(Mkb:ehCCbfNG{ظkTsx)]|rdW"<ߠh@2F}mGYGQtkjUvcmEGؠevZ O??P] ]*n* Yo ߲ @6 FNmtoJ4 5@gAXAA[|o[U  z  Hs԰iwAAEM     "  "  z  wªhAA˄F  +AAVAAUAAUAAAoAAAoAAA[E тh+ ҥ66[H~K+lRE)c"* 2J^&dކg-jZSMW]BR BZG^^^__ذaIII\{cUEoA,qr28j'OGμ st[Q܊Kol-MӪEUB 6mtʕI&}JR UpliW}JJ,,ү]\U/?ⲳM/vט, >Eh!Gǝw"Ńh 9MMNy9h^t;vh%%ef37nQ4V%T[|.W% b9sKM0ܮx=Iei;.Ztu{ ˽ݰHHC$Ut蹻 eGNyxhjPQ97p`ƍM@J RICm{M">{g=%YUD JJJ,ҦM{{ݻw E羟|~hhî]hZ7VD>AA>VU|oBddYfm??lLXM__=7ٹmǍ=o^}r|ٹߙ?Ehpqg%a=dHmi)%،I9nUS?_YsӦ_RlEn}˲o8iR]O LAMl <~O\ܴ8@SlÇ bbn2H 9Ǜ7G8Nf?Ұ!b+foܐӇ8AO~Xb2iR^Gc^҄KCbA8ad IDAT;Κ }R[[{qȔ0___77q͛7o?dcٓ&MڵkW9<$$o?~WOIIpTjдqWA..XHy:8SS a::^O{@l ʲ.^G3LMCNNyJYny@ff x,,+Kpꀭ-Mż,- ,! [-j#>yɓ# om-"S G!)1{\6 sĄ2&3H^>5<4#CR0ũ&~@L JSnLMMSJ3-9++ ,}?@vv+,'47ɤȏUEɔWT?҂Oɔ7omd;5Ue*P%\\а~f۠cիT t֜B6ym P#\<;[E_AUM[a`5DdY+0hXZ6<tatH*# :uNFjm0'Nĉ8e ȶ@@͛/‍˗z0]**rr!12j~Go rrr}i2͛7JјL& 200}}}y?gff f}|E M z8ȟ99%Ñ T@U~>Iq( VPv;֭A-_^|ٳ5,V/7}8ebU35fF IIXfRW&L4 r"#dv]g͚bM~g@@׷m+tez{@UAT\ 2bll|EE@r gw@~~>2'\"H a|wɒ"Nyyk< --}ngKl7vy,u*n|44x55WjXjA}ɓ/IzHj+l*84ujK…pw/7o>_d ˔!"&< a-p KFe"F.uq1֨f0]Zՙm[mi?AdG__Ȑ!o޼ %K˯]6v֭թp -сA\OyJAEUﷶ=:JlD‘#/S73ܾ]74y]PQ]ݬoOmȉKꢢ]Ҋ/BCLy:-[m秢|CΊjj=֭cÆkגbԨ~.+WV&=s'U5DD$e$'&:xDeQ?[򊊦} jG|0" 3hAjͿDoUƗ[T ȷAϟ?oJE   gAM  Rlٲeԩ"J[[g?;ixԩS֭WmF1eʔ[l  [˻{ ϟFUVVFEEEDDDDDDEEUVVRz'Z)^\\-[xlvvv>>>999-_rʕ|>_,6aV\ٴ*X,$ 7o|&=۳Lc8  gV޿:Sx 55<...wvv޸qeeXOOu֑vzIDDǏIo^,œz;{+V_x1**v otbf=k֬ׯ_4hߋ/ܾ}6mԶmx-b} 8a! !?֭ے%K-Zٳg[͛d;;{Vʞ={L777\bE1sf뵑$4y##ݕRhjj !=zddd޽z@DDȑ#lUUU***䱊#;wܹsIEWX6sLR8UPPpy֯_VSSS{)FJJ n5%%:͏9׫rr~>7}zDyijjEq^|YN2d E[~ѣݻwٳ~ &X"++( c嵅cٙ>}zbb")//WSSѣGH^{hkks8 ߹sgYY޽{mD HNNvpp S-O6l :=:zh###&&&Ç?~8v4HVW\feu$.۹sg6}իWΝ;SHXfh5K˖Uxйz#83f`ءgOEq_v-//Z^z)))G2g;;ݻwחLWTTtrr[je˖xΝ;<\Cԩj = #f̘aaac{bk'ߦM&ǏkπiӦS\zWTUU)rss_>~x/[[۔r|ɑdrrGXX֭[||<,--f̘!XNΪU^xfuuuݸ\I:nccr˗ VB<~377g2۶m"YܮDM-&3f۷۷o`0,,,8 M(;$s8sjkkR -R׮]@;v3SNMK~z:89V+֭[;FٳrCrMMM߽{m #F,]gϞM+]ٓbyxxP?yde˖<4#ݺu333C"NNNfڳgϢEvgϞ;wh#Gn۶͛}W߿_SS_OOO)/⍑Ѣ rf???CCC~͛vrbׯ_wڕ\u#8iӦ[n8p@KKkÆ * 2ìY.\(\DlӨO.a'!7:($$܃oÆ G(-u&JI֭:|0ASN{.YXisnݺx޻wzʔ)>y󦒒7(߲*qM8ۻB"""ȝs{]vJMM îG.oڵkǎ;y۵kxO>}РA4۷UUU:::䋒/^#=.]ddd""yս{;vX[[y3--Nϙ3w"n\uu: mxxX[%54j+*}p8mڴi533[tȑ#MMM%iK4CRͥKɀVvŪ 警_NN|qSR~Y\qƍ=ŋ al[n )ffFm%==8}}}|I7޿_KKkΝ^^^F4CRA(V=iFoӡVyXK.5ٵ"%ǣ_~!C/"_# v:888C"x: e%KܹÇݺuc0 39s$SSӴΨ.\iVrqqa~؝۷o^XXQ.]UVo</33̙3AAAn)2L:N$Y)f0k׮~SR(4dȐϓ!pСCV7a„5kw!((h ,nDI:KJ8q… <5UA|',Vr ߪZZZJֿR>zhuuI&M0Y4Ydׯ7hʽz{noh/]{{|cŊݻw1:qDhQXiڴi1yu1bk֬v5OOAQ7Ml$%^ʪ]v۷0`z4>KLU5u_ݖi7n,(( Ԑ;%5yEXXXzz-[^X~}ll `nnn"b "))I{Udff)))uEII:22277Fv֍wÆ .\ wBiiipp;wF׮]+8&}V!]z|>ŋw4hPTTA<OlC 7AA4n+Wtܙz[kjjkjj*))-U[["u?AAՌ{Gׯy"y:aOFAAH/// oUUUmm7o>}*{t$"AAi,V>|5ɓ'QFQg& QK.UTT@EEťK_)_NQUU5iҤݻw/\P"uuu)))g ܹsaa4J,Ϟ=rJfffǏ///?~8y  ̄9/_le'''b._rʕlJbZfʕ2]4 :|,Zhʔ)0ydkkk79sK.&&&v pwwS#֥Kҿa0 tB=zDܱc3344=zᡩ2p7nHRqIPGGGUUU]v j V"b...mڴ߽{YZ$: QeGYYyŊ7,''I69--Fۋ1227oڵk֬Y)Kǎ>Թ2˗/qHi-n8W쭦 >O>ׯ_ׯ_~_nMY,Ŋ4333glaaaIII3rܹR(--=w\UQQ@"]D|e!!!SLa~~~?^SS3...&&&..N V8^bnܸAO8!//u+bcZ]t144qéy&{򥡡!ϟ?ܴi&M#GX,իCBB/8E{ ۿ燆J2Eb斛;nܸy۷QѨ(hoaloo?dȐRw#G-5vX شFI" ǒ:Wơ ܹ3)<  &cx+W9Ν;NNN'޽{jUeeeYYYOn۶mAAA=ᇭ[v={rG[[{ժU;vh߾#Zh͛7O<o֭gΜ)++[t)+5)W^}򥋋ː!C~; il7nسg/  _ QQQЭ[79333)QFZsOKK6YPEMMͺulO?8pӧ7o>|صk׆Z0 :N> nB8LxxxTTTDGG \Y:NxNQRRs꫸V\nJJkjjL&[XXX ={>y;5p@EEEHKK<)OMM%㝝7n((++zzz vzIDDǏ D6B=I6lO> Ockw^޽=<<ݻ'7 !:u" 8wҒZ>wĉ@.98=}&]r|p'=~ XyfeeQFYXX؈QpE̒EaFUhL&?I2vGZ$(~"UTT?>w`ݻwǎW^ !//( 6mŋ/ع uқ|i4[=w\DDDzz:yț(5U'׭###cЫW"""#JEE|X-Y|y``˗_sN.]!fD4'Nxꕡ! !C%ߎO]'[GZZ &ydddLLL/<|P$ N̛+ɡ>sLyyӧOgeeeee>}Z^^QA`g#UV111&&&L&fVTT8;;y5}}}CC7UV.]zБ#G)!!!F5jUUUϟ|۷ܹjjjV^͛7t޽]vk׮mkll|ԩV())-[l˖-{o@c%E {99GL": 8ySN}MZnƍǏϟ_n]@@9sȑ#dUj ϝ;@F䏒o09K? F۷oJJC˗/p8G:-[EHym;_*6VB"}tlp13pe֯UDD~6Bu-2225bl1:::.AUUU[[B!h-}eCxB!PVɧRS  B!a B!Fv#*?!B~\NNΥK<zΝNs$ OOO1OnjjZ`d2 .ʟs1ܹs%)y}\ĉ333G!XXXxyyyzzhlNIIs]v(#G@yy#Gڡ^z[!B]5jT22VVVǎ$ cXгg˗/hSNݻw[.\|p``-[lmmO:A g̝;ҥKOpرuoK.?~|XX}wdbQQԩSccc}||x9Ӻ+))͛7oݺux#UJHHN@}*NNNwp۶mejj>n8}%3ŋriiiNNN@@ի8Oڳgϊ+=JF111rrr;wضu'WZկ_pR>{yy={,--ww?F 0!}W W[MNQsww֭/8555GG`}}3gΜ9seŊx4!s\*DPY,VNNΠArrr uKϞ=ݻSYYijjFFF}YFG?~ʕ+lA>>b=xbegg"P{sUUU1ZEKpHU}ت7NJJ{.on@G|||>}:tP2QNNFAAx %%%bOƎ\.WJyoNNAkE.(++t\\\LV_O;KتodddKB! }h4E~~~'l5kƍGRDccOϞ=311!Ŕ[QQDDSSb222D|r޽d o؊+++/Y$&&xB}׋\nmmmBBB癷*ȑ#'NLg+^]]~^xWB}eѪiLL̡CV:C`0xԴ;+i4nbbb``9N޽]]]+V899)++_raZ n޼Ǻrrr1%YoSNnwʕP2޽{x#B:Ǐ?.Q^^fﺛ/FuBxBd444߿ٟSȹsϘG!F`L:%P(]]Ç㑁B!j2}B!:WZVV&$B!P.@!B0@UUկN7Ea֭O>=z(:c ss˗ vzjaaajjjii)F633hyyyt:]KKܜFux%UEƵ#GP(W\i@\PS;; $X,q9,!o!>YU2n>L|N(\VV;wNNNs,B;!R V|xÆ hqqqnjjJ&RT2K{gϞ>}۶R]BOϧϙH̛G+޽/̤$xJ8}Qy):::F:}4~B!*T|XPPsaMuM@>GGGMMMiiifbbR\\ NNNZZZ2224O>cGIIɄ wqF^;99͛--- / {Y[[[[[ĴmaR") ʊPTFF()ȑy3gTU)[7S,-)w ^6m.ͣPƎR^xccJqqǍ'Pرc_*B}{1tT" SSSUTTGyyv ɓ^bii={<|0"""66Or lrmggg''۷omavy 1gOGd= {ÇAEEw-j)r3gPP>e II+~=F`bs3+++b>}*(!B4[A|s'm#"wXX0 vVUU0uu%% ߿_L'N?~/۷oڵ ŋɶj*~7Zuҧ>ܣʼnT*ŋX0!!aܴUUՒmmbTdee[% B۷oWUUihhwܙ8q2?ilUB` eREdŴ@TB uPScUmm}!#Cma`-}y|Q߾}CB}Sccc߼yr ]\\ZJzo7x|j@LLLYY˭9o˗=rttlPϪ?C|ΩS\͛7%%%!obb"oۿ9~3u7!Z''8u ƌvk`3޵X @` _>_޽! RQ99͉#FEEE41bą  p°aÄW!-]tfffVF2R0Ŭyy# ϝ;0p@HJJqF;/~~~}Ӗ+}||Ǝkcc#0rѢE%V䲳[\&tpq(Q{7М"|XYر I|" N4h_"&]B2U*s/`B!ԅvu16vUV?Zŋ?v͛7Ճ͛w] JIIٳgKX+u3f;pÆ ?ڵ͛QQQL&gݺuA~{̚5~##rR3B8qℝp8߇wP[Jבષ:;Bu,&qsUUU}&7o>w %??|KmmZmm-^t={ַo IDAT_}*PU==e˖M0{Ν366fff ӧ}Oo5}Zj&BHȫRSScbbrssMLL FK%bvJ GO>9ƚlC|ׯONN>sDvk)Y'NaB"Z_bǏ@ZZ &Oyfm[ޓP__/+++2ZHS2G6nxݻw5 3Ap\))&}Ց땕|t~>ѪVsrr.]t;w444^_Q0㣡jiiiHHHHHܹs%ܑe"gt;wU}'$$dϞ=eeemГ1L !yL啞pJKK[n۷op rn%$kkg.#7ijj"Bx+j~ZWWW%h :v9"󥤤XXXxyyyzzORd}tGSuuuJJJrrruu5/b=zKOH+MÏ1By555YYY9Y,֭[<(Nx⼼ŋK:}ϙ3l6;%%eʔ)" 6m= tBHBrQSw bk“kÓk=p5b0 +++.4f@H$/]4i$y zj I+++Ooݺekkkkk+|WK.I$m\rݝ5__X/I:7KuNݰgabpmcʕo\[nܼyرc7nkvJJ:. ,?~iӦN*!CX[[[ڣ@ $Ļ=5O5(l)7V +NNN.((,NмU"ihhhjj'Ν;w޽QQQݺu!!!b&6mڴ)**rrr;wܺukK91uuu JKKwK/,,477/**s ME;0s ?;hi۷/^*2K(// 0}ڵ%K0e˖/!!!={UEqƐ!C r tee劊 m/4ϤAAS#fCyV??}#|}} /^GbV:$hѐc5Ճ4d\Kv;]]4Ę^*ɻr?{h 5z{^%x=["B[?y5 11ٳ2&LgY񸹹bWoP'B};SΝC_TTvB!.@!B"B!ddeeI@ނT !BV B BUBC!D4M[[̌F^{nAA'v(B!h˗jjjMMMiiiqqq۶bW"B6|OYYYYY'9::jjjJKKh4b2$%%Y[[cW"B6'kC`tOi1466O~1!!ܜr+B!'?ˊJV1WB!ڊc111&&&L&fVoYwB!B$&&VTTh4 b!Bjiiiiiɀ!BIa BNK!,P֣GVBkֿP;HHHhJa B!B"B!V;rϠ ުk@@WlEiuۻFU߾]|@;#BH:o5'''))N֖N۷?w7 133…#"%%ŋ5?|cު/8tG77K}}mWÖ,QUPhH7f̐j%={)])''̖:!BDҿ)))^^^t:=::tH߼9Ņ FDWU@qUֈ IJ EE>mNYYlzvIUUwEZ^^.ƾ|9uȞ{_y~ry:]|*?>B! 5y++cǎu'ffVֺ?u:u#Faz׮VT /A<~yes斕yׯt/gbu+3~8]j^^Iuq˻{/#Fya{{xի Y**KǏ7q5kxqUܫWo|CM9{攖N4'k^+*=2?ut^Y^^d-m.EwxѣJ/ݗ/?5\=qbwU7ou ƍ"{o[SVu'B!UJOO} UPP044tqqQPPX~pfmd rرcݺu366677P(;oޜ"%ӏV#,…u}gt㌌5 ZL۷sWU92yۺcwsLZZD\ix9333 c9ee Շ%gL/\{#GztŋΥS 2t:q-ߧ&-/ON935/Y.._\|˴iW.\5k,{c.";Yd/yDȽ{JKu%EQB}Mbcc{sΝٳgiii'N`2(NP۷/^pvvoo3fxyyfeeŵg$ffw25?|86b {c,Ju33SUP fPWy֭ch4 %%tI=ёN((WWzkkpr 55t}AhnYRׯw]\!",~[_xXEY t:vu5РXW2}p1|ΐ!-ճEH_]}2/h;vkk733:En./3Pd'%=/wjXt !WiڴiT*UVVv/ݻ@VlvRRݻwys\]]ϝ;gkkn]s\]V ꊊ+'Lm[ U.,*2y?-m?`(_2?E^I@UA!13S| 4А\~UXHNٳy$S^VEFTT<~ܧ&|s==ׅ޾F\zzyM E]k'M:~K&vuYETٙJ3ЍhNd$-ٟ QxYͻ:NDP(A`?|SRR`ĉVVV֭vܹ Hҫ+f>|deeyFMMG@,,,BCCҥ㗘ho,bv$WUV+*B%%VoE^ݟ^P@FEWUش\O2}呱rEM oGQO8ǏKlL^9g!,ב#3>yٶW/^^߿hU"[4W/ĪkeR"{I["?E]|>FG8&TUUvճgO19edd444WZ&>hGPlmmccc$4,--ߓՐKKKSK,9}=uG.(++DnGҙׯ_/**r Ȓ.[UUuÏB7oaG|5Eii ΥGn?λ>H`&v؃ G @iuu}c#4;;俵ky|{inII؃N^MJ=ɛ7rtnj~~՗}X,) Gn$%*)ihl|!0ObZ#4`iy>.@TTvi)/^8/E@ϋ!?Sg|ʼ6<> ŋ3g֭[g9~~<|6iѣG=<vΐ!O]M|UB_r褠W&Mj@RTtܸ[Ϟo۶m&7@\NK=/J"$ cGcSD@nnT^^xȈ=yLr={TQQjjjt "25իW?~\]]Loll(**"WTj^VZbŊ7vD.p8uCYY9((B?6c???6-.i{ҥKL4… %S(.+%%EV,--SUUUIIÃ|j߸qZVVV__xТN4h&98zwXtUko#@VVV_>>>};w_xݻw{{{_/^ؽ{7B0aBppzPP?';w|L/R_ h#رݻw]`ի{x9W^ 4M6EFF8p@EEeÆ L Νkbb˿Ȧ% 'O+,,l`aak׮Ak߿?oAGG'$$dر6lu 3!N8aggb8o߾mhh8c (// 駟ۣG-ZE!VGW\9v؉'Fmnnٳ .Lzh^!gΜxjjj峲/_.&ԩSzQB%̻BdwZaÆa*++?zyǏPTT6mԩSۤ&K.}ꕋ /B0'&&FGG[YY ll2{{{gggCCC===2qʕmibz+"""srss#""O.~\ren\]]?CIIO䍱TZucD)}wL_X7!:ֽ{3v*wFF֭[ܻt𰴴,+A<N[AUIeUXXZZZJѴh4=}FBoMeeepppTTTW?cȘoB122JOO1ȗ#iKccc558҇ZZZjhh455=yUBؤBBBt[~_KHE!>TIqqqYYY}}}\6*Miii//X,dOTG!r$ƦuQ gg|KSKlllLKK#ؖ]xISSʊN[B!ʧEaaa`0\]]ȵzСRRROI$&&U ;))ڵkAWYa"BV)%#B!h!Pѻ"F!:>D!u.@!B yu;a B!BVB!F_BU4jՉ'' !BmKҫrrr tz=lmmt}hYfux< sUUUV:ɉsJJ׬uskhf |$ZX+-- INN楄XXX,Xg BKE˷/%%bĈ'999::zȑ] IDAT޼ 5൲r3f̸sN[ f̨uۜٳkwvRwlHHथ5-=KaXG]h~B!V8\8[PPsa +oԨQ,KFF`XYYfۭEEEC QPPXz5/N积̟ҼyR~~GgϛVfdRƏ%uk=P([~~uZԜM8 ۶5ӧ(+W89Usظ&45+ K.M4IXK. !Bm K%I=_l6;))ICC?ӧ=zozZ_~999o߾%!< `֭111/^((()r򠠠}FWa!wÆzkkipu@xxqTM0Ç e[pxXP@kQsN3vRc^%,é+VqԈF^˗l>`  PMRR@7!ByBKj.rRǧ &'ɹw|FɓcƌQVVnzXXl===FFF}jtttKsHٻw}7̩Fqt ח e9>sFťz ƨQTphS0卍ʼBP#ʈ>}ENyvm]}=!KYy^7JԴ*/O-u/+hjV0f0yt*Z[[KR~(((NJ:!$ .vBi3#ɱջw5L|򥚚Z{P\\ bHX,Wٳ722Rdb77ڼ<ӧMCR7$(-00ܛ90*׬a\=J.dӛM WrعrYY@痕1L]B!YތӉԡ-m@,,,BCCy)Ƀnzb222LMM_"OU+++/Yd֭-)تH::RRkԍ'0:mL-ddpMMEA$54(?h+~K9P?f uժ:{{u/>>~̘1eYXX !Bm d:R\wuKKK55zy4M[[=bĈ;w[.((H|N///ڵkӦM!o||&011iOMErsٻ!"BA s[{|-/'/Oٵ~&YQ966 /]<\XQ([]M9"QO`{{EDرcѣGI4DxA̋ȟ~BVHl[y2Zm+"N255))),͍LOJJ͛7O:UWWLJ?0_ +V:99UUU]+W6N2L6&s{PrEEʀ4߾7\ 'Xsr"֮e|"ưao++ InګLXu{WR6lhGQFR<<>l̙FFF999,L>{988BRx Wkbꭎov8c]H@@@bbٳg &XYY;o6`j/rd|쾧b ΝB!0Z@"sZ7;~XYE!hخB/JPy L}"B!B"Boϣ z%ݨ}7FٍZR>VUUQ[kKIzʀ30ɕؙGX)ēS<]Sr^>8:y}S$]"+7emE.A!Зv!pU?)4rg# &n{۟p􏓙Ee [vT=g6<[ߟ. S]V'YSsJiT^ ָTQ j&(qƵGuҝ]˨.hZZ^r86dw)WIRz ,N>SA*6'c/3L !BƊtw#t՛x] CiR:)5)h,C*+I͟/%Cא"O:NVdR\lCڷWGÓ܇j)YDV; XN&݇ OG|BI^ZU)~jo 6!&o2ؘ1ƃv#7*l̙X=vQY/>G5)5|;$ih (mSF&B&9C? XENp)*';;**뿱>/X)* uؙu ]Ȓy9XnylER=y>?&ǵb",C!$q4ʴ3dz=r <,+Xky‰l%)C^R2 CIɒSohzUOA@tY%VN쒷5; 1 oOz@NW폱KIz^(K[nOmϠQ H!\&iᅡj}^jgI$'؎[GXF}.as:z݂ ȭyJLȭ :#UNۖ^T H9rfЧ*-WpHʹ\Y{ӪW֩0eeRn/cTd,{aLʚj [W hFoKؽ d+*%Fk7y\pU"xq32O$Mg: rjĽ2{[|{>KaCL{滩+t05\&M^VRZ!I£ FJ1l޳x9O"?/آ3D[oĶ[b9g8FZ_y\.7U轡NdQۂju3dmlv6 wlB314 M->yY݁Փ# f )2֭Ln=QS7kh!En(uFYIV&fFS]4ė_e' ɡֲ f p^VQTbHXd(<@Ȯi/2Iti) Em} :TnR |_"?I|?.嬗uu4V?wF_ D87rF&nr(M~r©ШhIMoyᦺb 9yvk?;߰Wd ǒOe6 wEKBΆ^&U5 Rv#f\)Wz()4U~MQDhTh / Er2kD3>9o[os5WUJjt ߊ/]nK]% BE(SV]2~_wy)qI֟?ӕS|U]V>m&Q%_'G y21T㥄_y6ڵw+rMIY:gTmIs4e ަWRTŵUsTy.5s| C|c:'w ſq&[#pf~k~%Eʪj ߌ6|l V31Ac@Aa)D{B~mEcړ\ YŴBP}HUNOڌPT4JΝYV/`ggM 0U_|{nS-tv]VhDM{MM|# Y9j}n^{F8ɻ;1[> G_{-[俥4l%. :6O]|gΥԴ[zuݡn ~~XI+L7{ѧ\mѯTC֪ŷQ+Bm¡6WՑk[E!jl$W/"m8P}p)e7jױUB߈vf_!6^n08B!i}0:|Krrr tz=lmmtz]]ݻwsss)!BVCjBjDKIIp=:>eʔ.%%;!B}hUQFX,aee CVVիWء!B }UVl6;99YCqy+**CB!PE999www`Xqqq666\.766`"BVlvRRݻwGeoo޽'OȘtPB!a*h4 P:t(B!ڐWY]~&$$Vo޼Y[[븸8KKKPB!Ԇ$[555))), tttΜ9f\]]ql!BuLڽ{ݻ $ݻw؉!ZMamPnuF.}~m[/לo/YG|uB[헿g:E_!- Yh[S;^$şl]Ϸ_YzE5g)s]]Gw? ⅞bb>hob>ĩ"B-ǵadu5#1d@>s/{bx|K/~޾=vg~ny4s:7f^w~F!$afY<9ӡ %Űy׻BNJ1ld9Mյx+k+;Bo80e q@)  nQĭZkWmUURERRDPAQ`oH {iqA@˗r|'O.aeP(ik;8\IZx7 D3tEp}}uQȨ~ $putze+#TW("2{#u{ =D)*C%A޺J+IRJU7ޔ}VnNEn(lɔ_}g}t`J,3` '.5~l5ۉG_hw^xP(|#|"%!ZB'CӔ7MWݯk Wcnjv!GɆXwUd*X fOK/ÆKx|[C]%/?O2*-ݪiޜ ܊}4{b.=N5m0v`uf6EQ **M?y"錩wxY,)ݹĂy>~v}oϐNI/NOYBD.[d6lm`XП~ʪ EHl=h!{NU7ꮔet_YNQ7K>EZDB7'`<~ٕkqJJd8~IvgIllj(.+05O~#-<OdJT#DtOjlU[h0Jzy-?/ۏ={& lz.[2iʼ9^+~cc '95\^.8Ͻ,L IwmWnRZZee8w2.1E9_&9f={0yr_K{iĔ'-iX U`a=Mjvs&ncIl))+eˌ ]DL< EH{25-G|֍+ef\25;r$EVmQ_O[vl3ٲЙm/0~zA}z/_ۻ{tH1V)Gq*nu FSS Ŭ.+wYxx&ʕBWXV6ؗ/'L )^gbHGo2QOSc %Ϟ(} /\3y> )F-dwꢤ9EK?mZ̚bl^_p[ziDo}=Ljjz0!RU.uwGQTvN!w"o@IID߄68SS̬|Q4(i Ee(BW$SyFѕRUI}V84Է8u&r`\ɓF)DK"W3}?ck@mm=ԉJVܹE< O<( xر&2 z~+V/;_V[1tlK#s窱XUT9ћLԓ-jch~|G#F:J31$eVx ۹ZH >|`/g,todj^Lmÿ.[ν%?on^̃G7hһ $M5 oOo=p8l7GoUP">wa7[gJow1b>s[wPSv}آhߨA !%<[7nZiM4I!YUJSfSPv6k=ܹ$oJ-W3}?2[>1J#O~b֛b&%9?D55u^ZV&e1>u4uQ@\˲lB~IovhH.3{?o;ܺC"3=&ɼ v;n]IE$>lZf0 B0Q7L(q\MŭX6 v`0V.ޑs Ey ...7o|DVؼx{O=zV>iizypf:UKUxqNfy[Q>{6B*ZSVZ :Ҁjj~2>i#<n椡#ի), |yp[۴7os#GC}Ƽ" !bm-+JCII!wYQ:?w7EE<"8I5n̠ګ?Ǎ$Z#,6xx fz5BRLA},gka5\/ AKWF9n:m:7rHm΋VA]-uq[I6ac\7 Zb[rhOxRG(|؁?MM8[-߃(Bv,.;whqduMiSͶh0&@h0iĉȫ'.r&&Lyߏ;9?74;tlU#F`XP[[{qƵg?sf's,,f騫Sl~Ǝ_8A.]80h <`ͩS3ܜͯx 'MߵgO#-ޙ3 ~UPp 6XX\Qɯ<4Դ22kk旕,6#k.$隤HC'nl idQKIB[MM[M d|oHLXJzr8_;Pkk&SOCHYHBZO}D"ICǯ?t֭&*VV56655[XLuum1O:BdmN,YCQIKÆIt">>}XZ|A+2? 6~>C1&=Ç?$Y,[mmb{Vd|~YԢ!CA˒V\aiޒ"+CC&2y"mBVTTUWipw,>tbb͛zׯƼ"k2lUPbRƒ!w(),\6|8hRm)$}B-f9'kj]U31&6w%ucZ 0e%%% o.q2FiiBΝsb\%fp3?,Dž\Eu/;d^LN'?(P40#C4VV]MWu#Vc_yTWׂ@{f~ic׮B* '4 DG܊ wߙ>LOeee$s| I4UTy^t77Y: G-3gHCZTiepȐ/_LJX̬Y&ɓjyY#Dy%%(Ťd%aCQ#.'y{ٍ&H(V̜=s7~r]_S4C<SSgϞUVVVVV>{Դ='V'S~07_YY >Ż_y+D啔^\l(0SS#< .N+*J--)9(;;becm;^y|RqVw&1ET,ͽZ@jnt!iH$ i@2Ei0#!ٳko$.iSSnܨol򩛾چSfIBZO}D"e.9¢.1=](_64tmFT;eY!MPbRƒ!w())9`頠AA;NӸUBV[{111`nnn7xa0 ƌ!R~yS<7"QSOYIW- _|aMZ\<&1ue<<}X n0fh C-²F4.kMoIVtMRҡ# Q`Q{Fh]p[[UجnB' 2ܯ_D\\]wlM}FFˆ k1/k\(]LJd ~RǾNN^xΣTԴ9!m9Q;5g PrӁ <մr-ZD12'"b646*Y aeP($H]lSS~:}whjjxoX2Xh:&VRzRĄ~^ԑ^_h ,SA٩ :8K uv줨bd6bނ+*Id7v!>Yl;ė՜1ӂ*Bu,f}?pvWDZs7$%;30dIik45<<8!"% ?=ov9i}' Z5rxٶaC\r.tE3FM5 G;w{ޜf.V5ݻwY-g1SlE~SG =m1r}˔xt:wLyg]OefՊ#d5fw-\zDX1\K ,,XH w$8ͮ)W>rrz!>!eب.CU]\Ԥ&WZ6BHOռ-%GZ__%!v͙͋ypSr-YM =#W|!e]}[['W]PXoji w1b>soHrŤe/>ID]Ft"(((466iVn^}ssoojB}=Nm1>l5!!ĄB63qҪ'l{ L6_x ,[=;_RxdXf'WZQQuʽAr;sXY`܉KzZ2s7lqL7RbҲv=o >*S \/-Xaeʫ45kj}EZwvvLJ\VUVtr|}}!׳ǯYd* ȵyЂIl}_wua},Yj</_:UbW! ~>&˂/oü6%mzV"ڐ~KI(o_t"{oڝ^1XEK~>{mћټdEZwvvLJ׼K#:^eZ[[[XX@XXOB"*+$\p0@7/2FDvo޼F@t7B-_\V&c8 Ř DLLE!Ԟ=pMM8 Xj&Q>#3-V[H4J<!Pk9{4o sSV#J1=E!Bm y!BaB!f!Bs%I/A!t:p:BY813f.7}[bB!}O;w@^^˗/KJJX,ñe2S=r|W9˻:Zw\KC2E= ^%DzǒX ]KgÉ+ت*|~xx۷-ǣ.]]]G`0.]$%{xx̝;W[[!|՜*7oXYY555? nnnVu"+U_;GɻyUEݛĂ#oâ/G<H[8sJt$ [upʡIpɑ#Giωoz###G,T@qnJ?Y݋=VJJֽM~v  ޽dZzqa0P_߰O\˱ Bg444$'';::xxxp8EEE&٫Wo@g]Q^`ȂR(Vp?PDZrxEvjJl^ W5z1cDO\MLL6m$ZDGOݺuۻwwutttttwx!W^7BPȞ˶ ⒓vq`+w'CӔ7MWݯkkCSG  ߐjox?=ov9i}' Z5K[srQA &a/ >l5%%鑾rڿ {Lt|xt-nIOny\hտW%5?j߂?wޢ7nNII-xڵDkJl;p@XB={&Qr jOeӵ4V.NQ]]E_n9[8[ui55uc=YRhiiN/_e whj/_2BBO/;~Ab!A=4''gذa)cyybbb߾}۳)=z,]d ljhҙY]Q/^Ⱦ_Ujh+Koh`456Y5D!-~իW555)*qq333ذaubccw! /_.\SS u@Y`]@4nIx;sf 5`x!]MMohJK ^&I!>lÇ.ݳp8YuAp6<~YQU^jU?#TWԩh(*SUUQRm 9m7y6f.vӨn yZZZoey<XD]iqppl.+/--ԔX\c܅Mva߸ &o^vz/, m?7sg5Age] rY65-G|!A=$!!!***** ݻ}?)M]tR۴ md&/zՠ }$l[^z=~XTWW@qq3geff`0k[^^flvyy=zdmmo:@l?N$^b2F5ݭ7LJ~5rx&Sْ/ J+*.]7;xiڔQ;EO,[C<ׯ_?zhm}a;}.z0،ՂOOq9~fVmU=7b{t6TRN)oU~9S//={Ro8y5kdeeq͛7$nٲ8u˖-⧮;wnС%yuWCws?x베~WǍn**,q?YK?95-xv9|T⥵g-{Emϐ='Bl q㔔>b{,] _[vivՕ̬o@ܾ 15ӟ萇+oiFw.jlkS@)&M䔗gddׯ3gu@@tH< _xqUUwEEŊ+\t ˉ5ccco>qnn˗o>...@;[պ5a~^B+HɓO4\X&S)d1!~┨F+ ]mݺӧǎnӦM[qƭZj@}v FaBjYxUr? pEaBS#+o{poEwZ%k1_D!T(~NNC11E!U@KB !!@!B"B!$7[tRvv6qOМ䂂effb0!B7&{ӧOgΜbnܸE!B'[sww-9rGtIDATr޽0!Bq&@BB Ñ~ɓ'l6B1 /bBuU.+RXX7Bt~Osl۷o˺* .99Ν;#GĘ"BB7o3"L&>??B!ZݹUU0իWjjjUB!qURVVVr, B!>r*g511111 "B6B!0[E!BUB!9Q B!Q*B!l?FWW)B!jlΝ;QQQtv1DW7c<i.gϞӧO!B!Veɩb>|A!f]>%/1͜:o-[Z|Vc2% ca}7ɪ?~ɦM;A駟uw^5ܹٳp"BUr)))GOOم³{·;CvǖC)oxAoLLgwDAedmqFkk디JBbfTb]v=x :::>>HbMMJKKCCCmmm`!ҜaÆ5}[U%Y6ċ0Xξ9mI} dmuWjjjSqq333ذRh!lÇmmmLf ۙӺD*i94"$:݊?]Ҭ 1ZZZnݺ?S|9B!f%%% Ө(҅0Dԍ4Ytm6545PI.544,..^(33‚d P(d0$+p;wE! V}Oe-`{vA%e൷W@zYhMCuCH,H,je\k_ɓ'***9/ݻx!K.0ax&Jlhggo>ba|| _B}?NjNnܸqV0`ݻwCS|!P B!VB!bx B!B έ"BvکS']HB! =.]aaa/ ߾}o…qB!VjZZ@ SPP(**p#B!q&@}}}\\{ v`0 Fcc#F!B'jBB _xرz KKK[[[!ksccgώ7NQ!Bjr|}}4668;;*~ BB!{&۷Y,K:u|E 7nܵkWmm-!BDwn͛7o$I\_@=i5{l&AG!B禢T5&&AGGG u] jjjqB!*)SSׯёtwl 7B!jlU4jaaaaa>; 7B!jlU^MMMhB! B!*B!BrccBsuu8 pn!Bu\8B!:.[E!B"B!*B!l!B!VB!f!BaB!f!BUB!0[E!B"B!*B!B?! gKIENDB`gcc-python-plugin-0.17/docs/operators.rst000066400000000000000000000022621342215241600205120ustar00rootroot00000000000000.. Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . :py:class:`gcc.Tree` operators by symbol ---------------------------------------- .. _get_symbols: The following shows the symbol used for each expression subclass in debug dumps, as returned by the various `get_symbol()` class methods. There are some duplicates (e.g. `-` is used for both :py:class:`gcc.MinusExpr` as an infix binary operator, and by :py:class:`gcc.NegateExpr` as a prefixed unary operator). .. include:: ../tests/plugin/expressions/get_symbol/stdout.txt gcc-python-plugin-0.17/docs/options.rst000066400000000000000000000037611342215241600201740ustar00rootroot00000000000000Inspecting GCC's command-line options ===================================== GCC's command-line options are visible from Python scripts as instances of :py:class:`gcc.Option`. .. py:class:: gcc.Option Wrapper around one of GCC's command-line options. You can locate a specific option using its `text` attribute:: option = gcc.Option('-Wdiv-by-zero') The plugin will raise a `ValueError` if the option is not recognized. It does not appear to be possible to create new options from the plugin. .. py:attribute:: text (string) The text used at the command-line to affect this option e.g. `-Werror`. .. py:attribute:: help (string) The help text for this option (e.g. "Warn about uninitialized automatic variables") .. py:attribute:: is_enabled (bool) Is this option enabled? .. note:: Unfortunately, for many options, the internal implementation makes it difficult to extract this. The plugin will raise a `NotImplementedError` exception when querying this attribute for such an option. Calling :py:meth:`gcc.warning` with such an option will lead to GCC's warning machinery treating the option as enabled and emitting a warning, regardless of whether or not the option was actually enabled. It appears that this must be fixed on an option-by-option basis within the plugin. .. py:attribute:: is_driver (bool) Is this a driver option? .. py:attribute:: is_optimization (bool) Does this option control an optimization? .. py:attribute:: is_target (bool) Is this a target-specific option? .. py:attribute:: is_warning (bool) Does this option control a warning message? Internally, the class wraps GCC's `enum opt_code` (and thus a `struct cl_option`) .. py:function:: gcc.get_option_list() Returns a list of all :py:class:`gcc.Option` instances. .. py:function:: gcc.get_option_dict() Returns a dictionary, mapping from the option names to :py:class:`gcc.Option` instances gcc-python-plugin-0.17/docs/parameters.rst000066400000000000000000000022511342215241600206350ustar00rootroot00000000000000Working with GCC's tunable parameters ===================================== GCC has numerous tunable parameters, which are integer values, tweakable at the command-line by: .. code-block:: bash --param = A detailed description of the current parameters (in GCC 4.6.0) can be seen at http://gcc.gnu.org/onlinedocs/gcc-4.6.0/gcc/Optimize-Options.html#Optimize-Options (search for "--param" on that page; there doesn't seem to be an anchor to the list) The parameters are visible from Python scripts using the following API: .. py:function:: gcc.get_parameters() Returns a dictionary, mapping from the option names to :py:class:`gcc.Parameter` instances .. py:class:: gcc.Parameter .. py:attribute:: option (string) The name used with the command-line --param switch to set this value .. py:attribute:: current_value (int/long) .. py:attribute:: default_value (int/long) .. py:attribute:: min_value (int/long) The minimum acceptable value .. py:attribute:: max_value (int/long) The maximum acceptable value, if greater than min_value .. py:attribute:: help (string) A short description of the option. gcc-python-plugin-0.17/docs/passes.rst000066400000000000000000000316751342215241600200040ustar00rootroot00000000000000.. Copyright 2011, 2013 David Malcolm Copyright 2011, 2013 Red Hat, Inc. This 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 . .. For notes on how to document Python in RST form, see e.g.: .. http://sphinx.pocoo.org/domains.html#the-python-domain Optimization passes =================== Working with existing passes ---------------------------- GCC organizes the optimization work it does as "passes", and these form trees: passes can have both successors and child passes. There are actually five "roots" to this tree: * The :py:class:`gcc.Pass` holding :ref:`all "lowering" passes `, invoked per function within the callgraph, to turn high-level GIMPLE into lower-level forms (this wraps `all_lowering_passes` within gcc/passes.c). * The :py:class:`gcc.Pass` holding :ref:`all "small IPA" passes `, working on the whole callgraph (IPA is "Interprocedural Analysis"; `all_small_ipa_passes` within gcc/passes.c) * The :py:class:`gcc.Pass` holding :ref:`all regular IPA passes ` (`all_regular_ipa_passes` within gcc/passes.c) * The :py:class:`gcc.Pass` holding those :ref:`passes relating to link-time-optimization ` (`all_lto_gen_passes` within gcc/passes.c) * The :ref:`"all other passes" gcc.Pass catchall `, holding the majority of the passes. These are called on each function within the call graph (`all_passes` within gcc/passes.c) .. classmethod:: gcc.Pass.get_roots() Returns a 5-tuple of :py:class:`gcc.Pass` instances, giving the 5 top-level passes within GCC's tree of passes, in the order described above. .. classmethod:: gcc.Pass.get_by_name(name) Get the :py:class:`gcc.Pass` instance for the pass with the given name, raising ValueError if it isn't found .. py:class:: gcc.Pass This wraps one of GCC's `struct opt_pass *` instances. Beware: "pass" is a reserved word in Python, so use e.g. `ps` as a variable name for an instance of :py:class:`gcc.Pass` .. py:attribute:: name The name of the pass, as a string .. py:attribute:: sub The first child pass of this pass (if any) .. py:attribute:: next The next sibling pass of this pass (if any) .. py:attribute:: properties_required .. py:attribute:: properties_provided .. py:attribute:: properties_destroyed Currently these are int bitfields, expressing the flow of data betweeen the various passes. They can be accessed using bitwise arithmetic:: if ps.properties_provided & gcc.PROP_cfg: print(fn.cfg) Here are the bitfield flags: ========================= ============================================ ========================= ======================= Mask Meaning Which pass sets this up? Which pass clears this? ========================= ============================================ ========================= ======================= gcc.PROP_gimple_any Is the full GIMPLE grammar allowed? (the frontend) `"expand"` gcc.PROP_gimple_lcf Has control flow been lowered? `"lower"` `"expand"` gcc.PROP_gimple_leh Has exception-handling been lowered? `"eh"` `"expand"` gcc.PROP_cfg Does the gcc.Function have a non-None "cfg"? `"cfg"` `"*free_cfg"` gcc.PROP_referenced_vars Do we have data on which functions reference `"\*referenced_vars"` (none) which variables? (Dataflow analysis, aka DFA). This flag was removed in GCC 4.8 gcc.PROP_ssa Is the GIMPLE in SSA form? `"ssa"` `"expand"` gcc.PROP_no_crit_edges Have all critical edges within the CFG been `"crited"` (none) split? gcc.PROP_rtl Is the function now in RTL form? (rather `"expand"` `"*clean_state"` than GIMPLE-SSA) gcc.PROP_gimple_lomp Have OpenMP directives been lowered into `"omplower"` `"expand"` explicit calls to the runtime library (libgomp) gcc.PROP_cfglayout Are we reorganizing the CFG into a more `"into_cfglayout"` `"outof_cfglayout"` efficient order? gcc.PROP_gimple_lcx Have operations on complex numbers been `"cplxlower"` `"cplxlower0"` lowered to scalar operations? ========================= ============================================ ========================= ======================= .. py:attribute:: static_pass_number (int) The number of this pass, used as a fragment of the dump file name. This is assigned automatically for custom passes. .. py:attribute:: dump_enabled (boolean) Is dumping enabled for this pass? Set this attribute to `True` to enable dumping. Not available from GCC 4.8 onwards There are four subclasses of :py:class:`gcc.Pass`: .. py:class:: gcc.GimplePass Subclass of :py:class:`gcc.Pass`, signifying a pass called per-function on the GIMPLE representation of that function. .. py:class:: gcc.RtlPass Subclass of :py:class:`gcc.Pass`, signifying a pass called per-function on the RTL representation of that function. .. py:class:: gcc.SimpleIpaPass Subclass of :py:class:`gcc.Pass`, signifying a pass called once (not per-function) .. py:class:: gcc.IpaPass Subclass of :py:class:`gcc.Pass`, signifying a pass called once (not per-function) .. _creating-new-passes: Creating new optimization passes -------------------------------- You can create new optimization passes. This involves three steps: * subclassing the appropriate :py:class:`gcc.Pass` subclass (e.g. :py:class:`gcc.GimplePass`) * creating an instance of your subclass * registering the instance within the pass tree, relative to another pass Here's an example:: # Here's the (trivial) implementation of our new pass: class MyPass(gcc.GimplePass): # This is optional. # If present, it should return a bool, specifying whether or not # to execute this pass (and any child passes) def gate(self, fun): print('gate() called for %r' % fun) return True def execute(self, fun): print('execute() called for %r' % fun) # We now create an instance of the class: my_pass = MyPass(name='my-pass') # ...and wire it up, after the "cfg" pass: my_pass.register_after('cfg') For :py:class:`gcc.GimplePass` and :py:class:`gcc.RtlPass`, the signatures of `gate` and `execute` are: .. method:: gate(self, fun) .. method:: execute(self, fun) where fun is a :py:class:`gcc.Function`. For :py:class:`gcc.SimpleIpaPass` and :py:class:`gcc.IpaPass`, the signature of `gate` and `execute` are: .. method:: gate(self) .. method:: execute(self) .. warning:: Unfortunately it doesn't appear to be possible to implement `gate()` for `gcc.IpaPass` yet; for now, the `gate()` method on such passes will not be called. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54959 If an unhandled exception is raised within `gate` or `execute`, it will lead to a GCC error: .. code-block:: pytb /home/david/test.c:36:1: error: Unhandled Python exception raised calling 'execute' method Traceback (most recent call last): File "script.py", line 79, in execute dot = gccutils.tree_to_dot(fun) NameError: global name 'gccutils' is not defined .. method:: gcc.Pass.register_after(name [, instance_number=0 ]) Given the name of another pass, register this :py:class:`gcc.Pass` to occur immediately after that other pass. If the other pass occurs multiple times, the pass will be inserted at the specified instance number, or at every instance, if supplied 0. .. note:: The other pass must be of the same kind as this pass. For example, if it is a subclass of :py:class:`gcc.GimplePass`, then this pass must also be a subclass of :py:class:`gcc.GimplePass`. If they don't match, GCC won't be able to find the other pass, giving an error like this:: cc1: fatal error: pass 'ssa' not found but is referenced by new pass 'my-ipa-pass' where we attempted to register a :py:class:`gcc.IpaPass` subclass relative to 'ssa', which is a :py:class:`gcc.GimplePass` .. method:: gcc.Pass.register_before(name [, instance_number=0 ]) As above, but this pass is registered immediately before the referenced pass. .. method:: gcc.Pass.replace(name [, instance_number=0 ]) As above, but replace the given pass. This method is included for completeness; the result is unlikely to work well. Dumping per-pass information ---------------------------- GCC has a logging framework which supports per-pass logging ("dump files"). By default, no logging is done; dumping must be explicitly enabled. Dumping of passes can be enabled from the command-line in groups: * `-fdump-tree-all` enables dumping for all :py:class:`gcc.GimplePass` (both builtin, and custom ones from plugins) * `-fdump-rtl-all` is similar, but for all :py:class:`gcc.RtlPass` * `-fdump-ipa-all` as above, but for all :py:class:`gcc.IpaPass` and :py:class:`gcc.SimpleIpaPass` For more information, see http://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html It's not possible to directly enable dumping for a custom pass from the command-line (it would require adding new GCC command-line options). However, your script *can* directly enable dumping for a custom pass by writing to the `dump_enabled` attribute (perhaps in response to the arguments passed to plugin, or a driver script). If enabled for a pass, then a file is written to the same directory as the output file, with a name based on the input file and the pass number. For example, given a custom :py:class:`gcc.Pass` with name `'test-pass'`, then when `input.c` is compiled to `build/output.o`:: $ gcc -fdump-tree-all -o build/output.o src/input.c then a dump file `input.c.225t.test-pass` will be written to the directory `build`. In this case, `225` is the `static_pass_number` field, `"t"` signifies a tree pass, with the pass name appearing as the suffix. .. py:function:: gcc.dump(obj) Write str() of the argument to the current dump file. No newlines or other whitespace are added. Note that dumping is disabled by default; in this case, the call will do nothing. .. py:function:: gcc.get_dump_file_name() Get the name of the current dump file. If called from within a pass for which dumping is enabled, it will return the filename in string form. If dumping is disabled for this pass, it will return `None`. The typical output of a dump file will contain:: ;; Function bar (bar) (dumped information when handling function bar goes here) ;; Function foo (foo) (dumped information when handling function foo goes here) For example:: class TestPass(gcc.GimplePass): def execute(self, fun): # Dumping of strings: gcc.dump('hello world') # Dumping of other objects: gcc.dump(42) ps = TestPass(name='test-pass') ps.register_after('cfg') ps.dump_enabled = True would have a dump file like this:: ;; Function bar (bar) hello world42 ;; Function foo (foo) hello world42 Alternatively, it can be simpler to create your own logging system, given that one can simply open a file and write to it. .. py:function:: gcc.get_dump_base_name() Get the base file path and name prefix for GCC's dump files. You can use this when creating non-standard logfiles and other output. For example, the libcpychecker code can write HTML reports on reference-counting errors within a function, writing the output to a file named:: filename = '%s.%s-refcount-errors.html' % (gcc.get_dump_base_name(), fun.decl.name) given `fun`, a :py:class:`gcc.Function`. By default, this is the name of the input file, but within the output file's directory. (It can be overridden using the `-dumpbase` command-line option). gcc-python-plugin-0.17/docs/passes.svg000066400000000000000000007573711342215241600200030ustar00rootroot00000000000000 gcc-python-plugin-0.17/docs/preprocessor.rst000066400000000000000000000020701342215241600212170ustar00rootroot00000000000000Working with the preprocessor ============================= For languages that support a preprocessor, it's possible to inject new "built-in" macros into the compilation from a Python script. The motivation for this is to better support the creation of custom attributes, by creating preprocessor names that can be tested against. .. py:function:: gcc.define_macro(argument) Defines a preprocessor macro with the given argument, which may be of use for code that needs to test for the presence of your script. The argument can either be a simple name, or a name with a definition: .. code-block:: python gcc.define_macro("SOMETHING") # define as the empty string gcc.define_macro("SOMETHING=72") This function can only be called from within specific event callbacks, since it manipulates the state of the preprocessor for a given source file. For now, only call it in a handler for the event `gcc.PLUGIN_ATTRIBUTES`: .. literalinclude:: ../tests/examples/attributes-with-macros/script.py :lines: 18- :language: python gcc-python-plugin-0.17/docs/release-notes.rst000066400000000000000000000016161342215241600212440ustar00rootroot00000000000000.. Copyright 2011-2018 David Malcolm Copyright 2011-2018 Red Hat, Inc. This 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 . Release Notes ============= .. toctree:: 0.17.rst 0.16.rst 0.15.rst 0.14.rst 0.13.rst 0.12.rst 0.11.rst 0.10.rst 0.9.rst 0.8.rst 0.7.rst gcc-python-plugin-0.17/docs/rtl.rst000066400000000000000000000142571342215241600173040ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . Register Transfer Language (RTL) ================================ .. py:class:: gcc.Rtl A wrapper around GCC's `struct rtx_def` type: an expression within GCC's Register Transfer Language .. py:attribute:: loc The :py:class:`gcc.Location` of this expression, or None .. py:attribute:: operands The operands of this expression, as a tuple. The precise type of the operands will vary by subclass. There are numerous subclasses. However, this part of the API is much less polished than the rest of the plugin. .. Here's a dump of the class hierarchy, from help(gcc): .. Rtl .. RtxAutoinc .. RtlPostDec .. RtlPostInc .. RtlPostModify .. RtlPreDec .. RtlPreInc .. RtlPreModify .. RtxBinArith .. RtlAshift .. RtlAshiftrt .. RtlCompare .. RtlDiv .. RtlLshiftrt .. RtlMinus .. RtlMod .. RtlRotate .. RtlRotatert .. RtlSsAshift .. RtlSsDiv .. RtlSsMinus .. RtlUdiv .. RtlUmod .. RtlUsAshift .. RtlUsDiv .. RtlUsMinus .. RtlVecConcat .. RtlVecSelect .. RtxBitfieldOps .. RtlSignExtract .. RtlZeroExtract .. RtxCommArith .. RtlAnd .. RtlIor .. RtlMult .. RtlPlus .. RtlSmax .. RtlSmin .. RtlSsMult .. RtlSsPlus .. RtlUmax .. RtlUmin .. RtlUsMult .. RtlUsPlus .. RtlXor .. RtxCommCompare .. RtlEq .. RtlLtgt .. RtlNe .. RtlOrdered .. RtlUneq .. RtlUnordered .. RtxCompare .. RtlGe .. RtlGeu .. RtlGt .. RtlGtu .. RtlLe .. RtlLeu .. RtlLt .. RtlLtu .. RtlUnge .. RtlUngt .. RtlUnle .. RtlUnlt .. RtxConstObj .. RtlConst .. RtlConstDouble .. RtlConstFixed .. RtlConstInt .. RtlConstVector .. RtlHigh .. RtlLabelRef .. RtlSymbolRef .. RtxExtra .. RtlAddrDiffVec .. RtlAddrVec .. RtlAsmInput .. RtlAsmOperands .. RtlBarrier .. RtlCall .. RtlClobber .. RtlCodeLabel .. RtlCondExec .. RtlEhReturn .. RtlExprList .. RtlInsnList .. RtlNote .. RtlParallel .. RtlPrefetch .. RtlReturn .. RtlSequence .. RtlSet .. RtlStrictLowPart .. RtlSubreg .. RtlTrapIf .. RtlUnknown .. RtlUnspec .. RtlUnspecVolatile .. RtlUse .. RtlVarLocation .. RtxInsn .. RtlCallInsn .. RtlDebugInsn .. RtlInsn .. RtlJumpInsn .. RtxMatch .. RtlAddress .. RtxObj .. RtlCc0 .. RtlConcat .. RtlConcatn .. RtlConstString .. RtlDebugExpr .. RtlDebugImplicitPtr .. RtlLoSum .. RtlMem .. RtlPc .. RtlReg .. RtlScratch .. RtlValue .. RtxTernary .. RtlFma .. RtlIfThenElse .. RtlVecMerge .. RtxUnary .. RtlAbs .. RtlBswap .. RtlClz .. RtlCtz .. RtlFfs .. RtlFix .. RtlFloat .. RtlFloatExtend .. RtlFloatTruncate .. RtlFractConvert .. RtlNeg .. RtlNot .. RtlParity .. RtlPopcount .. RtlSatFract .. RtlSignExtend .. RtlSqrt .. RtlSsAbs .. RtlSsNeg .. RtlSsTruncate .. RtlTruncate .. RtlUnsignedFix .. RtlUnsignedFloat .. RtlUnsignedFractConvert .. RtlUnsignedSatFract .. RtlUsNeg .. RtlUsTruncate .. RtlVecDuplicate .. RtlZeroExtend gcc-python-plugin-0.17/docs/sample-callgraph.png000066400000000000000000000144511342215241600216670ustar00rootroot00000000000000PNG  IHDR05bKGDIDATxPwgCHW9<]FG:--AuDoZ=yړhX[[fM:=+k֩ z"XF~6 !~|KBHBM>}:???11#'''66V޺ugE68^,Ȳݻw@P?&Oꫯ:7omdfs\\ܔ)Sh8kmmPZZΝ;/L(uy8LN8Q*ZT*mǎyedTt%pVe29/DnaF8-T(_mrƍСCz~ppj:]paBFwҥ'x"%%eݤ> w B===~ JFlȅ ̙zzw\nѰMaX@__7{ +::O`)bX,Odc7Bw 6R>OEcv#?Ć ^t#W¯0!L؁_ L"uAC LcvH 1bD@f3DDDxTDDYЯ %Ёo1%>DB+C XvcDXXq0!d2^ցj+$9fc7삭1c"u7}HlF͛7-[f01{4:tPzzq]̱lO٘ "@'&&]0h̲ü ҁl3fKY,_Bd`X;wx,chH(}GKaP0؍Zw qz|Pŋ_}^%%% g}S RT(| q---J2&&_%ȅFFFΜ9bN?&JIOOw\_ƖH$ b>/_Cds}SVU&-\',}YߕQpaf񵵵b+:_~ӥ~,,,?dRkk+K-J]F … ^*ʡo0'OGl8.33S*h˓H$ww _jP :}^R HLϼ? إq'O{D".Z`T*ݵkdƽ{HDsNK_. <|"&l¾;?UeZi>L}h4ڽO^޽{v? U?x7]fZ>/_|vxb׋'?я~dX,[lc%{8+w [O>dE^z%D":u_1{Dx_zJ$++kΜ9W<3~駇]dcuVUTTؽmAƤ輼3g]`F",ZȝF#kml}$rJ.\ߧ-H,Kѣn.~-ojj"d?ϟzꩴ4TWUU|#\?id֭IIImmmfz{zz*>شiSÇ7mڴiӦraQ3IO>ёjme[n֭鯾*q'Nhhh +ekjj֮]K> hǎ+((VrqNۡpvL&TzTwD>ȟV^Hf7n` hVٳg ?w}W.KFk|2Yd.\X~=m~F&9kmmPZZΝ;<[,켼>111^V{lNg{DV@\\/d]0|S>z푆#G ЬY^ðAn?|, 8r{N:E~d&=%A^ :1l|h$&LAxNqqqvv6$I"͵Zdĉiii.֐n~ $''y`Νw=|0ZVr]ɘ488Fzٳg9rd׮]7LdnjSWWG1H'HLL[lyICmguxg'Ex(xkw O˳d,[VVF~%~m۶ ,]TT 6tttNHmwCo>+Yg#& Ftd#:a0وNlD'L6& Ftd#:a+ ׯ_;ΏPpie Y|9{}6& Ftd#:a0وNlD'L6& Ftd#:a0وNlD'L6& Ftd#:a0وNlD'L6& Ftd#:a0وNN Dk׮&oݺG"""fΜ)BeH `07l65Ò%KN>ȒF,X :+0̊+\=5kְ,׃DDU.\L&ɉ |IH,T%;22277W&=n6WZ%JIH,T%VZxT*Edϙ3gرGd2ʕ+D-aaaV6$fyʕ"DA6W\ȰM&&&2 #bI(h`ƌ'N$?aG! 0̚5kb`+2:Q؍@}}SO='Oo.1~ / v-HC$Ghܹb|KUCv#555˗/p5b` Ӎ/UWW&&Z% Мl4a0وNlD'L6& Ftd#:a0وNlD'1m0F{DmZʲn "hll,--]bɓ'6"$[*n޼C]5ZR`ʔ)v϶:9*A&Ν"##{챞/=sp=§׬Ys%3c%('؃/^Z+D~ma b̙W^o Ð{r$˲555k׮r\Չ'>|i&y?}t~~~bbbGGGNNNllV%1\+$%%,ZHPhڋ/Ӯ+...**H$= ן4 l߾>:vXTTL&yONNnZ8 p)ǵr(--tΝ ZֳgϒO?}wrT*mllt]]%SNUT>***Xy`v ?<pM{t> ( _d#Hvs%K_\澣GFGGo۶Mz8BV !!ABA*h7n̝;뙙ǏOu迂% y{Z*H>k,Na%{MIIbY_D bI,K'8p A1 Ftd#:a0وNlD'L6& Ftd#:a0وNkeee`@v_.v mdoܸ10u [C~}6& Ft?AE2[%IENDB`gcc-python-plugin-0.17/docs/sample-gimple-cfg.png000066400000000000000000002747611342215241600217600ustar00rootroot00000000000000PNG  IHDR5RbKGD IDATxy\e B n)KnP"ZnuV*hRY*n?qͭRT@rK e??|}X< zz>8s5|̨n\. PeAh>@{j*gϞ.AKtyٚ@#T3QQQ7t!۩S4h*T=>@3}t???MWQڊڃ} y=Ah>)i׮ݬYU:3\. sww766vww *}RSS'MbdddooŲOOOoѢaTTG!ϟߡCݻرC (}:$$dΜ9k׮Pq}'2, @.'rӧOo~ƍ&LhժݻwYh?ӧիWnڣG5kxfϞ]V#G޺ukСׯW.&44TxQFAرC&r__~ʔ)mڴB >ڵJNNNLL,9Z^^ހ֭[7p@5p߯_dv͘1cĈj&>@hkkf63fd2!L&1c~:lݺ~"BX[[wYeCo- T 6!LMM222ۧ j>@nݺ?&&ȨCWWW##XiqΝ2߿qrq,%ꋎ]gϞBSNUhZ!)))44T._xѣm۶ Vs۴ $133KKKoݺejjjffV86lV5jdɒe˖YYYI v#sΕ>שS?vqqQs۴4'''Fn߾mccS gΜBUz!Қ6m* Ș={v c>/Fpssrˡjn.=5O~vPQPP=gΜ[n͜9SU 8W^ϼ@}Yzv|ȑwy癛XXX<|Pg;;UΝ;WX1xoPk* B(j j !vޭNg bEKqqqFFhggY kחO_Ž{ -R¨$}H O\]]-qqqٮҢ\.Pg;]~}֬YS9rD j>@Myf!ěoNc !,Y"˅r|ҥv! Μ9ݻǏ/u0++e˖EGGYѣ .j\f:(<P#$%% !n޼bgg7}tE1cƨl5`G޸qcRRkLLLttt```6mn[l8p`Nu윘Guԩd1 8{3oӦM``rСgݺuJh'tՅL&۾}_6Q|666ݻ3KvP"r|?cBBB}||UJII7o^ll˗;u4sVZI㻹׿}͜9s\./{vM8Qz aH_`dddGj"T3Pyx~=Ah>@{ڃ} y=AZd2g y=/""655urr zZinbb͝7nܨV"Q^,9Hff)Sׯ% dNR? 20LWbM8q4hP\\ڵk?~CCCE}yxx8p`̘1-[tqqBH}Νkmm=nܸrx5ooݻwKFZhі-[<<<=m&GU frM@d۷ѣݺuݻwdddzSN]dG}P,X|iӦL|rEGGU^^٣'HIIֶoΝ;)))/mm۶{rK&};m_ !f̘!}BÇرC#]vBܼy*(( >!"B5…aÆi*5 ;v1Eɍ7H[j%HOOtttÇ?ٲeK~[lB0@fIIIyyys-sZW˖-4hP*ss>}uݶm[۷o_]31lll %TԩSQF=ydϞ=Ny󦿿ݜ߿?PwiР-[n* EfSNB)ݻwĉeiiWJ nڥK[[ʍ#h={7n߾}6666mWƍ7mTѡuVPP|իWWQFeff9:x_@}wݺu[n_uq{{)STtcǎB &TtOOOt__ߊn j"haÆ 6%Q<^{;w?HIw޽{ׯP9%oƌBh3?3gһwoM@͞=^z}]n7xxyT n1y=Ah>@{C&5] ZnmaaBsΑ.@tT5]3@{ڃ} y=Ah>@{ڃ} y=Ah>@{ڃ} y}/Œ^Vi>^n֬Y]vHHH(!!!a޼yCݴiSE/ܹ @Ejժӧݻ^{q>͛7_tiY;wH>/Tw}(|󍵵ݻ]]]KfhhX͛7.}]vF^Py/۷e2󌐔}* #0a“'ORSS'L0a„W|'#Gҥ9Ȇ AܹellܡC .ӦMOIIҥ d28>zhҥEZL&5]Z%22rȐ!r\&9;;_|Yj׋ ͭU*=UV5kʕ+}ٸq.][oEEE_ݻw]`oٲem۶qzGGGEZIWPS|G:::BZj]vC͛7OGG200lܸkƏojjڣG!V*y/IPPPVV>|WXXX顤PGGʕ+ʍUR-j?~e˖NNN!!!ut9y/رce2Y߾}EEEB5W;sJ@5E !k̔'N[.++K{Ν'OTNe^KIIQ7;;[ciѣGƼ<`-k׾[i6JBCC5]ZҥK;wB;vÇ377777?v؉'[lyɫWv᫯ʪ_ҥK͚5_vt]v !/=Ah>@{ڃ} y=Ah>@{ڃ} y=RSS7nܨXpb… TrM@VPP`aa#]4\.ɤƍ[v P0祧7bĈZj+??_Y.9R5)*pɎ;*))IqP@𰶶.ٮ?b>/ yU@&뫴6L#% @ոpB۶mU6mz5f>F6m^u===Mf"ʌ=ZOOOXXX8b b>/UƍM6mdm۶={PpU]v:::BZj5JqJFCj5yU׷H&yxxk.@C@Uڵ\.3fkP ?ϟ*U)99˝;wV~W/@ 9rD%l.KKK|xAA۵k'mXrŭ[nj:ydSSSiq˖-#G>}j699YׯB̘1C& !d2ٌ3?a֭ׯ_Oaںs*C :~[hQ``5(>!o Tf%J#۷owM111FFF:tPJ;wd#Ǎw`)14ڵk? @@5*/^xѶmmZZZÆ ut/133KKKoݺejjjffV86lVP8{lJJJ@@s$}@5c5MKKsrrRi411Q<666r̙!D\\\NNQ_EEEǏW^HHHʘ T3nnnr\._|944488577Wͥ')Sݯ* ̙s֭3g_yIgώ[xyƌSL;r:XXX<|Pg;;ZhbŊcǎUx!w}`ɓ'UnPyP h4iϲe*-(yP!O\]]-qqqٮҢ\.Pg;]~}֬YԩS~~~[nUM i͛o:ǎ+Xd\.BK*څ ,8sbw?~¬-[f744Ts+& T3IIIB7oFEEM>]!11q̘1*[ 0`oѣ7nܘئM-[ةSnݺ9;;'&&FEE}G:u*YL}}}Ξ=k``2332W:(dm>Д!Cp&{ƌ%;( 5\`1!!y>>>*[̛7/66:u9sfVT߿}fΜ` 2W?> jڃ} y=Ah>@{ڃx6wwwLBwhѢvd5o޼JCG4]d#GhCW}8x𠩩鯿Z} zeo|mڵ{m(7e k_iiذaFFѣG׬YW__aaa_haٲe999K. ^tivv5[5k"::ɩO.מ59IٵEmW|c͚ٲΆF/WիtRMxIj]]ݺu* :T!|}}/*!͢=zhǎ̼KJdffN2~^^^{))k677wvv޸qZņ111h,..."**ˣFWQիVb}rHHavJ7ikÇKmڜ=Zӄٳ >T̊Uvόuh۶ɑm+}߶Gt+{ĉGY]vWPn]m۶eddj i&!D߾}AݻwϞ=|Բ{n!*ӧiӦ۶mԩӅ 4h, IDAT-00ڵkJ5k&=Joݺuɥ"++o߾FFF3fL˖-]\\͝;zܸq%7W"..NѢE B-2nժa׮ufFGټ9 t9͛ u+ɹ/RóΟo^EgK(a-[ vSgδ+爃C7pUdW,hGkX𯿌LLNB$oߞZ]T3go߾O>ƿʕ+>|q Υ^~ovlll׮]xcǎׯ&Nzj[[޽{שS'66vܸq%NM-]ZL&sss~# Ш۷k,dddL:m۶YYYҪYfwqʔ)s{u> ꫯůJd>zzzϗZKViv&-.[L1eʔܞYm9}ƌ#HJJ*䥧g_rcM\kMqr:P\P 䦦JRv:ܤIgI7i<ĉM+V4J1*[/֭=znp&'zzrvqa}sʝc||7iRTx/R1gr.?FѠ i1??SN*ITTSN=R4ܹd=B5k֔U:(5~n[CeffΝ;wϟwuu]`>8qDxxɓ';w<}t}}}\9}>[k.zzzҢUEw1bt͛7+[l\b``P$ffFM*X!\Q)/,_oFKٵKaҢLW\i~BXF{ӰI%oC \w/v|{:Nǎ) 5jԼysi.ŋUzumذA1|piQOOG~F/?^zAAqV033{aiӦrݻ-[V:uYfYYY;vHIIo/\кu&ԩK_ڵk߹sGNYagggcc#}nժ"==ңʕ+eCϜ9?q]|ᘧCn2%'xrrf*}re:: t).oCeA!98N9zr|]E@8yA d2t|^Y_bb._ӻDZ(7KMM=x3T /x,--'OԧOuIBDDÇW\o !}\>y?^%WL&MttjתU*}ZlY~곴T|UңE&>\ q60Pduנyy*=/9B~zzm;;oZV9=x_4K˻{\7/=+__i^ׯL&kݺ[Νҳ/--PJ$GGdž%XtM>O^ FСCO?dkk+} !U/7oTo7o,9iN:/ {o߾]_ˋۮ\~@PǩSQjZkڮ߰۷WUZZ>矂*W3\fҡ[Ǐ;ϚUq㻻v]_ĸuZFF7W[ s)..޵kٳgڳ"11Q9tJDu'q-@+A)MMMRt|P3='O\7z'O7n-ycW8;;UzJ7 ,-{Tܯ`z[ή EsT9:ʋrE>k>>vuJI[wͿm^]#;^~+ի߾}*4c~~ӧ-%_۬YIysjTS}5Ι3gRRREEEEEERK#""Wt[~'!Dqq͛Eo3x,--JLUHM%u9ׯ_ BZɋ>6 @}{ѓ'όSTe2!AFU2f]|p3yyy%''+m۶ YUaa޽{UL6MnݺlEJ̼r x5*Ko߾/K.999033[daѢE)S֬Yӱc}:99M:UWϞ={ʕX++@7 >$%%)/*>[n^^^>>>&LB !;w\m~~iww~ɓV9I-iSҖ-Onܨ׼X=SS>6#F$mr쭷jծ8>AllQU(TjذsǛ :0'"qrimnj˽z###ܼSSSױcǖ󺌒bŊUV]|78vXu}„ WnժUϞ=ԩsܹcǎ}%}}}}2GWx{{:u̙3M4:tٳ-,,͛7?ٳcbb6mdoo?lذ9sTܩ7oޜ9s]֧O+VH1wR+;v?BT"ѣsDDD%BVݛ~hm[[S&ONٹSdjߺ_f;s[o5cݚYǎwlI?zM[,XPa5h˓mUn _0@q߀OrT!SE7ٳ/y:::̙3gꏠ{я>_~zjϞ=ϟocccbbmժU=zXzsrr7owߩ ={ 6+%>L^9d‹ J>ZvmPPЅ ZnZ#炂lFh皮EٵkO?ԻwoM"~L8qʕp'N,yW?TFjԨњ5k4U@B䅅i !:uT=/٨QeE}sAA;z8k׮իW91fP TW_}pB##_1cdzzxC&s;6xT8qbС{wnnn2l׮]=z0`@9}ٳgx2&1WØh|^fMO?=}W&&^Eܹ>>>zzz ysv@5Ah>@{6SNdVٽ{wxxxӦMgΜrxhĀ~W?"%WNUVu}̘1nti<}ĴiӮ^z^{Mk.++k:^> ==mڴQ4֯_СCtketP6lR.44p=Ah>@{ڃ} y=Ah>@{ڃ} y=Ah>ʼyׯB4@&k]&d2ّ#G^PBϦ:d244ΝSl^Qүm:::}Y`A~~~Fxɴi<<<5jdbbqtE:iӦuqtt=z?Sjʚ6m͛}133ׯ7|s֭]bj(_ U<))DU<ëp.ʯť]vK,iiiaaaڵ322j߾ѣG*@Q RTT}vwwƍϛ7B-[WtÒCBBf͚UwĤ{;vP`iiu.kkٳgw~ѣGV244 yرc[h}݃:995l0""u%w!0eŪ .nz陙#FȘ>}z֭?Jw^PPFO4޿?//O#۷oOHHxq>|fΜdɒ-[7NWWҥKU[ޫocƌi߾;ML6}۷oܹW_666%{oٵj9r[~z/N^urh9 P*!bqݵk۷#<~Xe…B_Z Pݼyۻ2T;V_+Z-[& TΪR\\ܬY3OOϗk}ʃcѢEQQQwܩ_\.߰aCÆ ۧʢK?~\^ѣGWb֩S?PUVH֫Wm۶I7mX>}ڨQ:HKKSl.B(/VT9G!)Ȭ vҲnݺ...|GT6433sʔ)R3f,]tĉ&MٳU;TYCDDomjj*ݝrUr.ʯa޽ʓ:NCݺu}||mۖ(IIIy䉭N:o^%̓'O&Nhmm=rR0?󪮴:H]]]Kմihh>}e?< 6!LMM222W{@#G !>\6m$۷ok j׮mllu-Y*4HgEݻ~~~.-Z(uĉ}}}\һwoqIݿƍ7.??? T!++o߾׮]3fGO?{֭v횴V1Zc9G"fΜӧO+W(0eAbbbԯgΜsӧONaÆ͘1#!!… ӧOW:q_&y6'MdhhtrvT)::v={ !N:U^'WiÚyPQbNd{BdddL:m۶YYYc:ܹ#-;wN;(:4m$;;"'+()RERRb%ٽ{w%*,W} M _-QV\==dSDMLGoʕFFF 4P\ϣo-P~W_}rj~QzzC>W^ ;oƍ>Uu.*=NW\V|,Yy{ 8P:L֭[7uy &11Q__URr_Tjjթ$L|!{{SNmݺÇSf͚oÆ e=?zUezRJ{/BWT!J&KKɓ';99gݺuʿ ! L<9((qFiӦ/J>\rIoZJTsNڵk]I;Ն4ή#xyyYYY:tH&M9U>)~BOw 8pQQQj}^_?s\./$&&+4j(111//O]tITwi]]]?????ܹ3<<<88Vz'r((iΜ9ҋk/={Boq-"jʺ{|yI.~kܸbܹ6lh׮M6/ \E'RV_TzKo"fϞݿǏ:::Co+f̘Q9:,,,Ν;WXX(P!DJJ2a>/ؼyG!%%%--MG;uꔟ֭["YZMMMRt|P͒KC+u*qf͊R7?{ɓѪĉ]IoZP[3]zZ!W4:_ܹx&5O:5Hy֭{GuV?pҤIΣG~8::>}Z9g>}k֬ٷoߚ5kbccի?Th>ݻ[n}ӧo=r$>@gϞ-[ݻ6lXӯG IDATΜ9#")** /**R~9w /^TK+%%eBw}WjСC~~~DDb6l<*|~(?MaڴiBuegg+8 }6lᅲsNL6|p?+OߓtWpBo5k֘I(LV yK.Ĵn:<}ッ>rȔ)S4} :4444::z~~~ݻwtǧLү_?O=\ ψ<^|Ry*/_~UVmٲW_ݵk;w9f„ ֭/_Ϟ8q]5OTUTXX+/?~Ç_WS7X`?\X޽CBB6m7nDGGbZ>$Ouɿ$Iԩߒ%K Y̙3ӦMիW۶m[h@} 4 ^^^$!VX&IRi&??_A$I:t萮 *GIU} 58JAAu&NhffVВ$ۏ1bҥEEE yy{{[YY;믿s玲Þ={Ə߭[7SSSI&ݸqCenݺpڵO>dĈ#GOOAcrJ]TNR(I *@]~'Oj$I ׯ˗ܹS6nvyWW򇶳 yfjjjbbb>}vءU#;;K.>>>=z033_۵kԹsg-[߿өSN>ݺu_!l3g>>>OCCCTCP8;;[XXUs_ۧ%%%-[TXbfτo񆅅w}gϞP1uԯǼy䖵kΛ7/$$$::Z:Gڎ3fͺ@=v@#|^@%Ϣ=vX~,--+N͙3k׮mڴ6lݝ;wZ!222ƍ׾}{GGǯ+GXVVVUu>|w=z믿ӧO9QO1~x!d+>!رc۶mQpy]!ĸq"Sݿiٲٳ|}޽={nZV@@Ν;ٳg-UxWkVaE.]zw#FnׯO#G,**[nݺUq(OOJ{+bٲe5ks̩>O5&O,|W\BL6MsssΝۧO##S޿ҞgϞBL:U:vx1-jݺu~̙ӺuŋjTd5rrrRSS˷B,^XΪaaafQQ=|;vT'&&FaÆ @CFnĈÇ !bccU:˛__j]v\ZZZf)^#tv~o߾K.-Rii[j o><|Iπlf͚͞=;..O>9uԀ|MUϞ=˷s/^ҳބ MccѣG+P6Tk׮B |РC~ݣG-ZܼySAUs*ѥK;ʯ{%sNG4{{{u(4\3[n ۟|ɝ;w\\\*]OJJG*}G{nٲ7on:x''o߾=++>][9uT@@Cf$IZ>ddd]q˗/rDy_vvTР=3mehhصk*}\\\*>kCCC!DYYYG1IԌ𲳳*ylllx㍵k޹ss⋥KaaaՏ$[e˖{_~ر:t5k;3vjݺ; TzV}qpp(.JLv=KRuР]vM]vISSzKzquu"L!D.]j|aÆ߿|}׿=ZZ=F{ɛܹs=0f!V,^,66699O?xW*Y:##|>ҧcǎyU!S 4h?NOO_?~SN_QȴѱP ~bȐ!5>VVVVNNI!D||uU͂+;\|YQRRR"UٕvޭEEEgΜQT|xSYY:)p!䜫҇'5º;)R5~nמk׮] ϝ={6++KYZZ'-]vݳgOJ/^T'ꫯ/QTTe!cxxxTM?հanݺ\/ҥK۶mh ʽJJJvܩg޼yBG)}5@cggdɒ䴴ӧOچ{DDBTPӋ/e˖aÆ=Yf3fhwuh{͵\raذayyycƌQɫwZrBBBbbb<==92eʔjQф ֭[_|g=qDu)R*S%JTn*;q uέE$C!T"%h'ys麶kem};LJ>|oܹS*Gӑ~{};w=zT_|mllZl>{t͙3k׮mڴ6쿗3gtpp ˫85^ggggnniӦ|Rϰe˖ղO˖-Gm۶\JJJZn]#kN8aÆO?y*o%$$h[2tP!D|||  !ZٕBzE{}vNm:[!B**dwkG{ HLԴ$!sŷ4V4[U()))֭[h?\VV掗.]zwGR\\~zwwCBB=K_RR /DEEuW_tȑ#+;s̱cǦ >|ĉ5&gg碢 .aɓ'cƌ1bDwsrrV\f[[[+A_";ܔ}?/*UBҢ[o>y>}<{_dcc!ĒhZIzzk׮rrrQz[ni:bϞ=ռEA N:u񜜜͛8׷cǎj޵kԞ={*[.]pGFFyY??dccc!Ē%[n=}tXXXTTx*G9rHTT} WA[S#8==fڐڷoݪJ}MQxx۷ӭ[7!IJerssm !ʺ6!8q򦉡+z׭}e!n!r'meҚ5k&'hZwjU?S[[[[ZZ֨ں}c 탃=<<4]aʪ|Kpp… /^ҳ8$$D+(O0aillk@jcDŽnnnڬ Py_z+VU}ڵ-7G>988} .$ ^tsrrtw !e$GDDX[[;w?_,!uW[̬2f*#!lZ&\ƦӧW !zi1TRR2i$MGə;wJڵkmll 񾾾rC !|||Ժfon^3SB.]~,GG[nf jU~_c$9;;;;;?~<..… 0`:)>!ݻ5*ƍgΜQ>NNNW\9x`PPF7pr&&&C-++;tP\\y&G~@yk׮ҥ}ҤIW^|y%I۰aQ#k'oܔuoSc[oH! iKG^ZyVKY1Ow IDAT/3g?o>I6,&&&--M..]ڶmFGhΝW\qss9sqtXaIgBS~#7tjZ~S:jDPԩSU_+Լ:t o4=D СC~}^zŋ TǏ~~hСK,رa|x>}O֡CnȐ!FtFt>Ϝ9̟?044Ʀש:ƼZbŎ;\RTT8z 뺮?_rrr;h ]w;iw@4뺊^U~b>/?A>ܹs%I...u-*?|'ݺuKHHxwu]"USNuwwBBBV^k.]Zꫭ[޽{>xɓ'. #1o޼{vAallkkk;nܸ"]WSݹsf޽{V6if#GLIIamH@Ү]]tr} y?A>@} y?A>@} 4^^^$ hBXdo߮:}F￿tN=mڴ0((??~W_}J@F.ҥN{M69rϞ=$幹;NJqڹsTNyf!Ă [XX޽u.''p;;;ТE oooeСC;KU@e h"""u떦]YNNʕ+333׬Yckk+kZ >ފ]5nݺ !-[mV]e k-!-BƌsuKK'OTէyUwbƍrݻw666, F[_ܹs%%%FF%k>}U}}}C !|||Q h (&MB,_\aKK˿/;Kݏ>@å/33:iE޽CBB6m7nDGGO)>@å2!WYoy/߹s7|lٲqU&?{VTp9NIhѢEU-..NKQP%@ ~W++!CMyZ7~x##nc-8}K!'} y?A>@H@!$Iu }:* >ԩSNt]@ bܸq.@S} y?A>@} y?A>@} y?A>@} y?б>tWi>_|hѢAy{{_tbK.}Ǐo*n `iiYuh+os:<|b={ZM͛7gϾxbiii #|vvvFFF?C߾}+ּyj6]6au-ZruIj3Bffom@P'f̘3f̘1ѣGG~o=u͛7ʃoyaZnqǏϛ7>++k;w^t$IrUV)71IPJLLL``B$r{=JKK322۷ookk*ҳ[NNNiii߿Ç_W^Uwpp( @/~@ahhhiix>s׿5kөS}C BKe^"ݿڵ+,,,))Prnh``о}Z@#}ԓ'Ot=<+[򲲲⢣߿/8}͛7?~,ʥr*:tR>ѣGBʛ@} y?A>@H=zT ؾ}tȑ: @#) ]@c++:k[nϒ4YLMMǎkll\U@SFL0ҷڶmWhЂ[XXTl711?~ '}h LLLTڋ^{5iyhǩShkk)INJqmggW$((@}"@;$I .?ɼyК .[ٽ{t >Q~mll@/K,u@rƍt__f͚i9t萮K=#]n>|xܸq.hBLMM@K*mѢE *AP,[˫u^^^˖-S(*}׿O8ŋUgg͛>|hĉO yم/^aƍ[lQ{OS*{oڴi;9smllf̘ѫW~aǎ#FoݺuȐ!~o߾]ӽQ3ucǎ댌ݻo}۷o$IP;vӦMsݻt„ 'OtssuVFFF G=fM?Çg̘1?P}>@:w|-5WBwyG$!$I/;lݺʕ+ .T}B;; UZZ:~GX"$$ϟ?d…5/T)55jOLL433P;$I8qb(Н;w.X@N 5uĉ 6۷y5/>̌P(/^},X999ڵ30999aaaaiiY86o5 >uVddu]tyy6m(;ܹs믿V~ԨQBGv/{t s>Gi߾+2vت xwtRm~TG< '}} y?A>@} -{"n7)R"%]i&??w$Igճ&aAϤ$I:TqMֵkWSSӈm4&}ĉ%I*ߨP(,Yann>x۷0\'5[nĉfff:)CV\Yqϟmllۿδi6mt?zhʔ)_}E #HBgmٲ? ɓ,IE}^wX=So3,,,,,… 5IIIZbm@c!) ]'Ç...'O===gȑ{$)//ѣGYYY&&&_V~`a E\\j^^whݣGlmmnjybllܽ{TՅF@+711YpJ ˁYXX޽%y>)R*S%JTn*;q uέE$C!T-*EJ^^yOum6 v>|ڹsT#kK\:w܉=z߿|شl}22|ϳg%'' !,ϥߺuӧ¢ŃV9ʑ#G|}}ۧ\.66& :5{Ԇо}{V4"}/<G{<9eO'!{UzHXiiiu<߆ 8:]oDD$I4h7UtԩUV;w.33Hn&AweddWRqB˗ b|<ڵk[o:}spp5.\xx^{MA@BBB̙SUkk{ !6n(ܽ{WaccS?֛?th̰U/!D#ťKmks,Ij[o,:''ŋmڴ133oBG)I{"## TzTz_tpph׮] *{߿}ʓIAxtR!{ eϟB('%I =o1 IDAT>w\II%j딕U݌Be!MKՄشѣٳg E]⧕utұc۷~___WWW pxⲲ;w+rKrr}2S/uFFFAA2KIIQӱcnj;w0+۶mۊuwU!D=4-_~㏃JJJ&M~ esUiYvM``>>>>Wp!!O=Zo>|;٫fbyJż֏֭LAVk,]$gggggǏ]pܼ_~ 077Wgt;;;e'ؽ{Fe888ܸq̙3ӕʕ+ hN5dСeee#@E@gv.]('Mz˗_ 6XZZ5"vMɛz[656֎&Npիuie>$Idii/9s$Ioabbbrҥm۶itɓ';vl۶mrWRRsN>ۻwottQZl)7ݻYYC8q'' j&Q!Pzi&ww߸q#::ZG6|S{9mO^PR0)*Պ^:`"""7/G\wXcccWWHHHLLvv#GL(&Lnݺ?>'*N>|ءCcn޼ƃHԷoDՅF!h =ǿ7?U׵Jllرc80|p]ҀڵkԨQ3g\~F;65sZ~ rss5h} YppVӭ_jȐ!.DǮ_|]\\}v!4qF??Zf<ݻw˜m4"}'7o>k֬o6??_׵J\\㍌ Çӧ:tw 2dԨQӈ3gr:@C|^@1 hVXcǎ+W9::ػӊEYRF!aK&Ef(̂e]Ƙ0~ A5CDehQHq9߮蜖vݮ|~?OӜSVVw!##>ӧ;F4C ,X`S4VFFFFFFNyztP}"o߾%%%oz;xtpByBЬM6wW^^fÇ zzոqՏ9ҪU͛7ڵk_a@@}@Ah:M>4ztP}@Ah:M>4ztP}@Ah:M>A$Annʕ+mmmE"ћ[ҥK544 떡ɻ|H$ wMI-[,ZhРA_B]Ŵhƍ'OVSS{޽{ڮZEY1}޽{_|GҊ-x=sݻwwƊDGGչ'׮]ۯ_76Cs0q6mڬ^ZA1}:t萨 O_x -,""͛^^^uP_~}MXIOOuֽ{O={VH$rppLLL444 "}ŋf255ן4iRnn}NBBB\]]555O?Ujкuk={>>=zɵ&&&3_~iӦ%KTl,7|#9믿lٲEW%22r˖- 8zh6m$'CBB#Brr2>a>/@u떪jǎyω|򉾾O?7ְ'nb y7nܨԲ[RArv!ĉ%]n 3f̐A3fLzgnnի~Z"ȑ#iiis΍5jŋjCrr~ߑ#GjԴҥK3ѕXXX?~o{kRꝤz%d##+W޿zzz˖- ɅC ̔gϞZ =eAW:tR9sm۶ɰQxKɃW<>xWAHLL|5C-N:u .oo}ۻeggGFFN:VeL8qƍ7oy󦍍MTTTu4ztP $d!((H&e~a˖-h^>})>+< >YwwH,;Fyyy^:wZOCb={|G4 b?#***??> Թsgyh(tIIIﯿ!8p%@U"G=z3g oߎ;wnnnn}ggggggy'4/^0335jԦMigϞ*((|ǿK= ˓Ν;סCGGGggCrh+V|4… W^]ZZ*˗i uSVVv}Ν{򥉉s/@@x͛7gddGEEE]v,//TPPصkׄ cDgggwٹVoJ[[~y'š5k*APRRJIIȆɓ'O,۷%}' R 4n|ի322i;wzyEEʼn'ǠHohh zޫW/yύ>e˖cǎ=|pm:lmmccci\ ANNΟqwknРAÆ ԩTzۺuyn߾]O+m;wn"d^ MMͧOǸ Ǐ?\:wСmڴw:CX[XXlܸrѣG.]t=AJKK{>}YOhJKK"""ZhѿIV$; h'N 6,!!R溺Z[[EGG߻wO,_tw2@SNEDD;vNNN#Fחw:LQ4Vnnn^ pO@#䤧w^Y:iҤcǎrP\IIɹs~cǎݸqM6...Æ sssӓw:pQ4&?;p,ǵ\l,wJ /_5jԨQ,,, 48ɶmΝcUUU sMM2UVVv…tyСnnnC QQQw: P4&#GTRR:x,={222 e9.dkjj:;;5}h*,,pwwqqqڵ;{lzz_|9}v9::fff; 43f̃tttd9)SN<)A>}*Ϟ=;v>@qAaݻowAmێ;688Ç۶mݻoDDDII3K @}ơcǎs/_TWW߽{qܹsٳ^ھ}eIII6lشi͛7+Vj?HV*--ի씗LJjkk>}Сmڴw7G\B0Q4ΝsttLLL~w^N{܋/:88ϟ??//`ڴi .ӧOEEE-[~tX1˗/>}bqmIԩSʃvwwwwwСUy:| }СCnݒ{yy9sݴiӢErssʤ@$UYRl [nݽ{ȑ#>}H$rrr=zȑ#  4'O:t?~,q{%ܹ#ަ+Y7ܹ~ѣ;vm/ܹʕ+SSSPx\3}F ;;ի;v$&&fggϜ9ԩSs122rrrܹ/$={zj |ԩS{|7 9s/^HF9sfAAArr_|1i$''W{ 2D]]޻vZ^ԍ ݛeoo~z333ccc__߳gϖ8RXXX?r]]]-Z4|ϟ ޽[YY3 ⢢sεhB><<&&&I'O$9⢫_EEE***v8X,...аaAXhQzzzXX }EnݸqBɓ'9rXkeeU^^.GZ}d(ɾ@mEFF:88nݺSԩSJJʌ3APVVLzxܹ;wܹQQQnnnU;qFqq]է͛ vJJJ]_tRΝ;kiiemmmmm:rH--#F>\MMMƑjѣ>lٲe-Zg>M>@#1mڴ74|ۮ.]deePqڴiӵkתOz&VK^AAAGGG8}_Iwܩ=rHOOOggg$G͛LڵkÆ `>@C|]=no ==ɲjSRz̙3fffxMF/^|ʕT??D777==iӦ?~#3>>~۷og>M>@Cnoo_oeQTT$9,..A,[YY|rŊҖ6l8[[j:uH$>|WGX\S;_ԁ󣣣?~nݺǏkhhW*^ IDATC5o"""~ײV4 ]ddAѡC,^fee%²eRRR6lؐ/‰'nffl2ooݻwWs̙:ujqKJJ]&Ika:wܶm$ܻwŋB\J A )³gϤ'kQikk{yy>x`˖- x{{J :Gڵk?~,555 k@S VYY٬YMP񼶶vdd+~7A:t kBBBΞ=;{˗/:u}'"## 6nX2y 6lXtӧO7lզMsM<ޚ5kbbb544TTTV^-=0008pիCBBAxⅣu$ɓ'/^ +277T iժUϞ='N'^xqÆ k׮zj-juͺ}ZXX߿ٳg!!!m۶駟F]| Y\\իW{!O?ٳg2}߿?>>^GGg̘1&Lpttv-TP hΜ9ս{wߣG}P+_|E\\\rrٳg ͛w%yYhΟ?߷o_V2:lѢEׯ_}ԩ>}tIRdzA;{l~d?nYYٕ+W344qㆷ>`cc &%%;Mw:uIC'&&Z[[KC@sΝ;wfeeYYY;vҤI]tw.4\ϟWQQӧ쇎SUUY[[/_<###,,v͚5޾}{~~Q4\ϟٳg˖-e?t\\\nTTTd?44#FعsÇ٣>k,==q>|Xh_.CűxF˖-ǎo|SSSsܸq@#C@\zU.uSBCC׭[5rHvgϞw: }*66o߾:55ٳg@^g̘qǎ0`U@@@ZZQ4P111zzz:..NYYGPe@@͛7O<٧O5k֘1ѣuX;;; oeeբE DQQeǎ>ܹsH$=ztN_~]hp(9d^hZl9a„Ǐ/Y$::gϞ{z!OMMիl tw1((@3}ڵkbX.EORF" 55򊈈N|P4DqqqچrZQQFCFAAq֭wnݺbmm|Ly@֨xyU[j%oCKKk/^LLL9r  @C$/0]\|{N:?yd]]]g;NĪ`VVV&&&NNN&L}/?zw-[[ZZ6msP/ggH%%2&mmnݺXZZvWTTܹS{,ڵCIII޹x_J***NNNNAÇO>UWWp(** Сɓ;Ҫ狋E"ѪUd#..K.ikѢرc>à sss77в2ymQ4=z(//z^YYӳw޲!}Bgcc?feeر#G466^tiffPw E'+V,>hZh1iҤӧO߻wo?SΝ]\\_RR"t>@lffV̙3e!;;;++z4[~~~o>qD۶m'Lйs fdd;@P{nSVV^xʼnD"})**:;;۷Ν;sٳgvjh(wﮠ,X#FFFm۶وL__/99y׮]Ǐrrr 7h(zY\\,y,455͛'vvv𩪪N0?yQV\i``uyGz E=E"ђ%KZn-zFk׮?Cvv>^j<000+++000>>W^{ z򥼣;}DRnݻG}$:uԮ]; hڴi3cƌׯ9sd;vMOOw4@?yO>71 {ABBNIIye5x'SRR6o?5Q7~c19˻v/ ֽ{߰/~_~wޟرceМUScz͛Uhj)66600pϞ=m۶6mڧ~j`` f *>@agg2mڴ SSq]xQ޹M>GFFF˗/sΏ?x5GG;iNobb#GZl9n8ss+V;27xCCC__;w;֫W[&&&z{{رO?ws} k֬wˏ9ҥKS&%%;qއJTW_}]z˗/wppPWWwppX|X,&;;O>ݻɓoܸsrrZhQŋ9sWWWWSS_~ɩɵh2̙q+++GGPy4VЈ/^4((o߾5|_|H$6mX,/Vlp%;;_~EOOo̙ݺu;xÆ KNN_kٳg7onѢǴiӧNjeeuݚ 4 bbbΜ9Ӷmۑ#G/;㴴433~˫&^zucƌٿH$Ş?g}֣GA&NXRRr[[[UiiiU{{ѣ/_m۶?۵kɓ֭[KϬ\oʕ6la'hb׮];mڴe˖}3gTUUw4@}h"LMM;wY? D D }vI_555/A+uUVVG+Wyf%%>A>#A222j ^z߼ys~~~]v ,,,w.@#@MDbb;w>x񢚚{'=ӧO55D"ɓ܏X,>}C$÷sNAiҥK```FFƄ -Zdddy4hE#vX|ƍ?gϞ~~~5ÇڵSPÇ%ZZZo;<==/_^W~SN%$$L6mҤIu M}}}׮]vO>WWWW ↤?~|-JH2WUV&L =/+m+fffvʕ'{왖VPP t7j(++`ddt 55:T333~ow@C&Myyy6m ,((9s`1X,7o ӧOQQQ /T1~6m .yt"|pΝRSS ЀPCS`nn>>5}yyyN>}Toh߾}-ڸqcTTTK_ ٶm[@s啔m۶ .XYYyyy;Aއcر 4%YYY B'էO˗/K\|O>CX\㓚W_>zaiH~ ۶mx񢅅Ÿq㒓 7tڵK^zդԩSAXjdeXzjyA&OlddIz3gp;v\nv*..NR(++[~}YYY~j !%%%]fii9nܸ[n;@ehhKM2UG=z?_~}xbttw=$͔v0>077OKK7oހX~iUUU6#G['''ss°'OhkkZ?4k cǎ3fLHHŋƌtR yGȎ.W窫:t:::UT/III~~~ZtiLL͛7utt pnݺIt7ߨQ9po_˗.\YTTdll}*\zAee3f~w?… ={&\wz4#ZMKK[h͛MMMWXKy3iݺ_ZZ%KvTZZ*\wz4Sڵ[|yrrgϞmffTVV&\B5[s̙w(@Qݺu;tЅ Zn/P/{{ .ں;v.D"QrJ[[[H.]QXXXI[u$ML(?"&%_Zh6{]rEzC^|Y$qpp:rȭ[}|| PS 7y` &t _nѸr˖-~~~_EEE7nLAA޽{Ojݻ˗:ѣG"##_ws_ppG|||5l3R{xh_ATttnoؠز?zcYQQ*::ou u2===ɅK,y>>>>>>ׯ_޽[͛3> 6|w?W_}5{l%]PIC3g\`Aէ=ɓk׮>^-&Dtt}bxڵkµ+{nduʕv~Cj2&U|M8M6W~@痖6qݺuۿX,w.@Jo***_~eէ)HMdٿmmm77j~g&&&C >{!Q"--mر:::Kg^x~'_F7ozyyGQyƖ-?t>tvqțIf\䬓ӟ=zg^LF{K3Ree{1R. 71)LMIT=%5zWmI|r[G'O&}A!7srdT_b^֭{駟V/f͚ejj?iҤ\2]]]JDwY ncϞ=OquTŒs'Osu}qNyi#GG:ݷoޕ+_8ilQj$dTVVrrWaĊg;A/^\U}||$ U? MRJ&o׽jɾ}Aغuk{Ւl׮]FFF***N/06lСCA Ԡ[YYYrرcKH*JOOk׺u떪jIhkWɮ nUj).-7NjĪ"ȑCNY upPn_ qA[ }H8 %%%Hqӧ[[[ wry{{[YY}g|+(]QQǕX[[W]ѯA(//so#_>}yܸJ[O`+Yj)J̬;ъsrZ*_*Ç b'AxÇ<7Κuq(AA!,o_92!u*qiiͻ?5 i<{vTj*uժ'0 cG=~>>c 2yEEEZ-CSSӊˌZ[[Wj3vXXo pU5Ԙ6eq\|7uruyKk׮]~]YYέ$KoUgjJjk׮wܩs/uZqyyM,L}}umYqjE*ڽsb}09R~۶Ln' P{gΘUNZ޽ZMׯyW&!EGx˗--SHYBŋF۷OKKXLHH>Nd^An߾-BZR to߾?#''_]$y}}}+ ?~Cu(^xlaa!Brr/䲑R՛j… u "=]UOOEzqDDzhehXZޓɻrRs%oedԦƛ ׆6FϞϟ/s~ӹsk>*k]>cz_wȑZ`jjzK.IζJJJF ~iHHYwbH} Aڵo߾} .8IP|ߺ*A044\nd7 s va̘1Ϡ-q0$:~Wم]^Z:#B_#qY'6k?|NPPaJJwxNAP}*:eg7e;j'O+~ R6o3D=`Ȑ!쒒S$BK*--=tPfcǎ---xsPz_~KAA+557 <88<@f|_MLPHKKJD7n݊رw/N~N ᡪ:sZ]. ~mAܸGxx|ǏsϟYXިQv{۷XZ>QҪLe y{62nvv?OL̻|YAUU,y{{۷-;;;22rԩoˢ'nܸq7o޴j׮]fn۶? i2z "B-Z4eʔM6]o߾M_#/999C}u```tŋ 6lƍZU^gɒ%ֶ|¨ӧO Pz p ڎС?lٹɿu@{)*kw=*LOvt2wnT~JLII%;]٥1X|֭V0z *Iܮk]6d/|rӋK?tպtق B'Nڵѣ/kރҟ9o޼'N$''|7)++=611mw.{o6a„ktJ' P+:u ;wOtRw:[Plro߾suS%8H\vM.KV8<[.[V ML4ll>> .ٳgD-7P# TEg^f͊+d nH$zxxӧZ־c[(Hos}GJJ:tH$ y䉳ѣkۏA^O}ҥ!#%ߟ$h444֬Y3{/eׯw.hRی4ruyNyI̙"'Mʕ+8Z\\lnn<7+ɳfoڴi#DDPZ{;TRRi&55~n o~@>}}}<==N:pW;4z򤭭SZZjkk啓#PЈQȟٳgpss2yFz4)"o߾%%%P;"ƍǏ7oSg㢸ھ. FQz[Pbe `K^D4ǨhD;F#-v )", A̝yޅe?vΜ9sͰ\{Ι i s$&&[YY|5tuu#""ܹ#++;dȐEyFAt'AAAޱclj'@+իLLL;x }=A]]ɓ'UTT6{|iJ,k鹹 , 4hPJJ /_ӧOgyy###S__/ZOEEezruu{Z|@W^^nhhx~хgϞ~ch׉'^J=5Yxp|X3 N2s"4MU]]q۷0 ""bklmm'N!PUU駟ܹlٲwk׭[WZZgϞK1HB~ݸqc񇝝]LL ONN\zUaH}X3gfffzxx̞={̙22'N˓nR|tW~zffϹ\.U񪫫Ǐ_SS#:}нWTT=ɓ'sΕVT҂|to-jhh-oll?v!H}Ѝ?ɓMW~z'G E@wDž X,%%Pxeg E@w|գGΟ?T"]UUUy{{Qzt9izSL!fdd$'''''߼yTVVVYY:333((ѣҎ! T :??@Gp8"}R#x{ϟ?>|h7O!G@j8C-..N999999?fuu5]2qD?[bҡGb.\vԩ|OȘ{zzn޼^jjj/_⢯wr©STTTlY^xd+r83331c9rՇvw TUU BCCCCCtA󌎎 ̐z<+~w)))B'ggg gg~ )))Ypy``Çk^IIŋ8&%'066~eVVڵk;{quuUUUvڜ9sMMM !_~ڰaƏ֭Ǐ߹sGhsΥuttyyyvtt6lX||KJKK8)++ , ^lذAAuEǎ񣴣h>LV73N5@;+|~VVVTTԏ?x3g2|ܹ999t۷oO8۷F1bē'Olv߾}E[|ill1c$= zQ>i|ǎ* 899ы^^^[hhh,ٲe !dѢE+**赇& C !_zիW/uujqggg\"KRWW5kVgE-h&I999,o߾Ur=Bɓy<x&M"ܿheeիW;w[^vMѣGBrp8Gy@2ӧO?{lZZMKnbhh~~~BjKII 7^.^cUWW/Y^]]}Ȑ!?3Q˽z=x` lcc3fTVVJ:-h}cxǏ_pF>}/^\ZZJW8~#Z<c Č5JGG:$$k?|0|6gM:OãbQSS=zhEEE 6mjjZTTİoFYj_+Zj!7Uȑ#999k׮8p /\n@@˗nc ZaƌVp!BWk544ʫΜ9#)**bZZZtСC ! !nXWW믿~sέ_bw\rss]\\={6{ljQFݻ?;;ۻG$JL 7;wy޼yϞ=+QS6(ǰ`Yfݹsg„ JJJQQQεaÆEFFdggKzEAbll,(޾>33#12)*((:1:x`dȐ!,+00P|;|>ܹǏ_f 1# vCrEDB͛'Q;˖-߿\PPлwI%邂dbbrz-͖} ]2{lBԩS*DGJ AYbXWW7l0u=m !Ԥ|>ΝKRTj~yxB%c***tIyyyee辜D=._L7n禍]vB¨Ũ(Bիź:*AܺQQMEA VkqV'>H Z֭[7i$mm V󿢅P%_~prrZb!ϯG!>y!DUVVϩpuuݼyh>BꫯՅ::]vʔ):zɓ>}KFMSSV9sRVV0e+))!244\dgtt`No߾͛7/Y$$$D|;3f8tÇE++;wZ*==,88xѢE'Nhb$MRl"]BeN344_S MG-l%%%B>}ZI@Yvݮ]b``bŊ˗+((0iQNLMT Cv'D1Gmnt[[[jիW_pa-nb``[7o훙o<66VVV6>>~Gi:O} BBY[022:{`3g.\KMs&ݻӧEq"}GJ4&##KͭS~="3 77NQ-Hbq\zQrrr__f)\(LLL޽{ի:x.~wDLv =Yzubb"|Çy<ݧUTT׏Z433̬ӈ2!$**jY >3fL[(...--|0M? !U!7aHbֶ ƥwޥKnݺE$I۷o%)))LǏ_[իׂ 'N0}իW`aa䩖 99Q>1oxh#J& §󩫑 :4$$$''モ<|:矿qDTk2!G冇s\zƴ,KKSN =|P~_҅߿_rHɭAAAѧNrww3gyŘ6mݻ\~]OOOgΜyQFQO>|-xzz :޽{7oTRRZh]RcEEۏbرs΍>{uvvիW̙3tPBȌ3~TݼyS? F뫨8|:=RPPh$$@SXXHgwb33K%hĉ'N0`YiҤÇ=677ŋ|M_1 IDAT~~~AAAw̷9-^'M5F|P,?wwwߴiS]]]s5G }yloohѢ]r劗abbbwMLL\TTN'@b<EL .BBCCU>|Xxq޽o=B%Ϸ066={6`?]f… ph10www###UUѣGoٲ%++KxGb |>ȑ#G600PWWwppXr`UUU!!!eeeͽQ0 OdȐ͛7 }e/BYYt陙tT׼o!xupKSLꑂ@ yt#9qĉ,X ގgEݻwvJHH;w)/>eu߄/h}o̙낂uCCC\\!ݽ#iq%**ݽCs7ooCx}폚nǎ[lQUU]7NAAɉb%$$TTT3fĉCho߾{nRRR@w:nݺuu~̙___okk|5kt~m3] <u. У^zҎzQH9999b9;;z'іv:Mee֭[H,p B,… @|p177ܼys}}D-,_E___KKtSNXYYYfxBKKKUU>}lٲEp-ߴiZwENK`\cff3f̘#Gx Zm^^^xx/ߺZSJ;ݻf--^*awImmݻUUUV9::80,,L*t>ϧbccL"Xb_|ڿx,%%%fff666׮]{^zz) 55aÆݺu+--MCCΝ;?fϞ2fssWΝ~'''GGsGGGϙ3]O8]DQa̝;.Yt)://&>>~ǎ_5 +**TTT [Bׯ544WW׏?::::99+++ߺuaR^Mҽ:{IꜜRRRZQ+cXv{цriucr >~hggqaDEE}ٳgn"''&X@pk"޷o_UUƍ---Byyy!k֬~ 8yd~rZ"++G-z{{WUU3laժU۷o_`… ǎgϞSN?a o޼!p8!TVV2?hիW+((]j ^z،3!IIInСC//&****++khh%<͍ 7IIIQVVvqqKƎKINNnum'RUUv["6-8СC !̣0aBFF˗/?~z->>IIIÇ~Ӓt/__TTD>}:BbBW8Q|ILLL\Beٲo޼KfϞM:u*O8amm=~xee1c899%''3߼:tҥK]]]544֭[kѳ uz|8} |>?>>aD+VPuuuÆ cXc$),,5Yddds@t>>C.AMNp۶mիW3⢨HM ^ D}rooDQs%|/))_s̙!CUK=r,XqFB5s_E=т6iҤ]]ݏ?JD(Bȏ?Hpsssssxڽ{u!ׯOLL7o^ddm\\'deexbTٳE7'99...nС[nuppHIISRR°YhQzz;njbbҺ ݈}}%Sيׯ_~:]_KKBEEƆzmccKIw!##3rH۾Ĝ(BǏeddƍA{WPP/WWWB-⃤u%$fbpɒ%֞Bi}m޼yɒ%!!!t ueLw300x-!$***&144lEO3f:tÇԢ՝;wVZ^VVhѢ'N0zP : 2ۈz%KΜ9W_1o$lootVWFx<XXXXWWaÆ6BFFޅ%5Dӧv#i 1'RXXf:heeel6[QQ.yBy7ARX,V?R[  Ĩ]fffnٳgΜYp/5- *'G*?E?޽{rrj+..~VmN(QE3 "8V011lsΙ ?iҤ%K\x>'rS9mРAiii/_D7o$XXXP...,0 .0!tE=ֶ 111#͛7޽ԤJ?~Lill>M˖-*ٵk)SgXg=sիW466.--mnmMMӧO!O>qpptJWW\.J;޸qCrrrΟ??c 1$}1˗wޥ`n"699H̯TOu@}?7nu^i ߓ\nxx8˥qYYY>>>NRRRma֬YxCYCCCBB!dтlB㨬եffB $C>wſ!/ N'OŹ[gTr8@VfX=@v ܹbjB@5syeeePM|zV>QΝ#x{uvvիW̙3tPǏ4hP``ȑ#alliӦw>y$--(88X]t3g޾}{ԨQ&Ly&= 977ݻo߾cǎUQQwׅ~ =ztLLUU 5 H >uꔻ{\\\mm9s/i "dee_jΣGNz8 Ry<tq7mTWW\M'''۷oϛ7o߾-*))څ/).?899{ʕ555x<ƍ 9jԨ8z tu///ccc%%%]]aÆ(**788N>}:esssee޽{o޼+BȌ3:?B1뾩+5ļB$hɑ#GFm``rʬ,vx<ޅ |}} CCC믿===?.yh\;\.w͚5FFF . dr|~||1c>3 ''} U(..n޼ߖc̙Mrwwo.Gp= }E*񔔔-ZtjUo߾{ʕ+l I>.*--M!@wnݺu։<ű>tċ+;::v\!ЅK;0@ρ|@ρ|@ρ|d˖-cX... Ҏ3 -`X,KFFsPSS|r}}}---??N:`eefg͚ vRRR---UUUeԩSMMMY,sZ]|>ӦMrssk@wneeJ;|xll)S:X,cc/_fee?>>ʊa %%%fff666׮]{^zz) 55aÆݺu+--MCCΝ;?fϞ2fssW )##ӧOL''v= 0w;999::;w.???::zΜ9z2^M vҥK &H;.O"cǎ BDeee///-444TUU P]-ZD-_^{aB][uuuKK\^3gμyF4`h_p}B7fffuuuhN{7 8?/..nnnZZZyyy =q8#X __ӧ={6--&rrrjjj%|j144TGG^秭-Ao߾}UUU7n Ғ`ջwl2bCCC555GGŋ^5ܸrҥښ lcc3fTVVÓ\Ζ4v5 YbBtttBBB***N<)@w|O>}>#'$$q8I'$^͘1:Djr]_s8܈{ ~muugpppCCCDDciih˗/L<955*lll5j޽{MMMMdee}}}߿s'&&ݻ9]B;,>&| O>ݯ_?PSSٳޏ=blMN@cbbBi bTVV}ի= D޽[\\D~g{YlU{n:m~VXZZfee 6o޼vڈ 6pww{.%mӦMT#GBBBKihhpssܐDEE%&&\*IKK+,, ep JKK widdD;ǩɥ@&8G("2^UU!Cv={F6YQ]].TWW vmbb"//$`&]2WXXH4iE5r5dǎРBWXj!$33Duj|.!& }Х`>hԜh| 'hee_z^^^ާO&g|ӷmF=r߽{7uԍ7.\oihhj2l֭['Ofي,VQQ!TMFFfȑ*(( 2.g*''뛒+$11ֶo߾ #d BW~?L4ݝ5̙3Еa}E[(--eJJJ5p8?s…)8eÆ sey۷:jw{A$F S533ku FFFgϞ,hYaaahhhaaGRSSEUTTP!'OGakk[]]}銊 ]]ݰ0GeeIFnҽꜜcccMMM\\\TK/8pG.X'''K.͙3N11mڴݻwٳ ~h5777mmm۶ 󶗮~ׯ_pp]]]Ϟ=ŋhv<]`<DQ,?wwwߴiS]]]s5Do߾=o޼}jkk+++/ZDh⿤\?䤦ֻw+WԈ)@r\sYTWWϟ?xxB̙C#x<ƍ 9jԨ&t[rtڤI7\]ִܫe˖S%HJqhhw}'醆M~(h kC]u|ٳgD˗.]!As;ݡ8s挎ΥK~׋V)++ 0:!{29h*ECCuB_~qm3B^e(Hdbb~z1>eׯoC!($$$)))33q@'iĉ-R “NDs;s$VWUUU͟?բΜ9SQQΏ cfff׆LX[[7A?C!E }n1 2֭/]Le%ccU6bI$HiӦo߾C.Ax$F F̑X]%߷zjk׊BvA}6\2tP]]]&TVV.]RSSÃ^{qvpzekk{A􆩩MCxB.^3gT4VUݛpmȐ}~Q#L޾}a#1kc9ïnxr9:x{ 5.XZV0 :'Gfz:eII|R$H>Wuᖇ΍{n˖-#F044TSSstt\x,XfϘ1r jpٲe,y?e =zՍ@ `2phq^W [$~0bĒ0q3gΈEaa!:z>>>|HLL,~tttQQQxӧgϞݧOGGGB Ν+9:tBDOyuF߾z#GʩU<|ƍ zzB&/Oohnuu_Q|.δivvVv78ބ%+Qx/MMS痝?jmje$H/|y7m/;l*MWtX]II!䂥C 1 2_rwR61rtTխ+:vpxSݡAyzzjhh\z5"""))ƍB4665*--mȑ ~@JWVV7**^|~bb}޽SƐ}}}}ffȑ#bRRRڲ <+L8qڴi+Wܾ}:<ڱcDŽJ:AMMɓEBS5Ŭ&;;vB„{{{S%EEEM9995VvO?Q;w$,]TZJufϞM),,Hs?<{&X<""" ,,.X[gڒEqBBEkhdaKTܼdaE$ ;JPu~c$YX$YX^SPkl|udʄ \\޻'X97B!7&k`C/ו[WVֿ}ۊݵEiiiVV`B֭['X(#@uV .FfaضmJ  GOFbcc !n^MRB]N8p@GmeeW_{vˮ:::yyyjbɒ%lhB%LNϏ;vKtt4|*ʇ/^ܻwo55رb-JJJ"ݻWL3g*(( #p8%ի˗/K;O'bܸq+VHHHo+444SFFFb WQ^^^[nғ'O[EAWWAWW7g۶O76IQ#XqB!/E\/7oj;;kkټyTIT\ʯ]chPt؋̂ څ|F3 RF^%//Lo%/ҥ ̜9sڵ>GeڴiԢ7 GrJ$!!Ȼf```N6BȂ ݫ>y7ndddH4Q(=hn_Otܸq***iiis faRuuu>twwrʊ+233l1H& F중Xɿ}۲۷oKuB ˥MLLTVVoJL>333z"Ztttlmmŷ ޛ;w͟cĈ66T$ u,,.MD>tEnm-]^r6ҲZ1bnnM[WSPxÆݛ?Ukc6d]I<=],x:=i$KKK#F055UPP#]r-7ܹsY,Vqq1f|2ߛ7o!wB>44nܸS%v""c-AH0xSs|ŋ !Ç3 @PQ+VPuuuÆ cX͵$CL#vH,}R~^GppҥKƧBF?tYYYKKׯ_ ӧ~үeee !<խ5bۢk__(f`A DFiSsNXySI9in khPϟ??|'Nhhh|Wjkkj6(--eق?G?,ՙԳkH>z]|~ nm?3!dժUTɴiddd$݅(=("va|m'f \ꫯ!LA2d0bĒxBz*khhh}K}H#4ddd2r~ 5v,UR]Dk BWWG>*]v3fPy%T?{5xpm!Ecر))y,[ Ldx{x{WV;g[(ߙ[?~|„ Tݻw7l ZAnnnmm-{PѣGSCz.\(ϟBlD2.LLL}%Sيׯ_~:]_KKJgNEEQVV~򥤻inhs!?!??_AA/K\]] !̃dɓ'3l6i4)-[&Tk.CC)S8;;K%ӧvvvO4Ș%'''کJ"EEEQ"5yytEZP13-.~T{denj)NLlxyssui%>RxȀ}4oZ}c2}֠A^^gvrӧtrIZ`/^}6B| rrr&LyMBBuyQ_󶶶 g3#.8,,,dzzz26(0>J e"3Kyw ܹ2sBx<ÇIve0++Nd,sT CFhW]II?W䔜:%Q F'BJIII \nATTg̟ќwbBmtuРٳۥ) +] /#B@?Vxxx?dggS&bjll<~h5ظm۶,)v# };"^z56669;s(=(=a|,ҋŒ`bbۊt Z bccSPP 9#|HCҋԳ2A"ƛ6m{'OҌ0D.,,\tѣcbb<<<|}}ϟ/愐ABI-!߿̙4_^G׽~]y떑_$C ^:T5ϟ&-M^GGСQQDMeG)((P\@gyXGA@AAQh Qq] ( QD gxh]5Q+QV[V٣]yڶm$oS;wnxx͛7Ya1k6 _Hqmtt:/Y½|v4k'QmJ+P'G#OYCXCXR'"O$~ƍ?p%q۶m۶m ={y># 4hPXXXBBBJJoǎo߾}ܹ)S|G5߽{ &?[C,=]4'^r_~#FxbǀOiӦ.] 4HWWڵkϟr$ciR!!!6lHHH8zʦL43Z}8qe||(~UUkN (-fgNK%Z/ZZZ8P%?}ν ȹGw5a]u;2IVo˖-{㏯^m۶'O̚5KC=}t޽bccϝ;'U,=]4 XXXddd5J7nLJJرcrr˷l"QGLAӤ>SSSÇwoagk)]l&V'mm5k֬XBOOȜ<0)?#bMeHZ\ڍiӻtRppjd?!o;BAvkI+Wdee2Վ$@Kgرcɤ20 3p@Weҫ eM!55˗/[#K&iUG@Q3=F==NFeM6w2b'&&3g[+a<==IpǏ{RO m?}:IrʤWTT8;;9r 7Ȇo-QYYC``/ɓu>gΜ{usFPP/{$1<1<\Q@?U4S2L,cѢE-.ѣGwb+%F(BRRG >KnnCF@i勊bۻRձ_%;tP\\\Ҿ U~A)'''$$롡k֬駟T{>P CC#Gnܸq'OBЄ (OKK zzznnn+VPIxx%HQ!.]ڣGcc8p@@|(ٳ޽{رmmll+**TWJJJ_>>>?GddǛ>㓓%6v'Yyxi6--MGGۛ+4h… SSS$*3 陖֐>ŻwF5dyyym۶ݸqcTT[~ Si2dfMP gsn/10@Ӏ.EZegޟQO [3X;C;c`^|]^7]7vZZZ^://oڵ666l@ Pɓkֺ5??˗B/|ͧOq*KeMe{(h#"oS%y>"'jځ[m]1puCD^Cŋ}Mjcs6sN|.܀dTWS/*uEW٤?/(?PSSSSSSÖ>}zđ@suJ!"kѤI())%ZYYyA"߿JW(("W濏}G$jZ[g?..nq҈m[r-b HDDϟ?733>|teaج.""OOϠ4{{޽;88Xա) 0#wحwvٿ}ʚnS&MM$~tsj`|ɽz9sf=ϕ+Wjjj6q$`>/Ǿ}t钜|vڍ;v è:.2|K2>zGcYB> |7o?<_htnSDWynx1վdhc ڲe?k.ww˗JWc۱c&Tae%&&3F){A9x`@@ىQbW|^ŋTu Dh.]mhU4)z z z z (Tu,A@:סC/B{>0 è9::2dٲerٳg{{{[ZZرp::99M4?n'---((H 鹹XBF^^tyI"Ǝkoo0\+'''$$롡k֬駟T/HĽHLL3fx 0=ξ|rnݒ:t^zuIOO_277ȰgݻonhhxU.M=wd-4b]]].]ѳg $ۈ͛7===Ҕ8J7W+V^^ޫWgϞ]|m۶C zjVV&(([$~"^:tHGGgС[,--/ao(d_s[wMD\III@ +**m \~葑H$ھ}#G -b$E"Qrr/7W+6}t}}ׯs%%%%* Z@9rܸq)))|xHСCk |Y[[;v> RRR v- @nǏ'SN)®]hСn144JN<aÆ={<|PF-4#Fdff>zݻwzzzHʆ ¾}J;88|* +Z;;;"ϟ?+))9wܭ[BBBlǏp%yyym۶ݸqcTT[~z:[WLL[9G]\\ܹ͛^z]VFF$8ܬH,Ull,s^-[VwӦM300 \a~~P(/Nzرe˖M>o߾NNN|Z[VZܥKӧOo߾}۶mkJo3zYYYɻ4*1 CD"9'ءCHɓ"77Z׭XjYVuu˗/ǎd{{>r<[prp%'Okf>*((HNNV`Gh< Hlz̙֭+**JHHغe˖e˖͜93<<\Ғg>>>Dt=-4333B --vi# -Alcc"^1rȵkJԷ]իW<[Rb+))Sَ$\vMDTǏp%AAA^^^{US{gΜiwE"j߾=|xϞ=~~~ҏ577%33길^z%ѣ-L4*++`>Pŋ---h ".] >"U4ŋyFՁ0^^^0L#50ӧu]Ƶߠ 5F>IMMqȐ!˖-oΞ=8 `ǎEEE\GwAWWiҤIt;iiiAAA@OOmŊhbeeeׯ0ab=gll(΅<<>>?Gddwvp޽(|YyyJi}v~ԩ7o*_|zj770uu[n)7dgg#ڿɓwtoVF u,kk똘X3gÇ)B 5M:5::M6Ǐ +4HP ".a„ *Xuh"1( "OOOCtttʿRHdd$2&&ۺ{n"JJJJ ANNWXQQTSSҫW?ڂcʕNNN?GD"͏9¨CiiIhDWW_J޼ySWh]o>]v9995F/ ߽{gii٣GFt5ׯ_'aÆԈDb33r47@ED-2e Ynݻǿ'OhjjL6mΜ9 XXX<|B__o={$"CCC.ۧebb8o޼}]rrJ>uFFF 55m۶5q ^`````x }PaaaDte|!֭eee&&&\ɪUhϞ=Q:u6mTWAy{{'$$p^~駟vY__Gk֬arqssN:m߾]BuuÇg̘ѡCWWȂnz$(dYRR|?ʊKFgΜپ}{CCC___;v>|===SSS=zcرcIM&qJ9c8|0Xɒ0qDMM͢"Dud$y͸q㊋,,,u&Y(;;8%kwܱd襁)M+bcc󲃗oa9sH_с7p+W^k.::}r]I06~x"bZٵk :֭ZZZ:::\ɓ'544srr6lذg6p*##\]]k:cƌ;wM0͟Qyy߮]sWDŽ|rС999ݛzݻwzzz³Waƌ&Mz#7oUVVUP⹨+v/[[ۺqյB)モwGV0UUU۴i}PP۷ ҦM#G^~? E"ѡC\]];w'ȱcΛ77nܘ3g΄ M.]z9;Y|(<OߴD7HQ ͕ 4RSSh9 ȑ#Ǎsuuu}}}`"e_ BSSSnk@@IZZWe˖%KPCCCkjj.\(ۉh삭Gi`/?\_i$u]YoԩrS\\, u릮"}+33BBB{{uqnݪp҄2u'O&}2UVqߦ{|WWל}Vnz,P엗O|޴ӦMSSSkZFTa Hjժ(HODƍ u} ȹshɒ% tR"Z~}]lllr_~ݻsss_III5SYYɕ\rE|^R"1[jt.ۆ LLLwECׯ/ fC433*̛7n޼)єIII9qċ/VZծ];={Hϫj`JbrM{ma""" ^_5zzzk333"***z- 0#>y^mǎG]Wز5r@]I}5ɵɓ'qqq:u277u#tuuhƌ\ dID.s:t蠦ٳZkNSS+t3+!"3Z|W_}uwWC>N> >A^tiԨQ@SS*4I D\O$=}T|lC$>ZXXpur.xC])zOX/^ !4+.#ħ4{Z[[Ϝ9sݺuEEE [lٲlٲ3g[ZZ/WCD⟐xv(//ܼ֭O>urrbҥ 5kkk6mQMM Wpׯʕ++**p@EA8qO>?iӸWc(DTW yyyϞ=(\(<dĞAvvޭ6tW\>}:7Ta kJa?"Ը~zNNN`` üzѣ=<<Բw󟕕j<|;w۷o7nXkGj +++ͳy'O@%?u}KD].**rssu茌UV~رc,YboogUVVn޼f͚X;;;JvIFfRPPX A]@NsppPؤ&''GDD9]Iennx짖W^Յ 111 oЩSLH$)**5biiS^^ΥnݺEr~UWW zYRRR\\܂ BQkѢE555?#ؒ," } aggǦVW vvv/_| ΨECơ'K8N:ɵי3gڵkNDiii۷owww >)++R~o:t(""D {?ڲeKtt#JKKk55%Ko 盶_~~͛'~evZUU"ǏM_ر!Cz)U\WCCC]]]'Ϋui} ݻwnDž666\IjjjPP޽{$o?޽{zz3g>CŋDԾ}{]Vk#W9555??_RO8>XGEKZXX̘1㣏>֭[bbuuuuۣx}' IDAT{-!6ggs 0ʍw.d9;;ݻwO{{{55{ϝ;K.Γ&Ma+WKd Y#Fعs/<رcnݺBYDȑ={|]v۶mk>%>l{ePdSSSSSS>nW^MfxEƍ$˗/3 # |mq\Q>|xϞ=~~~\Ncnn'Kff&wU]]W]]}=GsaIIIJ^J&D䈄ٳgQBB"رccǎ%[˚'%%1 'ĉS粲Huvvvyy9yE۷ۧ@EDqqq\ӧO%YMp.dA|ߠA(..jaaVUU]v믿v-o6!UUU?JW ZjUvv\7IneuK+Z% ;;;'Msb _ϟǛБ0+W"NDgΜJ$GI!"kk먨(ħoowޥr%NDĿ`XFD- aڵݻwkYBP]]}&Mb033Vf/ExWPPL2֖gES:umܸ֭eaaaf㏹?eee}VVV&L`SSL]z%J??|W\cĉDԷoߵk 2 %ƍ7eʔzK%dFDC ߽Bv / KQ'555 V%%%NNNDdaaceeզMݻyJ0Ĩ~͙3{_QQabb}pZo۶mxx8ݶmӋ~5j 盖b ":y X^D駟 a$Fɿk@:']̬ևx\z5??{YUU5|{Pۭ[LMMtŋZc`oWb+TTTݻgMD4l0s{qqYyyysy@n!aڵktZ< }ʕSvDGG522@ /^驯߹s?}+.RMMQ&%% 8m۶[l6~O?ܹ7|4Y~^z^WX-ǏGmaa!=<8 AԜ=zlݺBdۋ~5j 盖 }}}al|_MM͒%Kwnddԯ_DDOh!WӨx|@S'7n:`j|^>F M=}aݼyS-+ed7DDǏWeE\Ly!a@:߇u(ĉ{U7oڵQÇ+++8@D4]\KKKOYvHb5kmۦj#yEKKˆ3>!]EGGGGG70F0̗_~_ʮ.ChUB@ih=h=h=&0Lv+W3 p_/622zrcf%##aӧO:eBZadٴiӂ B_~„ zzz]3 8dȐe˖UTT۷ogϞmiiillcǎ"ѣG;t蠫4iҤ?S @b ]4\-xxx^Z9I߿ۍErr/x☘ZعsӧO%mmmŋ }ƍG)Sr;v 777߹sg׮]%׿?{xxDFF?~JǁO SNݼySh(>׳UE\0a„^S jgΜ=z˗rP( mmm˗Cwɓ322M¿K:|[sDk.":th[ttt 'OjhhlذaϞ=lL.t \]]+**dLŔ!fFFFs~ˌzJ|_r/_)>b`ZXX8;;رC9rqRRRy/^LDKPhjjm 011IKKJlRZZd@jhhH5|MMͅ =VM]@ːf͚9ssn۶>뛕^t)Ņ]Jĉv/_|DnݺٳgڵÃebccj]O>޽KD'bggGD.󸸸sݺu+$$͗IzqHHW׶mۍ7FEE%ׯ#WJlAF s}]BCCݻCq3fشiѣ3226o|…Lmmm".Kڲ&uM]OOرc'Ovssc?c+` Nb*υ^vXzOmԩ<[` ¨nݺ|֚D•۳޺u^o߾رѣ [ ԩsNttt=z$QGMM!wH988wQ.]H]MΝ;)jսt`:t ͛7+Ѕ4qт8'''"sN]$''ut𞨬 妗ذ?|D4o<O>QSS;p@7nY eeeE M숍]n{lٲZZ6m|^vҥYYYwIOO ;wm$Ƚ[w9x#GjiiM>]`?RdggBa^^ޭ[._ܵk׸8<#G|W}uvv~ͱcNJV^VtШ(___MMpj'N$}]vƍ⛾;" YV>*EBBB<==k׮wK(0 ""bҤI fff=b+s„K "2002e-y{{?j /AZ.%>>nܸ@QRR?~\Ձƍek HCX~H޻wo vgggKԑq=z666o߾m|_?V|W:Lj711q̘1O}DDTVV߫:2gΜ{竫׋{[XX/@|@|@|@| <|奕L,2 cFFFo޼uè,&^lh` l`ԩ@WW7&&FRM0a///BHt=z0 OY6elZcm菣}*l&LSIj}} lg 'NQFh+%%֜ԩSn9~Ǐ~zʔ)M$K61e/ 잲;w>}4<< ]S$)ϻi yf׮]nܹsjjjJ @UHĽHLL3fxI)--uss] ،7XW4h ###oosڴi˖-kx'jjjZ @e}obh@?1b>㓓%6x{{s% Zpajjj@@@ӆPR}؆o-.J:0샳sOνYxsۈmM W|Ͽׇn7 "ruu" (!!!??_ޖE0=)`E"Qvvsn޼)yx/{C 1441q/Y۲z"skۣYu$W7kz-TH޽KD@z 'N(9zÇHӜ,~;w\aav߾}cggsw@ݹsgdٲe .ܰaCllxL__߬, "Z?~޽6m"H/lڴO>BuT렬 6{b>wBQ}Bɓ'gΜukaaah&&&DTPPФQ6}7Qn l9y?/W"WYS&F޾ܹfДرc2?VVVfff PO<9{lZZZyyĉ{! %'N\pol3Ȯ^'/544Fy}׮]KDӦM*ѣQx*ݹsGϞ=KDʌ @TXfʕ+jԨQ>433kQ-la^zÒ55GuGGGi+wa>]ߘaz>i7|sڵ ]OZwhjjٓ+→L㍧":r\a899W\K...?qjc}9hРޝWU|?7&@ B "`)ZD*"U "h(>ŕŖ!I;}DkGE2vBVZ!uRْw4(\bȹ9|ww& }DF}_3Y7~w}w}y>_]tI6jA[^Y4۠iwscAljˑ;vIwL?X,}x՜t2nܸ+rk׮}bXF*++\sٶmSO=uLf͚z;tЊ+̹[V\YZZ:f̘:\L\Z oݺ)3@> 4hڴi-6lؐ!CVZX~clҗK;e[;o) $> nz4xEeeeͺͣy~M~iiiÆ 6l؎;ߟ^ӦM+++ξw~ʔ)'>=o1xu~_#ݻwƌzjϞ=kQF5|#Fѣcǎُx≽{~_dN:/@;%]w]׮]^{zv:bĈwީ{}e˖AРCLD>KJJF٠>Vz={}Ѫ !Rڵ馛{ )//kRSLf6z3<o>}z=x#F3XiA͛_y; 3{N8{|J8Z_'ϟ?~)M6!)L2e˗/?p@o9s4b}s9Mrq^^СC?TUUeee} \>ٳgϞ=;-U>}v h@t :}@Kr7bCeؾ}ԩS kM6{gIGر>۾}z/ÎEr˛or=zA_SN?~NID${+W4hP`NVZo}k˖-!fdv8餓 ,**j8Ԝѡ@t :}>}DCѡ@t :}>}DCѡ@t8999XLD=yyy{b999a a%Yt駟~j3̜93%%_Pcv03x<v-6nX޽iHVuqqqϞ= Bp8}MW^@q }# ;qrrrNFNٜ%C۷o\xqe8X+&LS-hӜe wѣǵ^_YrЬ)-]4777 В8K?iZP &Mt|&/vuĈaZ#}4k&5I@)MlӦMaGZ/@t :}>}DCѡH=|K.i-BUUUHW2~$/ ;|:tа_@t :}>}DCѡ@t :}>}DCѡ@t :}>}DCѡ@t :}>}DCѡ@t :R}Gx޽{S١Cf-I"\tEk׮wO8ᄝ;wv֭Az^ Yb|+%% /TQd7o,Rd1zϬƌIkn0zWx≡DE$+} 'MJhq}@i׮رc_zaEE$'}DCѡ sYg͛7oÆ &ax<`;,X 7{~C IOOӧɓ_[?۵kzݻQB ճg۷dСK,I|;3M:5y7pC 7o>{ݻϘ1cO??s^zoO1" 3g,++H<6,aZ^zվ޾}{~~\wuk=ƍ[lY,W_}EfΜ9hР 'NxןuY{ܹs퇯1cTTT^uUs=7lذX,^Ђ8h}{Ν /YjX,6k֬ } O>[o gϞ\pA^x{6m1ž˜Gغu;3|oܸ1==sΩJOOߴiScɓN</((XbŜ9sjC>,**㯿5k<9s$oUUI'sRRRtRUUUowܹK.G^;XxW_`DHΝ;k_o~C Ipߪ~5Ȩ=;ӫW#//oܸ1{Czh\ $*;;;7xhΜ9YYYKpQtϜ/sM<9>֬Y3gάx٥[n}Q={t޽uffݻH>}~~>uM4 xܭ[n>֭[fff|y/}Kmڴ))) `ڴi~iH4һ:YYY|IEEEHEE'|U9vx# DCѡ@t :}>}DhV999X,Y> bS@ vuYt駟~v ,}Ь233ÎQz^ !K,袋:wܯ_ӧuop )))k׮ٵkW.]|ڑ+Vix7~4ਜw~O7n\EEEII_~vAp SNءC nᆽ{>mڴ]aAܹoAP\\ܳgς;6X<;rss (++Kd5k>z貲O<1 |{^; /:ujIIɢE [_NNƍ?b6lh5 &hWH*`֬Ye_'NLIIYlYݜs=wُtP0kw}w^^xҰB+ڵk0g߾}&MԩSEEyw뭷[}52(o`…UUUu?6Νe˖~[n/'O>޽֭[$ɡr>(.䒂UV9_~۶m[v)S= V^p¼*}s=7tӏ.RTTTf݋ZÇ_dɨQƎ۶m3f41Bd\iA%SO=UZZe˖_}5jԩSO;={q[lܹsx<>jԨ5k֔geeAymPMڵ{޺u{M6eee&LH6>hq8}I :}>}DCѡ@t :}a±cǎSl/R!}R/N4X<;4ܿCѡ@t?` 4IENDB`gcc-python-plugin-0.17/docs/sample-gimple-ssa-cfg.png000066400000000000000000003127711342215241600225360ustar00rootroot00000000000000PNG  IHDRMCBbKGD IDATxy\e (" [lB) &jmX-VJf5J)Ko2p%AEA;ap9~܏k3 oQ(J2h5y"E@.]mճgOmܹٳ]-#Oࢢ<<<5jBj'O֯__U>Doڮf "O y"E@.Dr'<M 5ڵ5kVttFrFP*aaa&&&aaaJROjjI\]].\P-Z044yJ>Сiݷo.sG(y" 3gӚ5k<==#"">nܸ>HP;VT~GzSNo~Æ VVV&Lhժծ]vQh>۷˗lңG5;gϮUֈ#nܸ1dȐu?( : -Z4rH9;w~X~w),,TH D*&q'*nnnqqql777iGTFDDcǎAAAW^5k===?~6Ç !%'PM6 !^{59nj#XxRB(%Kڅ?}j۷o;v¬.]-QF !,X Րzjsss9}̘1¢du!!!R T*ϟ?$$$4o'88Xcy^x¢SN3glժ4݈ ػw̙3ϟ/,JgϞWknĉS F###<y"W<%'Dr'<\"O y"E@.Dr'<gBPxxxh xE@.D4EDDfffAAAsε7n\9 r劷wvv]Ƒ#G.\p͞[nU*#G|' P*ڮgHPl۶_N#Gt֭O>uBO:u|j@!DXXXppbٲeӦM2eʲe4]VUzzzzڽ{"%%Zھ}[n_ڶm{mL###rtW_}%1c& ! }vÇv*~zeUPP(BU(9rݻwgi5>>CV*Z =zQUkԻJ˭ZBWX:::RYҰal޼B͛7 ! : O_IIIyyys-jVZBe˖/uE߾}:ulݺy۷!c3(,,TԏUvr9ѣGw>yzPݻۓ[|o޼y˖- BF.D5uT!DxxxZZΝ;ǏPVVVyyyU?**j˖-]t8t'={7n޽6mZ޽5jqխ[޽{/[lժUU(fȑEEE#F,>o[nk׮?;0eʔcVvGB &Tv///[[t__ y":tв|7KokyWv!n߾}AիW|0j믿.**=z q"g}v}GvfϞ]n~]VGD9OWr'<\"O y"JR5 )֭[[ZZj?ܹsdd e.gO%T޽{׮]k߾ )OΝ=<<]D,22od5O y"E@.Dr'<\"O y"E@.Dr'<\"O y"E@.Dr'<\"O y"E@ m۶?ՒWi^|T#~Yv限PCBB¼y qƒ-ZԹsgssg^7y"ʕ+OgϞW^yÇ%4o|ɒ%eJnݺ%-{.\(**zv5xِ'P]|666vrss+a9ׯ_6lldd԰agT-y"͛7 œ}ݧUh OZ0a£GRSS'L0a„˗/|G#Fҥ˹s*d nݻIΟ?_TToM6!%%K.vvvW(R%KV@ET*]/ȷ~[T* /JM6-**JLL,((6iT_ԬYK.}'ƍ믿_?{wuu-]m۶׮]S*!t](|#U+W<Լyttt//F]refff=zB辰 <j+(((++kٲe+,,PR.caaq%F33R-O:vX˖-CBBԩr@D1c(~ !2eX;J %A@ w̔Ǐ]6++K{֭G OcW^IIIQ :;;[CiƼ<-Z!>+W|7Ag)!DPmKڱcѣ733[ѣG?вe'N\|C_~elllVVVz,YZm֬/bllܵk%KܹSѣ_}ҥwBddd۷Oeee+!ϟ?sn\\\Ν5j⢫3!ILhWddoj;<\"O y"E@.Dr'<\"O y"E@.Dr'<\.Njj TϟB,X@bjj" RxXZZ>x@WT* ?nܸ5kh@(x^V竖J#]#Ђ'NtرMIIIZDƦd T[hBh:tVJ9 v?m۶M4rV9?hӦMӦM[UA֌5JOOOZXX8|p-b3Zsڵ&MH+m۞9sFE@y?qttl׮VZ#GvEPDiȑRX\\_U$[[[mWx~~@>D5y"E@.Dr'<\@ PӮ]YfEGGkt(gRabbT*5N4! … eޢE CCè(9o߾!C4iҤvNNNF<lllBBB̙fOOψ7>R(cǎU*};CԩS۷߰aՄ Zjk׮;v:Ç{-[CNoց4hѺuW?#166DWQQ֭RA3|gfswwW*Jŋnnn2wNvR;h(((3g΍7fΜ)fϞh";;'<; eƌSL;|],--߿x=+++i>55A"##?-Z,_ѣU(^Ο?AAAU< Ok.9---ӋU-Ҫ}FFFfffYFʖ}I&,]<x!OUܲT-qqqnnnҪR3Zǎ^:k֬J|I-[h̶ /M6 !^{59nj#XxRB(%Kڅ?}j۷o;v¬.]-xoooGG} h wjP!ףRRROꐘ8zh8pW_}uԨQ6lHJJrss lӦMWWw̓ ԩSn\\\>N:,^zιcǎ=s持A;33sРA3ՍB/ ՇBضm A e{{>}̘1¢du!!!R`T*ϟ?$$$4o'88Xcy^x¢SN3glժ4݈ ػw̙3ϟ_SPM&dd DGx~"E@.Dr'<\"O y"JP( .l׮B(XͫW^NN3;haGGmWQ-)Çk@ME{Ѵi<==6lhjjaÆt>ׯ_۷y7nhB(ϪUMMMgϞRCnnKn=;hxs4eQQ,*z^OLڮ]ŋk@Mef̘  !Ο?cWWÇN>?>qD֭]vl۶033_ʺ91""Ν;AAAϴ,񆾅ŵoedd;eunEϳ,(((((?|iO_v 48rի?#,,ݻ .:,]4''gɒ%111K,^lv+"{{f͚=CDGG;;;&*ʯe΃Rvlpa+^]…6o.s6lXݺu,YB5yNWWN:-C Bf4߻wO+|nE>,#GtۻmOff)Sի׻wo={(ԔuD???  6oUS8!.^8rȪ}X*nȐ~m*歷.fgktܿqvrMӣF=~0{v=yaUS#G<Ҷmorddj.)zmJڶUsСÎ=CWAVVւ tbeeUNWW{ʿ=z4qD'''#Fdff:w~Νz1554:ԩSg֭d/6;CƍVgϞݻw{Rˮ]ڪʒU: IDATۤI[vxoold%͚5evRկ_?cccOO=e˖BcΝkcc3nܸ#BhѢRsꕅ MZjеn:ѷ6m8vCd~g|tzn݊rrԨ,*cذs{xm~lJ=;ifCk;=8w.~L{vtnjLVouy&|,,p+>>MIqT!Dmy=S0s~[bšC;fiiѹ+7ވڵ뫯z<ĉWZeggקOڵkƎ7.ķӢE?k׮P(ݣ/%!Ķm۞A322BBBNڶm[]]ݱcfeeIrssg͚ebbұc)Sxyy̙3'//9 /VK!ŋ5???_jINN.9{?;!¤եK !LR{ՖgB)K^zz+-V8ԸկRo<ԸagcH-BΝ7iƍc||Tg?~qS٪ƴ(Fc/rn8ޣǡƍ5n|WG7o޻7z===9PU8Ӓ_!̙X($wI:Bt;v'22Rz s'* ;C!233Νlٲsι͟?Dd``{yzz?~<<<ĉ;w>}vk}J }BܹSCAAA``jmm]C >\Z~zU-ӥK PDܸIk!DK=6 lϝt);w !zKZUZM˕BCu@ݰOb435YԶ:to~w~;\~NGBW 6l޼z4]… =˺ׯ_/6lvKDǏ_n]UK(TW477/#D!D&MJ۷[l+m:ydfͲoߞ7ߜ?ueMj׮ݴiSiiӦFFFn裣S P9mmmVZ !ӫ%BےDybjj*uC[&OܷoߵkJEDDWXk !}]R9y?^%WNƍutתUի}ZlYYYYkժ%(..heQ(WWA￟  ECկ/lYq^FOcggzJndoc`oZT#;;'0…˗ߋ~e` n}i޼;w}}MVO?ԿBѺuK͝;777WgYW`ZZzO>IIINNN J<$|HDhݻN % BHOjY_J_^rFgڵ{]ִiӛ7oV9RW+Vx,4iԆ=zڳQ ̙3.] {hhZ[n{1000aBvBo^߹s+O;p Ǣ[w3OͩL:i5`6o~tZꙙi1>/_ܳg>Tʕ+{jժ4ooÇw:thJ|%ςBضm y( ww֬Yt֭[k!{٠ Ûkyv?G۵~a'N\bEv\jĉ˹xi?22RۅPi=33sӧOoݺ)H5<7w V/Ǐk4޽{7//O+<۶mKHHxv:tH3g.^e˖ƍ믿ny߶mFmkk۾}>쫯*ƷP(O~;v[Q0k׮→Zrrr*^zmݺUZ޸qSeGxqÆ ;t˺Ν;'߿qqRȰ777˓?{p">|ccce[N!C!TCBBT[}}}ׯ-{ !Bdffkx.^8rȲ:ܹW^666^^^}jSNNɓ[hQn]777)cUWgaaa{4iI˖-{エ4!DLLjesZ`A.]ԩ{=x@cwL333LX^޽{ˬmƌK,8qI{r}曕: kx7̤{c5|װgu"e}Y:ulݺ5##C(IIIy葝vw*̣G&Ndcc3bĈRxWxUWYBBBe77Ro-444|wO:uرROhB`<33{>ţ"O`#FB:t#lܸQѯ_Rh?|pڵ/>pF0x`!ݻU-vBW²jx>-Z(uĉ}}}/]ԧOqI<<\ ;?qݺuUx~b95HSUݻwutt?է]J* nsSXظ~ITy7p/5NYeiiYϪ+..>xO?t޽E5juͪT<1!!APL4ԭ+KY׃q۶mJ"==ѣGBݻDj&y"b3{(AФIRy-[ٳg-ZHYiB?M6u޽~˗K[O<٬Y۷|7;_*W*ҥK>ŋ:::})u7nk///!͛7ջJ˭ZBܿ/ԧ>-埅"O81x`'''B!,`>]V±cݻwW=:>>W^ =oڵk>O뻨 }Y2999 !.]T8.^xӦMw4hс [nry &11Q__MRr*55rj.IPf͚-[O>BXZZӦMׯ)U,*,~߿BhݺHbܹ%㶖-[֯_ sXXXTajHJJ˛;wn9>ʟe$}}{ϦM Ǎ'|g&LXn)x &--PFA?/92jԨ3glܸqڴi)))cƌ *_rt]i(χ'dii)}qk֬Zi<PIS]'. IDATիWݻwձu+5|p-[|XdP>D@M{͛7ӧ4hPN>-݁")** /**Ro>*.\PG,%%eݺuBzKjСC~~~DDׯeW5T)>?Q \ԟ2m4!ڵkU*W$''رCP 6L'R:Y^=nVVV'UJgQ޽{'''×u*]SNBܹsqs.*$˒SCĞ={ !_}Uaaٳg ## Gx Fzj={hAde_ںRJoywҟW677^OTY g㢪Ǐ" "$ ,[% J -jy5 ^41, b嚹K]Qa~;pfs>}>=sz{>#I@ |g@uz…vڭZxXfff3Co'5''gΝK.Ugee 2Dmjp||ҥK{ݲes۷p„ Bbɒ%6mZttt׮]oߞުU+eF2sL:uB$''CmW޽׮]۶m۾}9sСC90aš5kbbbvywA߼y]6i̙36332eZثqFu놇?oQ8//A۷oھתU/>O>7/U?~x`ԩBɓ'y^n_|qܸqʂZX277wڵ/rΝׯ_[ZZ}}9pTaa;uԠAW^y%..NN ݻwF6 m*(0Ibcc fDBuօ={]vЃkN<۶m}_5t"ׯ$I[lӧ W8;;?"zP~06|T;99vȑ`bs|gfɒ%W^suu ={֘ g4*xOOJ@4k֬Yf: PQOܸq)@>rQO DrQORޒ$U),YұcGIt>ܠA憚J&))I={Zz"$[sڵkgϞmmm=o޼ΒzѣG[XX=$IFFF_pa^^V>ydƌ>>>vvvց|ͽ{T;vnٲرcoݺ։TL˖-\~}ժU8pg}hPy* ٱcǥKI%$BaH;l0C'5͛7>}$yyy?~\CL׮]/]t=N\n]XXٳg۵kWԎ!!!oNNNNLLСC||Z9O͛n~;} /ԬY3),--uv'NXYYEOL#44Tbcc3m4g>}ĉIII;zhj2/VQaJ<\)Ꮛ3t"hO*Fk,eBxyyi122*3FB777___ͧի7`=gggmYxbʔ)LN1h i?~_eŊBIɡ6(bή_رcKK: t|g@ue|]4ZeLVVִiZhѠAޭ[[)RRR5j7ݫ:011~TȱwK.3FsX@@ȑ#N8!gcccKKˢ-B`FDDب6lPØj߿/ *ډ"++KfϡL>\xqϞ=,--===N#0wԓ'O&O8jԨoٲw2,b شiSfffǖ ԠlrӦMݻw?{lÆ BBBA=~T)2sNx,,,|||v9n8OOO!jĨ(پ*rbHJJB9jԨ_K.oB 0Ľu֭WZ{vvvLL̃ڴiӽ{w+++ծCnݺ~:uEyt^Уŋ|ANoeeu5k믿Zpw_y'N륗^:thk׮m֬Y~O8[ڥ],0yyyΝիWǖ g)To~@t^˗/Wn._\tR)[ܹS+//*Oh"riӊ9YNƍBիW'NԪ̈ӧwx„ >,1ԩSB &QӁT{sssΝkeeյkiӦZYY}GϞ=*I9SFFFrrrіB>h;JjXXr3//{jh޽Bݻ?zHUr.^nRnѥeRf@9o{`3b3߿C~[lQ  111Qn6iDS9RAuMVw/_[i㬬+W]tYpa U &MT~}WTϙ3?޻wرcC֭[wԩ>>>GYjѣG{;jήM6E[ϟ?Ya!Ĉ#&&&j*KUC*U9K/7L-]vlez"177oݺs֭իwm##&cѼys'''m !ݻso:|1b jֲeKBqUVݻwã͛駟MO>i߾e޽7l0|𬬬իW+;vÇ7oNKKϕ/.mfP~G:tKݺu%IR^vG5\|K.)k:srø2wUf'_|?ԩӢE?j1WTAN:BB{ә$I2KBjmoʕ+ݻ/\poQF"Omܸk֬ lܸ)SfΜCI~ڽ{wݷmfee5iҤ󏹹jQ...EWuPIMMuqqyʟ拥Fyk(Jz"~WXXx++=/kݺ͛7TRSS͛7\M4IHH(ڸk׮z+ @9cW3僜T\tIsYi\X}vzpqq;y򤪥{ w]T*~r(˅Cj<B*"ھB/={VqPl ~_~W{J\ԩSiii͂UV*[ ԢE;v̫ryգBZ+[:wqF).ܹ毣RfEITΝ;/^i&zPRYuϷnݪ3c !DLLǏU;wy2/JoznՈ@; ,8}˗O8ѤIGFF*?(|6UdzW7nPnpSNB=zJMMHMMpBbbbvVZU̢U?۷'={tuuٹsgffҥKYYYC QeKݻe˖Ν۷o_aa  Xdɾ}Mݵk۷jj2~9_HHH\\נA7~x o)nĈW/.]K/:t:{_vm۶mknn~̙C\̋RFqSSSuP)P!bcc TQB//_~˫aÆvZ1z%v6tFiy92$Ijڴ߂ ={U'O8qb۶m6lX^=ww)SB(8p`fffݺu۸qY\2|pggziw޹/Ww !F%s_~e666;w(((y䄇⋎ƍS֩Ӹqc+++///Rfe^_7̣G,,,Ǝ[Z2e|-Z((((((Y OT?~~~~~~Uh|KJ|ή<)7o>o޼Y]yì[ϯh-R^ TO2332e/^laaat#G淚~zyyIe˖>} < ɔ9yӧW*TΩX;Ibcc fD*$N%K_z5//5 `&&& @U?..Љ5P`5xܬYf͚۱5iP{POƍNJ`P>rQO DrQODm4kɑ%;JQ%*?A999%$VjP6III$ٳO8E摑H PQ'=z$IE ł :wlmmݻw͛7*=}Yvfz̋arssW^=zh $ Gͻ/OOώ;.]̚5+&&d̘1zGj? %&N~z//QF??~|g/eSf}0q7nJ }ieT\e^}k̰sεkNNzH-L@VVִiZhѠA]'OLP'B!:g:~xz|||T-}B;vRSԇn !Q)222\\\.? /萡 4HyΝݺu!(R:.\(S·~X۝={V(IRDDDdd3g?nl߻%--Maoo_V(; ) JsA`^>371/Zn})BQA(ejq飢.^x]v%$$xxxt޽]vFFZl꣏>*,,ܺuol9}ʉJ%_z{{\UI… j1NNN)))3lI߹sgÆ rbvu&0e IDATٳ~O?5jǎmG=>}Zʕ+sرcǎu]g!o%Zќo?}ioT=zusرcw)] ~b$gff9^҈v#~.eڵkTOZ]zI*СC_'O|֭j13fعsgLL---;w,:X-J Pa' ۷ Y~gBB­[bbb}n7 kN9?ɛsi˝SNB=z튌T~HMM-Pi*^vٳg2 4hPzz}ƏEFz/ҥK/ҡCOjݻwxxڵk۶m۷o_ss3g:tH+ipႩi۶mu;Bs&($IÆ 3`jF)O>d֭W^رɓ ^HQPrs>kn?ۛm|IHrՊBhӦѣGs+eEEQy7no޼Ym?n0׭[Wβ/x*##C_*D0̦Lw::rHppq^_~:t4iRXXXƍ>} 0t: D(CJJʄ <==ϐ˗o۶I`@gϞ 6j_|E޽Ǎw CPOMf̘qʕ;w6nXabbe˖&M:;*D(ս{vپ}{Uc xA:^xᅈ͛7t0'O6mt̙yyy hz":ٺuT̚5tΜ9zM 6!fϞ&,,,33s핟 hz":qsspttԹCEGGgfffzMǏ׫WGҷo_!ıcNJdyG 77H!ݻܹCO> 2dHu8\yv*edd/]455uŊM4QT2%""ݻ( {e[l)XhQffy6l(HOO/loookk[P'j˗/YI 2d͛7mmm>}ZZLEF0 Z$$$}ڴiBu)[߿/ppp0`V D@-rY!IDI"""V.gΜy )< z">}Zʕ+.5_?رcǎ޽eϞ=B___9 aIz\ z!IRlla Tiމ)&IgϞСw!IRVVVǎ?~fjjjC \\@k<NTݔ``Ѿ}zzz&$$ܺu+&&x1 Dmj_~֭[ێ;.Z(((xέZTՉޗ0Ⱥ$͝;wܹÎ9"9rd$:PÇЉPO:rHpp1 T!/ Uԉ' !5{zzVJ.PcQO7n0t P~"'z"'z"'KR($IN@ 3th @R@msر_S@ CTʹZ`DrQO DrQO DrQO DrQO DrQO DrQO DrQO DrQO DrQO DrQO DrQO DrQO DrQO DrQOȈOJ,Çz"Uȗ_~9w^z\xxŋ?oO?G7Zz"U_|;DFFnݺqcڴilٲ6n߾0uT\jT}?إK4l^~}ĈճlN*n޼)IRyzHMM4h}j'P%?y$===<<<<}&&&ERA=*+,,Ç+W|gϞ?sWʺQF._\F/%X?*hժUDD!'Pe?^! 22IFWrssyyyO @,ZҎ9C!ĉ'n߾Q꧶ٸq㴴4ˠ?~,Vn>zH3B_򋲚 B:Z… BC=xƦ~5jԨѡC92zhG^rs˗/?qÇ4h`jjl2զӾ},,,zlٲ-[!|иqc!ٳglr)S$%%ѣiӦƬ @!$&1`XqqqÇ'2j䢞@.䢞@.䢞@.䢞@.䢞@.䢞@.䢞@.䢞@.26t:?Vm!߿j$@Y$Ba]ӝ J xW_)Lw988Ȩ’$曕D=7n\iLLL^'`%blllܯ_?++O 䠞ԯ_^+^R,((5jAR9'`#G,Jss^{ 0XXXm1112dR2QO0֭hbbj1bS2QO`Fڴӧ2QO`^}U[[[gSS7|ė>@A=Sȑ#S|MCge! 6L9@'`HM6B7Ȉ:IP:z?C ի7n_sP͝;gϞ,uܹK.1޵j*33YfN@ y': PPOg,@[l:tPId@M:MDDrQO DrQO DrQOرܹs?Bh"ooo+++ooE) zyϟ/{ݻWo4z2sB8::FDD|G...֭ٸqCCC?CI&LP(>É' 8ydNm%ݿ+W}$$$l޼Yۣ5)22R9%%UV~1c~WCݼy$I "00pӦMk߾`Ĉ{ǎGݹs'%%xoϞ=ƍ|̕/_UfPPO:siӦ%%%ٳG!7l֬$IA*A:t3gU{'(򾃖FasssW^=zh U;[linn2v[n?ӳcǎK.˭[N=Y@=ydƌ>>>vvvց|ͽ{Ԏ %66ӧ:8m4###۷o=+CCCׯ_OHHXpa\\ӧׯ/凨(UWDVIHHؼy7aaaE_uKKnݺkGݸq֭[㏖-[vܹvڕ'O*O8P=/|}},,,~ǿ III͚5+Y97 : vu}B!2Hj~#%VZ2=ziӦ+-B777___Lw}'4hPׯ?vX&7er2*???;;hŋSLQЁBVDb3Ѓ[szXyԩSgƌBSN0ׯϜ9sժUҬYLMM̙SZ޽{/]4ff```Æ ?^KKˀM6effgi*ᖓyO6PƖE[7nP,mzyϟ@e-"""""йB0^{e˖K.-((Pe:g$pwwUݺuիgeeU^wwsji-'s~bw8PaÜ,X)_bDnnnBݻw߹sP])((X|SNʖTݺʣG6lذz+WXYYo^C!C_Cؕ+W-Zӧ&LP^W\իj`[NE@eeeZp„ FU<āJKK=o޼M:Po+Pj( {Uׯڵ+33wQsSrr>~K.ׯ677Y"""޽~a/_[n&M4L4~ѨQ._sUR3 .,Y|„ &L?sԩuW&мb~èm۶9::~'NPܹ葇Gtt#Μ98a/_9Κ&7o^RRҧ~bQ e˖ ݻVwlj'i:Y&---&&ѣGƍ[[ IDAThVx>J|Hpuu|rBBBƍmll6mSO6HI4L˅ aaa*!́B888ۭZ߿LLL.]oݺ5%%ٹ܉z" *bw={|_`A~&M4p:ugϞBmLLL$)""@nԩS "׮]z뭀+Vh8ݵkה]%I@ߤI4 ԭ[֯_W_:99EFFN4N/z" ~aIwƍ5k|Wvrtt0aBxx e>}Zʕ+WWcǎݹscǎ 6~02Ҵ~LZ̫, &---##X@m۶mݺuw.,,ի׊+x cc~Ư#&Rdɒ?xӦMW?^'\re͋+k[׮]S+%''4E;v033|2뉒$vy6PNrppPU VZUPP[j&M:uGE}-QOz*uݔ_7nsJJjsÆ zȯWE ]433?~;֕C-SN=z(5dd \pԴm۶MT-',mo'5''gΝK.-Lrqƕ>h*{hJÆ B:J[9No4?+W^Jz?EiZPiѣ%ݸqҊ75jԷ~C2й(m֭[ /+ͳẂ*Ibcc'=(*J[QJ;w]v:${9rD1rHm: {ySNe@ilfcƌu-9|]>}tMR'dfffSLT7wӧOb9\^X\P~" 'jU UxA3ZqP@źqㆡS-j%B~"'z"{\MiKR)%I%9WN5·14CgM v1ƌQ2ePtB"}+]ֵR>u'hB mVdժUZZZ/$)2lذ S;ϟ00wrM81++KWW.›8SUYoU[;; 8FK%Pӡ .==ԴGܾ} {+ \\\* ={KLL{niidc9^z^FFj~`9--MIIiԨQo xgoݺU}_4m377UO\lYLL̹slll}ݺe4Q ,xU;>~X`ɖG ``` ry{ʭ)BNQʎM{ N$j 7K߱cܫWjiiKnG;V$Ν;Wroʕ+KJJjyg ݻ:tGBY:""B[[ťmSY5kAPVV---z8z"D]rE7%.\ :v옘X^^ުݻ'Bǎ埶Qg\=bqcܢ(˥_rejjyA*)a)+VTVV8p`ܸq- 5fNhWcǎ%%%U%m322XR~#""ڷo_߃4my =zk/^_|̙34Q .eÆ ӦM<9`sΝ;wnРAQQQ 8;;9jc36Sx'~S={ ]4pΝn#"˥DNMJJ8pڵkFFFUDA:To߾tRum,--oܸqљ3g֩&NRO끪nnnQQQTG=h$ DTaÆ.]TmuֹD۷JYjF޵}7v̎:j6i}"A4vFc ')ܼy12?N^$9::֮ɓIyK.:u*22R$Iuȑ뒚gϞ:{8P͢E"""BBBƏ!Qb~JD )))4WzޱcÑ#Gn߾Rs9}l5$>paws}fXYVAHIIf̘+V(++^xmϞ=ώ;VZZW_9ѣ՛%''[YYToмϧOvi;ww'".\].&+5@&'Rx=r{[[[Egiɓ'FRt&Ǐ?֭[t`z?ϟJJJ<dl[K___&s*֙3g]]]D.++ ݽ4388ݽ5]>z~͕U0DIMM>ڽ{wqq4Hll[zۧ5jT޽Ν۩S_~uu.]JHH׿~,Yc`` yB4Y< H睁f*00p߾}7n(--0as5Wnnn85,h%K,YD)+SSSSSSEЄ3iQO -E= K .D (++StQO7nح[/]TqG= sqpp|AED xbԩj۶mFdff*:@'d`ѢE׮]ԩ ***ᆆSL)--Ut:PO4T^^ADDD^6jii9rdر Vh:t_{{.]Ј?'DҢ@ZHz"iQO -E='DҢ@ZHz"iQO -E='DҢdI$AAAA``H$z}XjVqqq2@^^#..N$EEEջD WAAA~~~˗/G%%%[l5klmڴw511DNNNx٢E =y~)//淚_z$d%%%SSѣGYvvv~C+E-޽{?:::'N-vzo3ʂ lllܹSlom۶9;;O0A]]={vLLLj4~222sNJJʲeݻo߾nݺh뛔dkk0olbEgS  м)++;\,[[[6 GqvvDٲhhhTm 裏6o\.V Yc"CCÉ'ܹa"h޽_cd 8p@TMU*++/\𚖯U o4fmmznZxƍiɓu 2@CC?.,,L8vAA ̴FYٳg7777229sfAAKwww722vvvޱcgԪUDAO.BfffXM0;rŋkИ0až={_z@xȀd2Đ J\rOںI,?~|G9*55uҥG>>;ٟY1cU̙3CBB?޿KKKcE=Ȁe@@ G}i-OW+WWk׮ `ffV QXXs-[\vMSSs޼yzX33++-k֬Yl֭[W\Ye||{BB _}dzE__ߠ AFQc'N 4(22]vu:͂7>~ԩSs̙9sf6X5 pڻ$_vU76lQOIzzz֭ %RRR6o?ǎӧOo۶=WlٲWhYVV-)& PH1c䥊ʄ bbb~zAΝ[ULaҤIfMMmX/_SOLOOձolbDМ{mFecc}3f$&&^paΜ9u*&J={vҤI[DjO222TUU?5\{Ĵ4%%QF5[uݺu߸qc^^MX6/]'\xm [ܟHҪSS`aa~ȑN:tܹ==ztر"vܹ \FK!77\MMzm޽knnޡCƫO>޽ѣCBBj~(ĤJ /-OI=fI155رc}]zu׮]=<<:TQQ!}+V OHHشiS@@q^Uw>v1##z199Fcc㌌<都ȑ# 9Rcݻ tҥ7oJzձol٢ /^(|DOϟ??nܸ.]X޽{p5##5C)yiiK?FKKʣG֩׸w^nnR_,Xwյ.IUǾdz"hN$e#I 255 {?```z`i9rdvvvT{| UG8pFE RTTT1""B!㫗G+**6nXQQ\_FU{Gz"@@$_H\zY 2x`g$222^JM+55ٳgϞ=ܹsᑓsĉٳgi3flٲe۶miii}9}tyG1o޼={m611R>E~СzȐ!~mXw{nrr lmm7nᒓUUU{}کSɓ'D{ ܟd@[ks"O?MJJa2顁$30WX;smv۶mU/<(Yf&M4iR=B:88888HUK_~)..2߾}4QYYYU_ PF)7 X5 ujD̨}Gu5jH$ wuu?~B4b]t)!!n?,۷ƍP@ IDAT&LSQQQt.('D򙚚}[dɒ%Kw[F~'/33S(DҢ@ZHz"iQO<\)@fZ):Мܼy388X)4']tQtӶm*+++:POUt ÇdF$y }mMMeƌ{ oQ۷;v'N}(:&p"mM6}ᇍ4DTT{"hȑG# 8w\DDdE]]Ç{xx;ڊNPDbڵ|ݻw۴iWTT+==.@ S<+**<==]]] ࿨' 'Oi۷KIIٳg@z'Oܴx}333WWWwww~Ţx+?~|ȑaaan^xCh*++>ߦMÇ{zzzxx*:xQOOHgffO ݻwAPQQD|witocǎEDDϞ=۷ŁD"Eo ѣG6mpӧO}v~.^xƍŋatoϟ>|s玾ȑ#===Gծ];E-D@˷m۶_Ԕψ{3f̚5kAxիWUUUmmm3:Gee """"##/_ܮ];wwczxx):h'Z>GGG ]vgv޽{ڴiAsNddddd'JKKǍ7~xsssEG- D@ ԫW#FgĿ!--B>#@uΝ ڵСC[jtSRt &544w.:eee 6ܽ{ի^^^nnn^^^aaaŊ1Op={>|ƍ6'|+n޼yᰰsεnzĈǏWt4p"%KKKKNN0a<MLLӧ|h``.YEnO ??YYYgΜy?`eeUqjj͛njaaVe|ÇϞ=[^^.+)) ;tГ'OL{u]O]>+$$MD@ wYfkN;wtYZZZN6mʕ۶m[xǏ̙tn_RRҦMWkFFF=@PQQqܹ~---~?@u-kbbbAu떗FUTTo߾ &X[[WTT(HT/k$LOO@RUX ɑNjmm< 0":yA{Ç0+W*++%deeD&x%dffFDDӦM666[n]ff43hin߾9tPAvܙ3o޼SN-Z޽{C 111YfH$mYXXwU諯={kRRkMLLl߾իW͛3ɠ+**vڄ >3g2o<ɫosȑ#555wʕz(D֭ǎ?=zO>fffܴiSNNNc甔ww/bJJJO>@"eٽ{g$/A%%%jjj Y&**ӧfff?^VVVΚ5+55UM__ɓ'?:thA%wnff&KKKvhYe] |n݊ť'MGEEELL̂ A_~u|ѩƍͭg- 'Z;;6m޺ukggg&suuu PQQެell?lee%/**O~ոkϖ.]*}vV255=z]דCIII(/_ndd$٘' R|~AAA *++W^VZ&&&fРAګ$t钵ueeeIII'N:uÆ W~EyytJJJzzzϟ?Q&HYYu׮]044366ܵkWQQQ -m۶߿qqf>#ha'Z4[nWXQQ%%%'t_CCEjӦd%`A===JKKe>?'O0bĈ~g- D@rYHT% H^J^ϟ?_n]U˔͛7֭[[[[tٳE"ј1ckS,Ks:U'U@δ>~GM6@!wy 篿{ѿkEE@ J3gXYYUSNݻ|r޽Ax ZC:--m|cǞ<55ŋ{鸉666i %ŻO裸wz7o}:::ڵ;}tllYlllΞ={ڵ~}/^|䉖w}WX|С}]xx Ϟ=sqqY~D/\pa)O¢͈Ӝ9sNvo744|cliiVXX޾};v?^Z>Z/^hjj̚5KC|'rN[XXÇ?}S):-'Z˗/:99y۷oj1))) 4h˖-?LLL$DX&D@qҥwM&&&*))If9SVFs~M6>>>'*++z"t钽}旉333MMM9 6eʔÇ?|Prߢ3gz"tR~?nbb";@dɒ'N 4& ++Kh 'Z´4 ??7nŹ?133ssscEz"HHHtppYYYɲ߾}dE555oooCCC//hXt4?-D\\\N:w,q/_,+quR5۷WZuU77.],]N@sB=BHc;v444zԩ… ^:cƌ;waÆ yyyN@3@=B+ػwo h kfggGEEY[[/[3,,Lh'Z \zѣG D$)..ԬAEEEɌ"H__GgϞ={vc/8;88xݭZ>}C Kܟh~%_U-)sssϜ9coo/,~|ٳg%+>}T fٹUVy911z"ॴΝ{Hcc>K. .LMMUt4z"YDWVVZJrrr߿O=JJJcƌ9p@ffEocc3|Ъhv'%;;̞=QF(!͝ъ+?ޡC{OOO7%%E3fO>/?q hll+-khhhVV~ȑ={J.//Wt:E=,ihhtڵy晘''/##c޽ L6dҥwQt4ތz"rrrRVVzl2N=@SLtҘ1c6mԣGPO4WJJ kժ… 3tQQש'd>$$$;;{ڵ/^0`@~v EG%'K.YDnC_ro߾rikk/\0%%%**p&&&_~evvh$Kjj\%11QKK4H$ruu=x`vv~sN77ÇbE@'/MMMcccA.\(ϡ{ ??7ooϞ={w,,,֭[cEG'_o^&$$0y"@$k 6, K.x{3_}W L@dfffeeT_Y߿߶m[---y xDEEޘu;v_~ٰaիW?쳉'f/'2/(,,TUUUSSStdFΜ9qwa޼y ,ёCB[͘&D[%444==}ڴi|djtEp377߰aCVV֗_~imm\z"4{:ty?k׮rEG4PUUJJJԩ|УGu=yD-Dhi\\\>|Y{{/GW~葢sZ2999]vm֬Yׯҥ_NNs7hD}ϟ5=k:99ijj:99]V,hA]]t֬YW^}UyyyjjjҜ³g-Z4`}}}mmɓ'Oyyy How͛obb啖\z"1###+V0`׮]矋D9s??ҥK?yz}O=ڵk4 mۦ6a„9sܺukַoߖ,in+%%%$$$..ҥKh~Z):P322w7xyyIs˗I&D"X}ݛdkk;cƌ^zVVV*: b ,搎;>~GHҥKNN;155 /lrz000O6lؐR~222{u-88\ѹMDSLa4رc^^^ZYY߱cG.]6mڨ 9ȑ#  SS۷_v>ѣUE@=-ɽ{Ax}J⪶_r„ bx׮]6p@__7n|uݻ 5h:vZhaa6lc݂ WƳgoł j f255]fM|||Qߏyik׮544\~ /JTTTlܸYT]]]ڵ"DbDhhiӪo,Hddd#­[ݻץK˗/kiiItaر5?~A~… ϟ>wٳg'NÇ[XXdddDGGgW'Oleeкu7B@@_=d ∈|]]7.- ]f͚_SVVVt(PODsU}eMM.]5jzzzT/)5kx@ѹA= {***sνu֦M8`nntBE4D@cQUU;wnrr  PC~njGWt(@=QOȃqppիWmll&Nr9EDԇH$j! D"ѫZjVqqq&ilQffNXZֻ'F4%'6 &dI^UeU;99ظ8H%caa8UUUggg77˗/+:V=q֬YB޴iӻkbb?ѼHs)/__AIIɖ-[f͚^{oUU&…]?FqEENDʼncon(Ozzf -\(5$Dd gdd_@~STqppoq۟>~ݟ'|kii):uO>7oޒ%KjϿrSUtbـsqݻ(8;;Zɽp[پ&0/ܲUSHIf̘Ѯ]NWXdnnnݺ/^(:J=qɒ%˖-k̘1H2?ᑙ6 ,0339rdySLӳ駟:… /jceeU̠dϞy'߿F3e\yfȐ?{ϟ._~f\??zbW\QcktjUY҄,qCwoZ{KKkypx yFXǏ._;l؟gGL/˓oRɓ'֭2d\XXX={6|sss##3gT=_QQߧO.\P$JeRИ0až={ ܴm/##gŊz Ŋx&ӧo wޝ2eѣugϞA]r}5y{{_~ãhU---%d?4  $ZEYVA* Tj Z7j+ bݲ(j]ZVQP@P@Ay1ϝ@$H~2'gιfHʜsJJZիWNNN'O3gN !ok֬1447oLB233 !b>6/ѦM-Vʿ#P9o tƎm}T!nnk[L’uw/>|'-9jajn$HO?}~W_mח~ UG^KKáF,9rLA9fkbff-[֬Ycdd$H%TTT,--ǖ«fȴ5: 9`BH{)] \=963!ZKFuEE2I=8%+*+sU6[gRe+.^{Ԩ7ms8~\Ofg$#~pu_AT˴4־zɓ9"Ţ^ WPP>|8] 999__ߴgϞQ%VVVq8"2XVVڅGFF&000??~ںumZZZK,nfff22ʲ/^߿ӏeee !\.Wbxҥ̀˩N f!NuZXz']cEJyzٳܷOyz2{,H08Q MM]3gFuq ?<,,^f[or?kxGBn߾MMGH; ϟ1c_mggwi&|;w޽+///oxxtc -((qҲHb/AQQ'OZsBCu]][?Jۢ"cÇuz;;E se<ؙRְa#j*~%%EDhɪDE=d$HװO23m~Q^Sƍϻp\.7)))++k&Lhf[o==|cvv@cC;ؙcBXtW۶m{.ϗvPRR' !뇆:6:ỴÇa &++W+(P3n]yZP1165s.uKKVVezzӫWgΨ3^$(HNM,>th69;&Z VXA*m4c !Nuq>444!ill~:]"2܄ .]ˤ$ A:ӥX|t'O}}̣GHt?yye'NՂɄw )^KˋgyzZZbbj=bK[;p"(v5~9gN4%dK<1ﯳ !rBKJJsss>,V Ԫ^ǎܼy朜)ޜH%;Rꚕ믿>|`RE.ɫ:s&*pڵYYY<000f;=ScǎswwUTT?XBJݗbPg؆/^5';?a{XeF|z!YCJӦ>/ҚkkBm/+䄆x˗ͯ_w$<Aޜ;WPkPE=LEEꩮ]VVv…sXDwرs<:::\\\zyf"`zB {%''oܸqʕ111[lqwwv\ @>q߾}㊊ zD W~葇ǎ;zk˚5kZ7ӱaÆ˗/_t"A>*..N} !#Gٷٱcll e/^\ȼCS˵#G/[vYNC741QXciɓ W|ƍ}'N$<~@<4YZ o+ezzsMAKKEYY!BǍ8p 55ᄅ^gނ_W_>7nڵkO8gC 7z=ztڴisR$>G^6lwߍ?{֭Gqp.(FGannnnnnm=+l`۷ob{;}Xe˖ܽ{NtS=jQeO.'ls!BQߴMbG(DFAhtәG\gj&̛)BBȌ3ݑb GEEeΝ !l6[oo!;x`mmW_}V$@wcaaĉ˗8pʕ˗/WnceE8)ϟ YfFGGK+z`x#9J^ݺu;`miSs>'p]rEWW׵߷"qSSSBB!DD^j!{%%%qwu>>w~g[[ۤ$iGcuRRRZhі-[6nܨܜ3%/9x0*?s#F芟Zb̒b>tJ~N*''Əb*++]]]'N(n;aaa D__Ȩ:ׯ_:A4IA^^ 2eʐ!C"##@c񏭋2e T,!--MځO-*65}g!;ӣmڴ)111//wʕҎ z|/nZdɕ+WfΜyf===iGs Ѝ{Y':q_|o/TPPvD=O''''444,,Ҏ'@>z,UU{YXXxzz<~XA|ؐOɓΝ˳ vP*zP$XWW˗ <8%%E|O9###Ҿi]6;;{&L>}zii C߾};88x˖-ǏvP>~xjjjFF/,>' )))***;wtqq3gNaaC<==sssᇰ03gH;"=ҥK>|xɾ}B哒 @7%//z}''''OH;(D^EEɓ'Hjjj:u+;;[tqqq.\Ϸ o۷ovqx SL/eRj*_uܸq6668p ,KZAHO A<}~;z $!SRRmll6n|.w%&&J;LA>@ϝ;w,Xߏ1bڴiΚ5޽{Ҋ @Oh͛jjjn߾Dx&jF '\dҤI---R @Ohʕ+kjjWnjj|w}'D]~}Ϟ=Hg---6m:zhG ]'e\.WAAAD3fdggwYTR|"@+deeO>~ʔ)Ҏ{-… rr =||<z, 6L[[[t-[ cK;%$$`=` D` D` D` D` D` Dn~ԬK&NzQu,KQz,ܹsl[j"TJFFcbPWWtR''']]]---??}UTTN81uTsss3{'O?sss &&&{vuu=tdG*T__cǎ@UUUpԩaaaaaaҊ 'yL{{!CDDDteHo>m4ccc߃rbȐ!VJKK DaGGG GG ZZ IDAT6x<:eee .WUU555 w^[WTT*))?^t+r>O>o9;wtvvUUUtܹsutt233 !~ȑ#}||^wر7ntahh8o}*qꢂĬ[n߾}fby~7GGǠk׮bcc ׯ_8qbuu1c>$''s8VSSxWWWqޔt/<>G( 888Лʞ[hjj/ٸq#!dѢEfxxxee%!"0c BݻgϞG]]yJ\.ٹ 'T'KԨϞ="/_u Xg}/;)///b 0 nݺEʰAz՚T0((H ~FFF,ÑTR̙C6mEwѮ0C_\\,"VEEEB-[Fm6449bݺu_B5;ۺu+!dɒ%&u[HH8jԨc@x|Om*((L0ʕ+4hMjIBѣ !޽{ !ӧO6}}}oq#a׶fW0?b_!˗/WWWJO_&$$|WjOzĉA2'D1f !<輸:Xh6PQQYb=}-,, EEEFFFyfzz:!$33S^@ 6j*Я֢xgϞEFFVTT߿͛7SK?֭[7p@555{N2jǎԳʟ}ٰafΜibbLe.%7=aB>^VV611qǎ«EF b+..&H܂S SSS.\KM'Զ9y$I-ӑ|p,xgKK˛7ox},XZRZQՋ/.^V33VOTɵ;'2=@~]bRyo޼̤K233߼yCxNFj*?^@|"ѣ?~t:::n޼ZKKKdddKK =\NN7>qUڽ{ /-- !~)]kqJJJBBb҅h8"uNO'ۻӧӦM#ٳRRRbIE--MBinn>vXB޼yCigΜK455 %DFFm?N2k,w?.VH__?''AD"b|RG?DZs%DDDP< !ׯy&׳g oÆ [ne[/xgh_qqqXXXqqqvvvzz]ddpV[O>=ydeevDDUݽjҤI#1‰...w޽p 7nU+ :>>C TWW=z4oPPPTTTll'Ν+ӧO߱cΝ;߿?x˗/Y_>fjuQFۂɈ# nݺu%%E5ޤt(ƍ7o޼SNYXX^xqܹ#F ̜9sԩrS> Ǝ뫨8|: L .%??ޤ詊* Ο?_ZZjbb_KM8qĉ={}Lw;xIF5vX++W~jjjFFFeeeOܖnui`"ѷW ybvچjK~g}6`^z)++.ZL Rȑ#rFFF .))0+q\+++'''Ν;+''G z_|accfoo?s\}L%7333443gErwwgKKʕ+ LMM.\HE1DDD;vƍ999Ef=vcx;v_FMMMHH xy[o{Lx'Oӧתgf ;gϞBfΜ)A=Lg}/;)NCCnϟ?o?3[nÇ_~:+)) 裏g̘q]} o!tK#.Y>2>>~ʔ)<Nr;;;Fr'.X@=q۵kׂ I$??ӧOwdSt򿳙cDh֬Y]uQQ)!!tD8֝+Wvd'?; ܲeƍUUU+((888XJWW׉'ve 3S\~=++ٳԩS!Н Zzի߹s&&&&&&666ZYY-]tʕ]FÆ F;fddH;`DLMMEWHLLX>+VXb訞qYN'@WXX(z  0|"0|"0|"X,ĻO8NG2UUU6m2dic?hjj gffXsν ԅjnҥNNNZZZ~~~+8qbԩ***gO<n'--- fƍxk6LKK%!!AMN5K9|޽{:tH#CZZZH)((ھ};V\eV試N&@ڵkʕ+{M^';v TUUx~Ȑ!R x<?eehhӜA%&&2O8;;[ZZ^t)++KGG'33ؘBMMmȑW^иq̙jjjZXX/͛g{{ӧOΝ;MN5Ƽy޽{/Yz\PP`ggW[[koooiiظe˖/R#mvUVV(++0 EEE&-ܹs۷W^c~ Cg}/SNM4!--M ꔰ;mĈ͉߯LNTQQ۷o_111!!!wijABH||9rD@o&''+++{zz2ooѢEfxxxee%!tIuu:ϧ Ƿo&xyyq\WYYibb0vT !`ƌݻwSϞ=ӧzmm-8EwѮM6q8?}TSSݻWGG'%%a T_~KnJ 8*Yˤ \gt'Z[[;;;UF]]}]@/xg3N:p999555SBaaa{իOлwﮩl6](//O?޻w/!dʕ{ LIIad]t=vQ^^^555˗ ,Xpaccqvy -|OPGTUU>Yb·~+q5H>}'l\\ܘ1czmaakF_#F.F,Yf555ŊrM{}!IIInnnZZZ{jcǎϠ"xmmŋmmmՇN(sA?fjt>|:= 3gBΞ=+q 'xzz]GEE}[UUիW5ɓ !GKɻɜM˗/GGGo߾]III W^yzz;99=zhΜ9 ,={7&LX__/Vl6ٳF~?{{bhhȿɜE߃ƏBψ$kkkPGG__~kll\lYppAB[[[kkkxݶ*z1&yyy>Lv*++BCC $''իVkR x۶m痒Ҟ={ l6Ąm޼944㕔Bf̘0vhWHLZ'4͖_E?pdee_|I̙32m4Ǐ[XX(++:88\vV1bĒ%K544V^y {Aw׆ MjҌ%KP'!d_J v:|-[P[loayyy///z Ɏ8JD(wyBȨQx  ",[lhh9r$źu )Oqq墣۪;#!ΣGsyyy՚ՋBuuuMMM--UVرH^^>//:h M#0 .wkw8<o}vMMgϞлw VOTEOŚ?㕕OkK˗/WSS{))nAŭ[!>>>&5ƅ /^hu v󉹹fNN!I.F%?Tq2'ux<7!d|Q!?:n޼b ARBH/>PD9_~Qs\/ܜ㕕%$$OMM>|@>33s׮]zĔ ㏄jּ/2&&Zo6iҤ"mmoߊ." aO5!dݺu.;v^gEGG[YY%$$xxx?~\VVyhWPP_|AL̙3Kxv[]imm0bĈM6٥ *++c,\ff-[6mddd$Y } TTTP/_|2]_KKbubiiI=TVV~]Ȍ=Z~;ES }]LFt^P5۪ 1OQWBI(/^laa+ڽ{/^_[XXFPWxzzzՄ`H__a`v!]͜9sݣ6oܸ|ϟ/Zf(EPPI2W]]E}ֿ^xqjjμlkkd+|@1ո\.Y\\аf͚vaff&##CwfPz`$!DB9{GQQ.҅y=ARX,V?R{9 'بLLL$nԩS .G% .X7 IDAT{W:zzznjnnG[ii)U0vhWxx϶O0겛JRlllN|XWWggg'n[ZZ-n yyygΜ9sjrrr⮎M155}iVVz*Z++k׮ 'v% -ѣG<8~x~VJ7o%22Nx{{'N()) 0{lBHbb"u3ZSSSRR!dر6nHC֦f+g.%zcʩw;!O? LIIIHHpvvvrrbx\Lx߆ HeB !---{ʅ+X-*bbbuVc =6>]XXHϊ(ӄ.K8ydqx޼yFV7ҥK !o޼ APsrrmڴi={P+$&&Xӧ%= fD\T@}aaavvv*++[|nݺO>ʪɓTwwI& spp766;{lIIԩS  ޳gS>>C TWW=z4)k׮zAFFApp]Wfͺ~1c&Lpzs...ߵk׀ƍr֭˗/ ;vظ8www___EEO0,1& (((***66ĉnnn s4h:U';;[AAuȑ#+߆`Xsss[vmCCC[5˯_g 0W^ʶ-*++BTKK?ࠦfcc_Wr?СC555nj/N_ihh=rȸ8.\n``KJJ:v>2e͗_~KjϞ=#̜9#^ߵżB$pɡCƎngg_rϝ;KM&ЫW/Ǐ nuzjeʕ .DxDWW׾}jhh888޽[Biiɓ}m͛/FMMrCj5H.kee)x_VUU={v[pQ.Y(>>~ʔ)V2z?L:fJJJ-:p@mmSׯ_E쎋 I:hSFFCbիW.6lXs z355]!11޾Kbp:{A'@WXX(-p?|^@aD` D` D` Dx~MMZqwdXΝ{QtP$XbdddLMM=<<֯_(V uuuK.urr۷o_EE]ĉSN577WQQp8g~p;iiil6[UU7nvӦM366fXiAAAdd1[0Vdee+ر#00PUUUv_QC xӶe֮];l0--- Vt ZrrrddyZZ7|#p)Seb >}>hРDsss-8;;[ZZ^t)++KGG'33ؘBMMmȑW^иq̙jjjZXX/)##ӿw:88uwqvv~CFFFffիWjg ()((?~ӧ s9dju͛g{{ӧOΝ;t^w] ??С.]Zdɞ={;6aiЙ!WsBɼEQᕕ x{{%l6;??.lllo355˗3D`~d֭`qYt)!$!!AZ[[;;; 3EԨϞ=Cn}6!ˋxJmm톆A2yg@e~2_~<EKK@ڡt&wΘ1ԩS wSSS/:u*!MֻwoY??^z`{fӅmzzzjiiw0|^|IRUU%qzƍ?}}}555{{/ըUUUK,aٚuuu ,p83gά--MYYɉ.7n!ڵk C)YB__ɓ 555O:啝-79i###BHkGPUUY]]}젠 *A#,++4((.)..۷Ν;CCC%%;v |wy޽իWO4a  2fsrrzׯo֬Y_͛nnnYYYԭdk׮:k.BHSSa$''TIFFFqqqXXClELL !$88XRn#"^Q>=zdvn///Օ(..޺uU0.xgT:::^A0~ MGXSSCqwwGџ>VknnWWW/** 555VZUTTc###yy<&3TVV?pۛ!B&M$Qrrp1cB.]Dlٲ455ikk;99/_N{ݿ_vrrRTT+qٲeVw _ xڄ:B zg5$H '@P\/ܜ={,22NEgffn޼ZիiӦ .ꫯ:ŵk׬_zPZZ믿R 0lAg0F^:ydb{gi~|Ç%T|}}Ҟ={F$''[YY 0aֽl<055^-xEq8)c! H;'MFe";,55U.xg[YY!?ŋ-,,<<#nŋ~ՋhgΜbXvvv9ZfM}}@Kr_SllѣG.\xUVI- ̈00&rɻ\s֬Ychh8ovXmm &%%w+Y+99bK𳴴y&k7(E-cii)Vlg{͚5FFFƨnݺ,'+RaH]Dہ!PZZZ^^NAvZ@@Cdd:thFFƟI\B1338/_|ꕦ&UBM̰V4&DBHJJ )ɓ'ׯ_5jU&PGNNn„ qqq/_LJJ4hXPDBvZII A+'ZYYIa/{''k׮]vs;w0.xg@σ@qX]ٳ !Φ&ƺcvaaÆ566Aݻ*gBOtww/)) =|XG1gBWsscDŽ777o޼9''G=ztEKKK:%JR ADH'vϗ=Uaƍ *::Z[[LJqbX'@Š"##UVVRY )))֭OjkkOxccc??gϖ899G(&lצM.\dɒ#FYXX2ٽS;88x{{]paܹb-E2};vܹ|p5^zm޼H:YJ,CBrrr>c?{ & eQDPE( yM,Il`,1v#E {&c^w ///syf w!ÇJJJ=.FBmm߽{_ɓ/^عsedgtä+SC_PձcGoo+VTWW7UU͛ӦMڵCXXX~~>_¿-[ꪡѥKoR0NQFYYYve޼y%%%g\CCÙ3gƏU__/VZϴ3fX[[M4럏_ɓ'B8qVWW _{{{wwwr免2uu'۾aNNNuIF?|ܸqbq@*yKqqqFmߟ9|3gnٲ{߾}{hhu&۶m9sس%&&:u ]:.HEPP!$..)>*ߵooo3a##Xf߾}7mիW$y |4pD+EQEEE^^^ÇI0***aaa֭[jH޼yΝ;-ڵk '|\&OPSScoo?wE0%K,YD]\\0FHMMuV ?+++[%p… 7'>zؚBO_VVCh>&ؚB<` D` D` DEQ-Eqqի{EQTS}-[L[[E#iig9_;Hg9t#$@EE:Mewefwsskt޴4Ξ=+>h[ !oڴiB}a)m۶h"Ȩ( UUU7o?~LD[lb‰=[_DW<<׋Ӳ!'"!HI%HZd333E$**ѝܫWkJ @P>ɓqqq:::4Ï=j.e˖EGG VػwׯCCC[4֡bbb|bAR ML4u_^%6y4v1MDME xGGG '6DGG7S MNN~qLHxMÛԦ(s0XkHO,//1c… _JJJ***~GM,\6RRRQ\=<Y,S&Ȋ =迵{yQ&cmhx(2Z~uqTTMaaFJ޾}jժ}hhh8;;Ϟ=]r̙666fffƍ+..fr덌>3#""(b=o{K***h lj;`f j#Z6._$ 999 =tP>}߿W-$$ٳgC yݑ#GΝ;g vܙho߾s.3{nnwBBY(yy#C޿xjaR.vv궶l4ꫧ?dܺgWwg^*uKFQGGƆ7b BYG>, M78;+W>{СE=zourr4hŋlْ|cccʍuuuKMM._Ǔ2߾}rGqppҥ 8e,988oƚ5<ɭ3 (-/1bW$`Sr&3n3Ӧ%O&XRHN.tÇ_ޭd``b `3XF&[͛7gCQSS'TUU oJd]]!]' oA[Θqos:ї%[[ 歓lm}Ʀ+.}S*-~m2SUP@O^ڀM 62;ҥw;cF͛MUD 2sdk wyWZ+vڈ#8طo_:Bv %%3%7uԩEѓ666Q_XRRB5>͍-++K6nH rKG1산fyS\(dEqsB,MHH`FӶlB?>=Y]]_PugXaAffKT>Qc qppmkkk9ntyyy:wTdĄ[^^ vkM(e*t)-(͹s fGs""6TWTSlPRNYkvWx`NN&?z Y?fμ1ls;q[LLſnnkr7ri}&5kƌҧOVllCmm+z>}ZZZӧOgtVUUW]ƆD#_޽{$ѻ^B,XI3FNN.>>^.xG1DBa2PNM>YBȑ#C!̡Ұaø\3RM߿Oas4QQQm>bCCCjjjM4:u}6/W~=XÁݸQ&(]^Jﳳbӧ|u<<uu_>q8z΄gt\\׬Q {ƺuF<߲?}k-M_MqS^l*SSC[3%K444Æ KܹtRMƙUUULJÇ|uOy5kl;B_BH'L7 ]v%ғ=RVV|˗:::Q{jjj!CNTUU_|)jrrrM mBV!rrrt%dee)))ٓ) dgg'OXYY τ| ʐq>1""dƍ&&&FrssIHʧOvܙʦ Ԣ/ sssQ$ϟ+0DBțsDjAҲ*/..tIݻ|u(yy#/#Gj߾-8}ZJC~ !J&<َ=zԕ_ȍo DT3;V'Or>ӧfffL2r1Zy͛7ǰB70lذ{$&&CF*999Ս^!!Qv!0G (cccc``B~Fg".KIe4'3RM7 XZZnذ2ǟSNBOz/{&&&٣S$xaeu~~Ň+22ӑ# !R7ɂՌgo^'4EQGH袓IҔ`E.qy>%>$(\g.FboeN{lРAW^k, ֔Gg&E cʕ#GLOO;w̙3Eeoow^1%E={ /]{rH-PN>''IΝW$(hiO5ÁZuLG䄇wO^CaժÇBݸW;)IGRNɍ9sݎw=}Ÿ38̙3_}թSʢv)R .\1cFVVV\\\n~WBo5EEÇq8^zG=슽&@ _~}5C b(QhQ޽o6UYYI?X$s%ܹݻwL'=rф_@QԘ1cDmYlkH6w}E<y{{{{{7`:t萐 jS(?~hhĹ$E.x;` /yU.˗3oΞ%vWqjիEP1y$>!%!]VVZ… .\((%dfkTC>}Xdɒ%K]\\,55U*1 iQVVV+$$$8;;\ТOiʒuЂd'EDDP^[[+X@O);rHLLmJJʷ~+p@Oi̜2e{BBB֭[:(@j;uL4 7?(777򔔔 j*FPK)r+Vpqq0`@||#6Ds>}ĉ:t (**&&&::h='Ol4{zz;w9,,ԩS4mڴHyyqeeeo dOAGĉݻwg Oٳ.ڬ~~~Ǐ(W^ ,;vY QQQ}+F2ӱpB%%ŋر|L2غI =T}qpFJm>oO?UzvBOUK#Ƚ;>b_, 4\ch{{/ݻ !-(ZTTt1G '\|9666))IEE3g(**fff9sF[[/Ir,Yѳ!yw|fmi44G:LK~k+Co(BFՖ^Zji6ԖO<=1IΦlMIIQUUuwwgJxׯU(5%%EDO:uĈ |5''C[nKTTT6oHAAaڵ9996l055Ŏ> ի 64jnn۷o͛7̘1#33㔖چڐ^!t"biJzBt229J.T xZyBx^7(`d,++#퓚ZZo޾Ezn=3W'R 366{n]]Syyyty  SV7n4115j!)55={+\zbmmʡ4{}gI. BU]U/?.|\ϭW^\~B_~}KΞ=KhHX@ 6 XZZnذח2qDBHBB=611ҿ.}n1(WD$Mzu}`#:jޥB*k+ƵdȍתU\.!866V__С))@>ZkPPPJJŔ)S,Фl g^S=9fs}t\q߽}|5[mmix_c!~616 OCBB<<<̙ŋիW+))r$`3Cu5)))>>cǎG^lEQKv ߺ;oݺ}J*5|J)|Vz}R%B K\f컿/-/mDkƧǷ2c++?c߾}zZre```5vvv E_L5jo &iZMbbb@@S "]0wU###///YDߵkׂpG5Ov_jjCV----ڥkkke |BOh9ckk: ͠((999++AO555"PYY9w\www###={2?lkkfcc3q/^pWZ%j effZZZyyy|L9rDUUu[-//-/g 'WO2dSRZZp233šf[hرc !;v'_zehhYQQ ^ܹs/_rw6008v]]l .TRR"0K'O&_)ٰa!$$$DԦ#i#6c {1%2 >J% ;vɓ'SSSY΢[LaFFEE1^Զcǎ˗s8PQQ"//@O'$$oDB ,gΜ9k֬nݺCm~7:^BH`` obh8q{Lɓ'>|(|"lܸqd[طo!dŔ9sFQQ100033s˖-Ҿ` ͵͂ݛrmHnذao~olB #G$=z)9r0΂طhh WniiݳgODY폹9!DĔ^xÇSLsaܹ7e$''C[nKTTT6o[Gx 233,--eR׮]#~e b8v7|ӹs'OTTTxxxlذAHo 1>yߝ%K1BZ]x? #R;Ϟ=c}<<< VWW쬩jjjjkk|ٛ7o677WTT`B"!d,[`'oBmB0`}||kP IDAT BߐQHwhpDhSpD!\ikkr_zSXX]"##֬YC?nV__ѣG/_b֬Y_ummYЬhUUiӦ7~2!D^^e ¿ٴ0eʔ 0)4iRzzwSm!ׯw۷yyyu떛+X9???))IZ]@OBD1LLL̙q;wcǎ~iΜ9FFF޴'!:;-4֭[~~~/_rJHH?@> (n|MҖ[ZZe˖:-X ''yCOB,--nɓIIIf߰a_}##,4_YYҥˉ'I!'R"ܼM6-$q) ɏ1bΜ9Ν>}z "ۿ?!K LMMׯ>>#F]\gaa̲fYXX>x௿222ڲe [MI&1gff2tZYW믿ccc{}ر|;;6@x5jT ]Caͻt_.X@IIњ)))|iii۷o~znnnUUu~"##y?$%%=|cǎ~~~˖-c^¦\ĉ/\;;;WWe˖n Ŷ`وpϞ=qFAA%Ktttwaܸqc2@'2TBJLL 8u\}'@"Oz$Q!յkׂp?hփ^:O񳲲^!!!ٹUba[w'/++K!a[w"|"|"|"@{l2mm튊 Y"E:OEQ-xZZEQgϞmijcݽ{i{$k>q'@3%999++AO555"PYY9w\www###={2?lkkfcc3q/^pWZž 66m4zh \+\\\ttt /RUUUyǫ0 !>윜YGь-ܫWk׊~AAʕ+{ꥮt…hĨTEEE}vssرeh$*****JJ0H.hee%b]Մŧ}KI6DhYdddKJJ/^gddl֭***SLyɓW_}dggj``wnݺu{zz;w9,,ԩS`#<<<..NHnhڴiƍ ط/]{}uhh͛72>|ѣG-~hhhrrěo]vԩS>|(Zytt4_Ó&M277wrrZb녴211n)yegg'$$P|]Մŧ}KIvqF8)> [Z8|0_ !ՕEo5|"pFQVVֱcH1>o6VЦ*@d_/^$,_\6WXAټysSLMM93奨XSSygeeI؅qwwWUUeJn޼I?]H>mx{ƌ )S̞=>KTUUյkWBܹs{A MFu,EA^~4iҤݻSeggǛ~Çwtt3gɓ+ +//!Æ u^ &M7\.]޿TʶãG̚(dc~Xr%!… "EnUUQHI7Lmm-Ԟsի=b$>}:!x8,\?==޽{ütUX W`7-}捊ʰaÄ'3+Y>QOfוx+Dhgg̘accC F,l  dDW;}giӦNQQQTTTDDD=L"xۄ)S0%;wظq#sݍʯ*v5kXZZr5kDDDp\BرcY6%HN4#ҹs!})++c ?l{T\]]_Pu]2W\IOnذTPSS+((`J "h6Ȃk}K,PQQϏ9Ӟ>?S*'6 !Jwk2Ke[\MeXlbBbccWkСCErٳg!$44ӧ"ӏ^f ZJJ !`Ϟ=ܹ3wpp̬=t萋ec[(&H6oӧ55(ZL>O"f_t'O66{, H;;{把:hVJBȀ7+H+++S 2o߾Fgsi/… |%=y]BСC :::޽$?/ BF!ȑ#GĈPOСC !S999޵*mr=4jvcԯsl٢kll̼+$!_~8[wkkkݙ , :u}6̼gff6122̬f^>$"'+((y&!!!&&fѢENR4kɒ% ǰa;wWE/}9z[S1}իWtQ 7ҩS':|ǎsҥ)))wի3!{Ͷ 833I){9rd֬Y IHzz_|oiiiBv9lذQNNn[k!ldׯ_Ϟ=7o޼`ދF[-A3vvv ڹs' 13YYYB臄888 gKp bؿ?!K LMMׯKGe6449sf޽{ǗPPP%KѬO1!BȱcċD퓒.^8`f+ܶhˍ&:hoo/RO>}ⅅ{5]O8$ؼx͛^ Æ ۻwoIIIbbuƏ;{ر?۷;vܵkW['7-!믿7n'P?ZB<}B"\7nܠ(***J YyFa3ѣeR ___Yn߾Mcbbc!Cp8Ǐd`mm-}uOYv!9:UV##cccf!t…N;w.!dΝm.i'N=z4!_o@QԘ1c~iޡw!hkkU311IOOf2˥hOnn'OG:tH0d+""SPP~%EXn,61O8p !$&&3khh~w㏂I^\uuu`5k֤tq d);!M Yi !AAAmZ(o>!L )~ YKbӂ_Δ<!cfB̖,Y2eݺu{i5rTT€f͚5qD#̝SSN Bɓ!킍B IBHH!m('ܵdB֭[}yԩSÿKaɓyg܎|%tcƌoTTTTTTn޼Ƅ !}ݰaCS !d) 噶رc&OrɄNJQPP)RyTTT̘1lҤI4<:Spj۲j۶m={tqq_kjj7VHkh4HoZZIIj־,d.»~!dܸqƆ|"pF}0B?&V LXBetOhxg6L>sҎvsf0؟*kQ,3}'y,҄ "##cccY ۷o֭Svv6sxB31٣d,ܽ{yU{1w_~eݺuvzbjkիFFF >Y\7n]EFFFFFJس`K(Wor=vV JƐOhTTT֭[j*uuuY 7o޹s1h;뫤JQTbbbQQEm'**y0 >mk%Y,kׂ[bS@IFxsqqq9e@zꄄ{{{E)**:.hu\;矻oܸQցH}'͸8gee%BBB -\~n&|RSSeBA>>~YYY37@pF&'@>B>ZEQ-Eqqի{EQ}-[L[[B*--g:B>܄ٶmۢEttt"##襪jǏWWW횢(999++ IDATAO555"5[YY9w\www###={2?lkkfcc3q/^pWZž 66m4zh 6]b΀EjYbUlA@KKK===// qvvիڵkEm Ï=j.e˖EGG޽{_~Wnffٿŋ-++ۺu)S?>yd&W_%%%مݻ[n|]Νsvv stt=00Pѽ{Ҳ_~3g\`tˣm^rE5E˖-/!4OZjhѢ}V9]jjTaffֵk*וSq'j';x`HHH֭Gqҥ*̝;}vvvyuGWpرc۴iӱcǭ[/>r߿_q*+ToBdeeu +WH\Kk.}T?ny";d9rsMn9rdXXXVV7.....ͭ)Ç?>222;;[ZoB777*r!MWkɓ' !Q7bU.jѢoo.d:O-ZZ,8r䈾fZ{Zx{{_W޻wo!DNNvg??pɳ)_t#~qƼy󼼼 a۶moݺt۷oz8aVV[TWT%t۶m}||>#wѓ&MڻwѣGebnn޲eK !._,kݺjرc[jjټy;wVXjОkŖ˖-=j^݄j-@PPPfܴ~z…~K/''''44/7xCW} ̙3VZdbݺu//P1!!ݽʻ"#ǹs'.Mnܸ~[n:u***J ˴?~իQQQ'xbÆ ϗZ7o:(..vrr277_zu~~ڵk]]]v}Mdwu #-//֭[/Bk׮iw~ ϝ;ׯ_?SF ::#G{ݻU/ƍmmm²>Ӽys!zBBtk>'&88xϞ=;w`III.Ձ.Eoy7mڔc]:BhQdE·TIqimnn"88XݻkJ1JuS'N!{9n^\ O <;wC ϟW )..[yy/JW_rnaaqU{Ʀ{JQq{ !,r5]]Iم V7s.`ʸDaaa1bҋ !zmeݺuBիWkRwcEUTWWZ%]vb޼y7Pc:v3f̐9$..nݻw777?)]jm׮$͛7߲eZСCXXXue'g]":q3@G;v^wرEF33n&kV-Z1cF^&O"h֬vgooo!ٳg(55UAxQtt[!Ļ+Xh2qD33]v(B[ҥK:,t*%!!aݺu?s@@ʕ+ MEE̙3mmmTPPũ"if֬Yyyy>qqqRZUY:ggg|;di߾ٿB5kK:xsέZy k)Ry4) UQQNܹs;t0lذ$ :6o޼rʹsƪ;99]|944T"}iS?O:svvuBIwTΔwuu#ꩧ駟-Z}ڵ9s|嗪T]AN ؟>}*=====]pE 99\TTk׮;wNMM+޸qQ;w +VB̞=Ç ,HLL풝f͚z/izWe/FGҥKRXYYy%b(M:v옓T*knwWoȐ!{UoLMM={ѣJ!{o˙>O8Q^^nnoW^>쮮a9:ug[3U~U6P# 痕%$$~ 0~~~ӡ@q3@{I*B;w޽{O>077cǧƨO yWNB9r$"""((hǎ풞={ !H~!DOQOWҵiGmۄC^$>>>4 wwwo۶myyP(***TokxrYcFȿɓ'-,, TFoBTVVJNXXkpqq-++{|SHM|۷o:thPPƢ6mT KX~}EE*9r_]ݦM&HII8~g}&0`)O7ߔ޸qcӦM=vgB4wu#ȡ~W_}k׮`ힵz?jYxGG3gΨ"ÇBݘQZ.../~ܸ oqg=z5::Zcw!Gۺ0`mۆ 2zh++Yfiu9>ƢSNeeeuu#H!o[߾}}||JKKSRRzjÐ!Cnܸ1fcܮ]c߿ 88x2#22R://Ov֭BnݺEGGoٲ%$$޽[RR)jejU^^ޮ]#FpٳgO6Mû.u.^oDDD!lmmO&յoB6mڨ/>_xxxxx7*嗖mڴI/-%%E7oÆ 5@F</;v0`m׮]_~\>5Xdٳݻg<"/- uY;OT(NNN7n-4MrRbĈZPܸqGw޽zԋf7+3&X(J??֭[gdd5zw]WW1clݺUA,,,:t萛E_IlڴI/UD//4)ܹS`f͚-j_`"==]]k.cY/"^TOû:S˿k͑5JA7+wnVڅ~ZJ/ظq?\'#a>ǴGJJ/[VZ:;;sΜ9F7FIIܹs۷ooggt֬Y^^^&M*))$IIISN~zjUS0"}]&@17mڔڼysE-ZV P_E/9?Ȼ>dgg ! ! :sW(ʧߍU*iii'OT*2W<}ҥK{9l0;;>`vvv}#G޽{w}?zh~~#FhO /lܸ]vC>zhLLLttF7ٟ2=|ɓ@CDDDDDLBعs'Y:1~x1c 6L{iqqի׮]*/Hgzׅ }Gm:?h~w=s sBC:naf!xcuܹsB///E#nۧ[$_װ瞓3a>o?cZZZqqqۧOwww{yyvIղre˖} =srrBCC?naa!x};v8zhllƍ=8p,ƍ铚Qg}WC_Ns'JڶmߪhČ'{ 333,|ȑ#;w0aB]W2r7o޼:8;;ߺuK(ܼySb Z5VVͬT-]!j;vnռU}R(&|ſte]۷oĈ k׮3gΔ LHHxFΝ;W9EqqG;w'??ۻM6:T/#GT޳g3SrĆ'$$>}СC{ܹs>}vjff&˗WVV~?rq;'J蝝mڴ@dAΝ; _}Uߎ;]Ogp¾}&O\M'uEKKWVV8p ##<'JψPn:Ui֬Yo>3 ƍ6mrttK5 N8-Ƿtsfma]p %7E!N\RrǑ'u E``vfHaaa_;P(d2$99ٳrO?NGFF:tO?/Bϋ/gϞQFlRjܳgS؟'JsssY\nݺEGGoٲ%$$޽[RRR]kr>uw]LzÝgϞBg}VcQ||"??_asZXX\xLZɁ#G,**O^L8߰aÙ3g~t틚8k֬7ve'NHOO4s)KK.]趺\EШ)ܹS-+Vٳ}Ux9Pû:S˿Ow*2׭J*jsb2?B!ɓuD6o2eJ֭{WQQAE]iiYڷo)%zC 薒2hР'x.00p}ʹi3" @O)]4B;w 1ZBxW_}U8(N^l,lіSIDATn։BXpallɓ'v_se~BI&0lB UoԮj7lؠz_ !40Nڟ۷o/--}饗3畏D4>ux -S:99^?899 4؅ٕ+WT=zk.!FF)Gڟx]o͛~aqq #Ol7o>gΜO>صKFFML[:thgΜOl߾}РAF8 h;v/r=Yxqfbbb\\\ |P1Y|ˍ]E}=z%ӧ<|_\d4٫W/\a=a„ƍ<h /^xbcWPyzzzzz w y"E!?B~葱k)"O`޽{O=TffҥK]My"!///**^f͗_~ihr4eeevvv_} yec@Bxϝ;gϞ'xBaaag?|Є'0uׯ_wqqٳgOnT{1bĩSXM Zi&..N#>>Фq~"E@.Dr'<\"O y"E@.Dr'<\"O y"E@.D@) cW5' ޛ0aBv EPPS S@#fn Ν;߿Ê333ܹ[Cz|iPF<PPuĵv0Ƿ` 5hĸP/BFP`͔O竕MaOhsss3v-QLn@\ o||b߾}. Ry-U1#O4 5,'65ΎƮ@Ch JK6fee}; <Szcz@y"1N΄wwR9<pD0 3c(kdLB 2vONu~_~~[EFF^nݺUf0f)SCÎ;.\l2U(pss{g5?~z:=|pNNDFLQnn+W  VVM^T-666Gަ()S]zjR)P*]1eOOϕ+W*,,<|pZuڵQT !.]w]zcyyyk5jԨQO?i>nݺIͷo>f̘>} 0'//{饗]ǎuq+++9^z[n2{=B$''_EvvvC]hQ۶m;RHRr/ӝ:u=z%K4ֺz믿~3gδm۶O>K.ҥ4~``و?W_}tҕ+Wu+$''7&<y"'qDr'<\"O y"E@.Dr'F%((HP "OMB 2v@fniΝ7v@Ec4f\ L¶mߺu:޾}[hƌfffiiiBGGǧ~ÇR_|P1z{VVVuԊ 7n NLL<|pNNNͅ~嗖-[ !f̘QZZ'XZZJ# ! 4Ɨ !bbb m@P*Ʈ46Bd9:4`C&'' !֯_?իWKR}FEE%&&nٲ%&&F}BVrrqHo`Rٻ+Xh& !&Nhffk.U޽{/^x͉ .ׯ… S.дq30ӧO[YY.^-!!o裏!`jfaasϝ8q߿]v wF޶mrn?o~$cU 4q|||㏴6}޽{ !O0a̘1BoozkΜ9ߖ/_. /Wz!0`mۆ 2zh++YffFCq'B$''_O?MJJ:uԽ{<== թS7ovڵԩS[:+!C:t(### @Q5¹s禧KMMrrqLH45@t<&'<\"O y"E@.Dr'<\.4N/^LLL4v O?dM駟bcc]=S(Jca"O y"0;BIENDB`gcc-python-plugin-0.17/docs/sample-html-error-report.png000066400000000000000000002163061342215241600233410ustar00rootroot00000000000000PNG  IHDRiCCPICC profilexڭQbqBH66!YlHR3c2{// kXRKtZ `?ی&"BNu˹;~<XQ-:ݞ88 x0?\p&>Ow`ql,(N-x nmz2 ((q b@~μP=%5( gqj(啥Mר2 0DѠI&2UjeVXb0#PWc?3>8vp!Mك4Y$g݄ 5M?~:]8ugPzoNІopeiȠU/&x6@L^ܖj,Edd}y6l#QbB3wR)Qj7#3p (Ph#.1`cbKGD pHYs   IDATx}\? " )ff%[o,xWdiZOf,|t9uNѯ! *ސ" +μۉ]v|?{\s5C溙K "c1cDUc18d1cp2c1'c1〓1c}еkWh4?& 'N <<...Mll,yHw c1؝8 0 (--ŕ+WA$G8u[$Iltc1kDg6jEQjW]v)5gv;v.仒1c-J|a4Vk?OSZ@cccTɓjiӦ:燜}Q ,X7nLeٳ|>|8֯_"VEZZdYQ ܹ3Zn0g1XFu "Fsjj*IDDD&"͛G*{G&L DDD PF$I|)ڸq#jzd2YlYzIE6mITk~F"`0(Y… IEzשܤ1c:.K[j4ϲ,(*(Z;""vB۶mQ^^0?(B& on}4ʹL&TVVBej ,ռ}aa!:wAVgk:uꄋ/"++ aaaeE _`1X%uG^EӧOJewҥK T>W nNA puu|W^mЉSEEE;Ze1kqyddd8p_Vȧ%*//4ȟ1cMULr$ΝZX[K.7љ||}}j*eQ`` A@AAA~c18lë<F;wRׯO>a„ Jٹsgm۶̙35FNؿ?z=yܸq˖-S'x `Ŋ(++;1c-+_УGH`̝;=YYY$Iڵ+BBB Iz쉓'Ob9r$bcc+W*AWS 6'LGԩSs={6Ξ=}ʔ)ʶ* }5k0i$J … Ѻuk̟?[nU&M6 ,c@\\1c j gVx;w]v-*Pg1X,u",JR,ӫ+T*YMm$Iy\nA@U'1cEvֈ1c^*hUwbc1p g[8c18d1cww3c18c11cqc18d1cp2c1'c1k}еkWh4?&<66VYه1c3::^R\r111 pBȲ|`tCc1NAVt(jExxx[nX:r׮]JYKff&*++l~1c-.J& Ÿcǎ'~mmڴ},?aaaDDD$IB\\N< 777$$$W^j-6mBXXݻw+y;Çc "hZAeȲ;v`hӦ  .(eggcĈNSV~1c-Ձ(d4ϩ$IL&|2ѪUhd2ѣFǏ"zd2) cdggӄ ŋty1b jNc1qm'|+WÇQYYiD^p/+WPPP `@^߯gJ²e믿Rf#FAAAHc1kat) 'A( ^~e\B>>>J:iZ0`5A^zd~ 4l02L$2}ԥKɓ'+۷ڵkG{0wN'N YiÆ J]v+3cXNDa  wwweJZt)EDDV%QIV+3G^^^It1ef~1c"R^ /IgJտ'"HT#JAeޮ1}5n|L6hfV7oާjYlc1֒H1c9JUc18d1cp2c1'c1〓1cqc11cNc1'c1cp2cp۷]vFc2OUff&*++LU}o!֮]x`^weQΞS'VX"g}YH;rKt}[2BVcСơCSO 񝹥dӦM {nXd2!66έPΪٳѩS' %%b=^^^  :vJ̟?:& ,cǎ?~<ڴiDž q"""@C ܹ3Zn0HiӦ:燜׃jf5`U tJP)2f08ydhZVl1>>>t$I6ϩ뚿_$@Vo܏5E#11ڵC֭ѩS',Zuc1V+Qh4… I$%d2(n::z(%$$PEEEL&eggӄ ŋty1bwI oFW^%Q驧]|DQZ kŋ)??_ *H*--U%_kAMPP,}ee%ݾ}[vWpԵkH$j۶-Qee% ުnݺ57xzsv<_v^c8zu:7nPII H5c1` Hͫb!dHu򹬬> n>̼ UTTh$Q)((DQ$WWWΊ `LJ(t`Ã͛77>׷.e4g< @*ڷoOwVxi#W{cbbHڼy҂?1-ui0`gdY+pvS3ʧ:88lhΫFEs4f]^rڵk1`\Yf94a_:nk>'OFJJ ?IN2k^$1?^{5ƉWHHHh2!z`Z~ Dd3R8RւFL])]t> jyyy zuܘ 77=>#~ömеk{1ƚh eee8~D/+WPPP `>, &@|7غu+dYFRRR򕗗cӦM>}YT*,[ +*++qZ' Xbʜ:N߿z^uq'1 DFF*q}Y+:nk0j(xVP<_x&:cq}߾}ԭ[7b(ԡC24dwNG%Y~GD$2mذBCCk׮~ze<}bbbġrz]v@)))TVV'''}G>>>@ͣJӧ @SNcee%-]"""HՒ(Vh4Rbb~IB4w\ܹ3yxx(ԟ{9RTRRR(//Ooaa  www[[]*S}Tsܸq裏*d2Q~ˋ<==)::;בsu$Eΐe٢|i1ZyW_kU-%oU!9SΥ.iΫgZ]cdxw=VjI]^>{=̝;_} Ap < DNNr,[c1fKWZzm>wVCRֺ `u\k;ke] Ф[uđ}󃯯/㑛 "r(ݑ O@Ѡm۶HHH: 6 22cƌ3g,L!bTȑ#!IR裏WWWdeeax2c1brss(L:U xc2a0w^DGG#99쫯’%K[oaڴi0a[ު+SNթL0dY@LL A;1c-#ܻw/Oi3gDvvyyyt+رc{i4e|t:%eo*y 7c1l8L&L2ظqEpH#c8EQ '|wƶm0qDmpq%~gt?y0cݳ2{ȶHII#>Ϝ9~~~Cvvs9e/;wbϞ=x뭷,;m۶EBBΟ?Ա7l؀H3gΜg+25|PG}Gʂ+z n/97nKJJ@@6m0zhرCiݹs'F___@vv6z̀BVVEK˺uyf:tD5kX֥Ipa9r* K.U_d dYƞ={pi"99F(*qt( 99#GĴipAfϞQFСCGbb"RSS+{Gw_箿hԩS/ŋ8|0BBBh"9s/x \|bHļyp}ɓϱrJ*w=C8|0p2XSmjӦMԷo_Z+ƍoIlu4 ':{ˋ|޽^{a "I#͑:t@.O@@M2 ݻwS\\rƍ]v)-,T\\lCiEN=Jǭ[,փZPiHZVٮUVT^^TqC*((y<@>>>裏Zty+S8ӧ)11Ã\\\@jP^wj,jZnnnp2]Pd”)S퍍7BYh{ΩIt\GW%ޱyy@t4`٥nNZEF x'{nl۶ 'N&,, ǏWN80:,,,T]\\(']t.]RB JKK.Iܸq㮵w8x ^u㡇p5\zmժpuٳg-_U :cP`fͲׯ222p-\xK.EϞ=  |dfEE@y9r%0v82ذ<KO111Iz8\k~G8p nݚ)&&8`Q_u6̤^]ɓ8}l۶M}ĉu lٳgiРAԺuk /b lڵ4ecwCK[zY ˲ҪP5ɉ~'PH?g-ݑoݺ~۶mCPPPw{711)))xGjԨZԡu Q\\\<,+[{կ PWQc|?GԩSgyƩ'oo><ձ$I7nqajӧccMK?f懓{Z]@Q H9jUHZŋ/p裏pM 0깛Z݈hQՃ˗/JW l8zV>{#~t_oo>:quVXj&N#Wnn..^ÇscM5W5  *I;vO?͛77DXxyy;{=[.]4J׿t:x7#99٩zx[oA/cuKv{:d2mU2T5.zCa18c1X դn݊Ph4h4Ec1X 8O>x͵+WյNٳ֭[6c1kgrr2F\ ** SN1&??׿`4T/bĈpuu`1cph $I+WXG1p@ܾ}Qp&c1X PdW|[la,f|@HHW_c1vU[*o~m^} ߿?1c^ 8eYƢE>>>Jٳ1|ѱcGj {VNcԓ3gcY-־EW^oǏ׹1c5W_Epp0_jpAnݺ___ܺu' 1ckVEeeen[-umγc1Z";^o}ga}FI8d1kA#՟ڶ|gu~q>~~~E||}:|||ЦM̜9ÿ:&5E2w gVZ늊eYPP///>KèQ0c ,_\ A7|x!!!HOOǍ7,_[tYYRSSѡC!55oV+**+SNth6_PPpwuD1cƀ,ڎ{IL4 !!!/8]~g1ƚqV-Vڲe pݼy_|w<())iRRRLڴiѣGcǎJsN5 l* ** YYYӺuyf:tD5k8%Ipa9r* K.U_d dYƞ={pi"99F(*qHNNȑ#1m4>>8v-޽wF???:Nɿ1j X߬[?qY 4=z8u~o+c149L&L2ظqEpc~W HL&4McOl 'ݻm6L8b0?~\ O80:2bDEE9Lu.]UZZX5Ƅ6';vDzz:<gΜZߏ3cNӧ???">>J|[FZI)m۶EBBΟ?T6l@dd$ƌ3gX3Xȑ#ʏ>YYYpuuEVVV^m59>c1Zx#F 77999ԩScXd V^g">>/rY)d2A`0`޽FrrW_}%K୷´i0a|u sySN!C`͚5(..FFFbbb59>c1Zxw^L>>>>hӦ fΜl%xxj*~~~xW_7W%֭fϞ (**EgAA*?KèQ0c ,_⽘| x ==RU[Y!55:t@PPRSSqm% :uN-Z6D1cƀ,o_UTTgϞx#G`ҤI k#w3c4T3lقaÆ)-\9998q~駟Fqqqdff"00ЦM=;vPȝ;wbԨQdggw (deeYC֭͛q!֬Yclna&==$8rT*.]dȲ={ӈErrrEQTc0jM=p1rHL6 8o Ξ=ݻ###C A.]'h`1k]hӦMԷo_z򝗗ӕ+WҥKNO?tYfr4e*,,TwMqqq7vڥ,n^'eFQɓ4tPcmip:{ŋ)<<\ٿgϞTPPKpގ$I_e$eBm;?Qիd4IE:wFh4c1ȱ,Xh_GIj oooTt֭Y׫:٥;7cbb[n.]^N< //:"`e{&U{i4e|t:%x_h<Ahw;?sKBט_c1V8‘L&Looolܸ"P+96ySb)bM<ؽ{7mۆ'ZlǏ+Љ'fQGP\\(:ҥKJW`@iiR5Ƅ6'Ώ1c@`fͲ$''w p5;kQK4N4 {ESOYی3l2l߾۷oGzz:RSS-Yh^] r8LJJBff&P^^+Wbر{ ˗/GAA;1>$${hą x:są Э[7~c19F*bcx,Ӓ%K[nԶm[4i5qm|())HYiݺuԳgO+Fjժ}A;w7|fX+Kyy9QNۛLǎS<Ӝ9s(88iذa/(f律^z5[777Pw|{W5/[^~=M<ٌf1%-mikoNYADʚHHLLDJJ yD dZVֺ(..QY-^Tҫ#VֿNh<3-e1k $H0֜ί֭͛71`]pm_|Dddm+cc֟#ǯU~kL1置cǎ?~<6o(Ybccpdddp ]Xme1gK]}P[Pi &ɡĺ$If栉1k>L&@CNu$qb5ՓD G1XDw8mk H6.yG-~v؁G}Cŗ_~qaǎزe 51=>SN!..h׮qXOs\Z}mnu V^ WW;Z?U;t^xW\qjG|7\?\y 3ev,xa2xbӧ[g/)3cpV1}tڵ Xv-lق^yѶ90 شicǎm-8IU?F=/^\$&&"33'Ov;]?& `ڵJLsN 66=z_uA&cСΝ;1X{AYYCۧ<0Fyy9^z%xxx`}RX=9r ___`8uE`jD(-p9s&AC@DӟJy= 777$Μ9 "9iiiؾ}Rz^w5055j:5իXr%dY!yf̘mbΜ9 CNNNsʲhU]> FC{KKKߟ<==f),w 撗 з~Kzrm$3$"Qhh(͜9_NDDڷoOt :/zurGE]F!!!TVVV=z+" IDAT AnܸA(Rdd$L&z饗GGGS~d2QϞ=)--MgNGڵk.:m4צDQ$g4k5cQNX>Ü9sRaXl۶ EEE=z4ڴisO}BCCzj1Xu>?ζzdggcŊʵrssԩSa0yf|7(,,ij> wwfQ?Dooo 4_|,W:vL&;wDgϞENNN>l"//r<==TVZ)fC=2cMk4l…7\#y'O>??3^}UJ>aaaEaa!|||,#G_~xgpmFxzz;= M6q_n+ իW׸.%%%C֭QXXc5O7-\z`? MLL?JKK AYc9PCQQ\~aZ) ƍ޽{W_!..;wn6cַo_$uʕ+@bb"$IB= ">3$$$ !!6m wﮔ}/_SK+W@DJ>]v wxc1"/DVg!:th/>;ϯ z聡Cx'.]ۣW^իDKݞHk׮^w61=  ^|EZ ?vw}jZh46l """,Z!V'Z8GkdݨСC1ZbsPP9u ld/elV[E՝gTwet ڵùshj=R?Oe%ЫAxy]S}[b6lnjfݛ?}v^xk׮1|p0cMÏ.G_+9KԶݝ/G6뒆 bq. qԧ~ljɬFZkk^6kP|Axxkm_`ȑV[0{+b1ƚ;[85IC*///8{o2e׭~'xs+#Ǝ[_ƍg5ʿ#18䀳E1L}$`f &6 .G[ljKa1֢1h?dM&'j6cfp6z_Qַ r&/cfj*8~6 K~gwNJ2[6j9c1VG*WPP$''|mN}kTދٮ];$&&"??χ'"&-Wk׀A@lll|…˕HNN;t:׿6X=;w< &N/6{(++ ;wFRR 1XcK57fj񩧞1zh8q.nܸ։-{gUϙvP@PvRs򚕚vovffRu\L숂,""6̜fE_y|<(b𗐒,.]++ IYYY8y$r9DQѣ1e :Q:t,Y~eeeXj//G!"رz 0 <(HLLD``[WQ&!%%!!!O>+Vٳunpp U$"L4 eee5##NNNA&ʕ+ jy)̟?ĠA0zhL:Q/CA#Gks2 0,8jDEEk׮0QYY)-Z@"!!2d\\\) ӧO[npqq!"͛[[[a51 yyyx Aŋ A0gIv8qjU.ڷocV̼puuFPivpt: 0;wWeVUU &LW_}%-1T\\ ___>EQ… qFx{{K3f 8`ч6l|}}ܸq踰'Oi7&Km7c xyyw>`a& 1N.]V* ݻw(Cƍ#VK>>>4m4JJJ8իiZΦP:z(%&&i4}6̙3k\cڵt?/$QIӑ/ݽ{rssiԨQ4t6lH.]x۷oS=>>>O%%%tRtg1bUVV鳄>|8JΦݻ￧޽{F1j|Ǵ]gΜAI駟hj:t@єO׮]_~_n˗/mjԩZ(55Zn-na l~zŋ "|Xlrss 6 00pttZ QQQŠ+h"i5HJJ¹s>+WرcʽQTTRepvNBP~A@RR L4JFRRڷoD%_ddd 33Sr`'L&?~<ǂ899Y˴]F h 6 K,'m6Yo6Yj;#_|pwwݻwZk0 0LS#j$љ'N ""'ODnn.nݺ`߿=zJ HOOG@@A?{",, f³> L$ʪ5DիL&Lm۶ENNJKKC1= @x˖-8q"t:fϞ-3fL&%%0vX޽ر#XI}|-?^{-iiihiRRRڝΝ;ۇ{7d&KW8p ЩS'vs}0 0MV+ GpFFFbժUx% LHSSSHYQQ#GJB%%%E`Xq!!!P*P(fNlӦ 1sQU 'B񻠴tI\Ro\h5}C C"|R_YsINNF>},Ӵ]RdWɓ XR,}777!s}0 0,8C@hh(~WۣUV FCҩmӦ oߎrc5Ď5HLL4h׮V\^8r(EEQDqq1ƍ'%%\pvvF֭\gbb"!llloVj%p]hѢFus6kWaa!ʐ  77W2?GYYN<h{Km/kwŞ={$iaXSiitFׯ_[lIұVcǎQpp09;; /@DDiC($$A ???)O&2 QFF P(>͓Q<==)$$iL{X[R@@Z߰>&"8rwwb,>&"j۶-ݸq軸8 L1׮?@}ٳgNSNQhh(9;;رc)//dݝLB:b0 0LS~oLE7=j\Stݬ}ڥKNbRݖaa*,8|ؿqc>R`sT*ŵ8+**􃠠CW^yV9aaXp2f|ak_U+^_m ?h';;QQQѮ]; 0 Ây| Z)Jj~;3 0 N2p`e`a'ӈܽ 7naa8xR۟9l2 0 Âyж-Saa'`RR:tŋkΝ+]ꊘb}a, ds pssÀ?JV2Eb}mlT*ѫW/t4_5\]]T*ѣG&aSC}GoS4ޏ>("77mڴRDVp4̤LL}z/M6_~tؾ};ƌcqa^}TTr9_O>(((@ll,Ο?QSOٳ5kOQlh]:u 5hF(G" ޜv6JC׹vZhZl'O.]h}Owސd7o^u޽oߖƠA2 -5m۶%Aʕ+$"ݺu 9::J[6Z#cV!)*|9ёݩP$ijiȐ!$]tu[_|T*jǏ'Ah$b^l4i @~ ͽb·u4M[ݻ cEN8AO==6L9rT*7oNGZ5kFnϜ9C """1 Qlj5)J/Br:vHǏ'RImڴ!VK*F۷oB ggg/UV) _HEڵkmۖT*mۖKrH$LFKΝ;P(͍4 y{{͘1O>;wPHH)JR*G`rqq_oAAԲeKrppݻرcI0ɔzgŅϓ(D H{={ @͚5} fshZ=5l֟~wNdooO̙3fEpC۷/ӧ9 }]sU>!BQcI8ѿɐ۷o7$)((4 }G4g0lܸgѯ'Of͚F!FCdeeEyyyF~ajiiiDC R(deeE ji}DD={=z<0`IZH.ӠAjȑ#$˩wҹ*R(dmm-A?? 0 lAD8s \]] NNNػw/&NB[[[TUUbccfd2@ADԩꝂAD@N刈R ! iii ""))I*3tP8z(;&qccúr\t>*5iހd2,^XzRRRmd2t+n߾?ΝZƱc0o|Xjב#GڨK.fO<)/++8::J/^sVO?йsg$%%ńԧAS\ P\3 ?ZIV&{v0boϟѣG%iy 9{,.\VSNСC7nA'@Dxk:];j5***j|,E9JCdNp \ B899ݽK{…dشiå?l N6?D={>#0 ðlQPP~-3gEÄAAAEeeeسg$CjJ_A }L&Pr? B(,,ĥKsƍغu+Zn-.]`ӦMAddQTQGSضm[C!++ ,(fm2D>|0N>]Cp4<֗;~8{=XYY^L&hOmjz!q|4 _~|-LS{?@i?ʽ4"kd؄ ТE a}Kϋa \.իWsIe+++nnn57'8G AгgO<쳏Z iN~~>k׎A iqٞpZr/P֭k 3|r9曤ӓȑ#IRaPӀرc$"[iܸqRҀ(t1ׯ5k֌|||ht ɆqƑY[[S`` }wR, 쒒""-Z-YDǩS(,,hR9 ٙz-R*fΜI:3ZmK.QϞ=ٙzM봧Lm͛7#͘1 @ Pn>z>L}h\}գ[6%&&Jujh +Ve[z^'w^iJzZnMHn޼y.͍VZER(((HJz0a 0 Ti(:hAz3g"66V[IDF$d2d24Ntrp&ANJ(BEi (٧Od0\ܹt}HLLB0WoidyL&pdrƱ:cLmV{o{ji}|҇Mrm2V4-k^N3z^`޼yغu+N8!M i΅ ?ā0`SNaΝشiSa va饥hl2L>'NG}kkk =Y 䰶6f· IDAT#Kx IP?^%{-a&sM˙cz\[ci}Zjk]mFG]>>lʿIRYLnmO9^}UbxiʣV: ðdǎ11@l,:aaGkRoݻaaXp2;Y aa摂B eeenj3jW=z͚տ|,ZڵCخ#h>+)) iӦw],_XntiӦxw\ߵkкukL6Qm}***0c x{{cF2 0,8n:ɓ3]ڬe˖!<<OFX:I^hL=zׯǏ?(mYioÇ#&&FD} > -j-[Ċ+}0a%fc2}tܹsYp2 ð|49}oΝ;FlF}&PDUFF"L&Õ+Wtv'&&FbǎX`Ŗ4dggc͚5ԩSq 0,8.Ν~ʕ+ҋ… (,,DPP|}}'L~ /Env""t:ڸq#uFcc!CP\\HDDW\!??Zaa^>Yj,XI&A.cѢEر#DŽ P(0k, TU,@NNu6zIedd.rJ̞=QQQŠ+n -- śR ưaðdiAm۶ܹs(++0chh(Jٶfddcǎ HJJBxx8k̿ȰX_m~% ׯGII .^1c?IJeu?>=NNNF@@@Cj(*V>yRRڷo"##yyy w\bÆ 77F;Lz*VZ///KCOLLDvz/_ Ňa>9y$|M%{888Μ9Po;;o ..@j*V(޽{ @ػw/0k,<}Mξ}w^L&Crr2aeeAnt]RRƌc񥛔ddeL8:g6[R}Moovv6N8O{"%%$SaP(KIII;w;:vh~*FX[C}[na͚5(++k-[֨RVEEY޼yhիWѽ{w 0yܾ}ҋjڵ+Aܿ/= p60h}{qJJ )**BVVbcc1rHILo5 ׷3%%垚j]JNNFppp0 4tP`ʕfV_m~ HZ ?8zjDFF4>0X# `Ȑ!8t233k 忄#%5Ǎz~?7ljTTTXF}waĈ,8aXp6Mv7n;n:L&Ù3gb]۷oGAAYSo$&&.mڴQ^^x,X("55:t0*?eee8y$""" "j$ l񥛜,+Whݺkj6kdv ů {{{jժ^u5giO]>& ILL?A *n_BBž䷆}BCCkH{ƿo#::m 0.]޽{ ۗd_|ŅFE;wf\.Rug$j“9v3 TXXHDD1͜9(aԩSJ4vX#""OOQHH|uV T)OOO r5rzeVEi) ZB'%%C5kʚM5>۵Wݿ̉}xeNVojeaaxy!w ܽFSUU; 7ȰaC / Ba!eDv s23ayJsZj{7V=MTN]0 0_:V '"Ti30vlu#cb>}~'>0 0_:#JGsƨQJBVӨ\)GG\[tl9ՙB\bcc1zh 8Ʈ@u wU4S_sBbȑW_}Ç̓lj}0 0LӠICxg-Z`vt_رcѢE 4oFBVV#jb""r#G&Adbf*A(Xd Z=ȁUfϞwִqkky^֭[amm]hd?0 0L][̞=gΜALL Aѣoa߾}2e ~'߿0bGj2of㰒Y5qqq@k-% j~h45ر#qqq5 0 0,8= "\.Gqq1 >>^ZٳEO=$vΜ9G1 ^?FSNnrw^ƌcQYYYIq7Zϟ?e˖!!!:O>$֭[gLJBUUMG3=݋aÆdaqO>(GSNѣP( "T*?6n};aajAj/E ɰcGLo&Z-#|!nܹpppX J~Ѷm[lݺO?4{1|5cmBK 0 0Qerrs "|7ׯQjw܁BQZZ J-|K flѬY3͚53[F/-EJ%Z-4وDQ?6l@믿pww#_.ea1G#˗/ZƶmjM ADܹ3'ӳ!鈈̝;#=<<o%/_iTÏ_ k$]֭[cڴiu*0 <@aaXUUUM@nի +p!ׇ=7+W0j(9 @ETUUaZiӦM(,,l7l+VbaGp6DI&) 99@ƍV=sWT5J'dHIIAHH  >^A%$$ ((8110 ðk0`ܹ^^^jv^x2Jo>aݺ"t:Wh`mm/LLik׮apss+kqh yyyxj4جfHJJBv كvt8}4u,_P%K`oo3gΠgϞƁdS'$$C aaXp?>7n O?4s ׮]CV@D~W^y2 ~!!9Shب+qz̛N QQZZ!CK.HNN?Jm۶FCs̑DaiiEܹsG~ϐ>} ''3gĚ5kpq|f+kkk$&&b߾}Xb}]6XP -- 9y|aa矒4BTbڵ4i.] Itn=z4cJB.##;v\.ҥ k˗7G| fZL: .D-;Z/)) Ν,"EkJJJ`gg'EGA@RR5k ** VVVXb-Zdv>)) {/ WWW<isrr 6 00pttaa(4$%%a̘1| ?.E.\`Dj?UUUɁ}"+)) ᰱB&LHG6HHm7<*9lmQ~IIIXv-Ν+ Adl-[`ĉt={6~ݻaaa5k}Yŋ ǧN8!!=zJ HOOG@@da& G8drssagg''裂ݻwǡCqFYh8%%E*"E-HOOEϞؼYu.Т?PkMRRT*K¯6ܹsFCELL V\ QQTT,bȑtYg}&ee MMM"aa&//>>>;wn:ٳGN3f T*!q1ih[f7ZVFNj8{V0n.0Yz6\pvvF֭!ڴi۷X`/^ ܹm۶믿ŋ! 5|c(@aibslݺ(558@D$www2e t:򢼼<Ç'Pyy9yzzR~~TϏBBBHҞ={ۛAvi(<_?DBBBHݻw(5?xE|||$LԻwo%QرcL /Paaa:ܹCD͚5^*S >nٲE0 0 -QADRPAd2t:d2d2hZo"NjrFYGr\N?Lxb|!'NYY O tagljNIeAI>З3̙3;w.%; e }faa<N$% tP($d(H3WV.-KTRR7n|>7p4#ڵ l6K._o97HMek 0 0,8Fh߾=гgOL:UgPcQǎII; +kcǎW_ſolٲda<`4l QlRo{Sj^aaXp2(zS=ťzO?0 0 N/Qg^h0a Oaa+y~ |9p(в%vmeay3-- Ç;0|p\tIGXZZӧ ~~~X|9Z-{!VXV38yM@"#ƱKR'A vyKej+0 0#&8M~.]BFF:wSJQQQpqqqQO>l̷B#j| 2Т]<0i LV@ߟCqhN(~Wd0 0<~8fϞ 4k HOO"@dd$Zh???kرcxzhQTarv nVa[5LJ Z=`5||psJBQ҅ @EE2[hBaYe^h(@ڠ㖎Q~>}vc.Scǎ{01 0@ݫvDQAd2 D/, $J6 '"^D$]+dRúj\meO 5TZ@T@)S?$3^G:)\̭i~KN=z_;0 4E0gᄈPL|(#7:-{7ЧĽm۶ЦMcaaXp2*/O>i45<4kV3u:Ga N h+*jUA''G20 Z@JUwƨyD;cbʐ:Sa?'uF8JQOiP 66Gw+#GDll,+ >ASoayx;]755< 5k͛cԨQʒWUUaҥ7PYY:FV*6n kkGjnJ%-iekk6m`֬Y(((hfAV EM6[a~&DQĒ%Kjm6+?55sٳ!O{/Fdd$>cܺuK: "0 0ޱ/v NcǢ\.͛J{/X͛7ǢEp[# BaÆI=IQ2sBRСC QJDEE>>>JBpp0F`Ġy裏LCLlgΜgϞ±cǤ~KHH uV]z*++`j >>>>}:j5@@R ۶mCvЪU+IO>puuźu RcƖ-[GGG3gc޽{AD3fY1fHl޼!!!hӦ 33S:/ƌcTOm!2 0@"Nî] "ڵk'ADpww+Z-IV9ݻPF&::ׯ͛AD?jHOOwL0D7o믿ԩSeۼy -[4hAB{E 9s&~g_B^ vF3fٳuo-[୷ނ7N J 6H[oᥗ^ҥK1h rkƍP(PTTV ++F_VsApuu6~gvjVpGNNNͿ@ 'NiT[nFX1MaF@ h~?!LFhͤݻwIPR*hjܣ>۠`R4{> AׯSII ) !V+ݳ B kkk~&BA666Զm[b"" OOO{.yyy:}BDԶm[233͛P(WF"}6) *,,zi2?#Xo꿦WWW@7nܨQzz:͝;nݺEf͢3f}~~'={6͞=Ӎt-R(bƌߛoayx4xreii)>d2{RAy& B "x{{ZYYYF...L&3ihWV#==ׯGf666:u****ׯ_ɓakk[{HQ1}(GGG]oܸptt}E+**jMԩS`}쿟{soҐXtQ9Ξ=ݻwCL2۷7򓽽cC'f20 0]FK/9sH P\\b;\B.]|I_Ac &fΜk"&&Z{͛#++ ׯ_3ӳρ***p5]NiSPVVkkksEsΕΝ;Wz~nܸ;v@P`Μ9a[YY$zaQSO=s!&&DTcĉd8p(B<>;K"*IXҬXj-բU*xBC[TFD"D Ad=?̝H%3{ν3߳ &L N'''ܼyiiihذ%=z4.\GF^]vKk֬}m!Cg!$$^^^&1b-[F2tvWWG>$ q^vZtYF?{EEEyb CӦMѴiSASs8/\z#0`KMMvs2www͛MjР8/su͍31}ӓ988+WԘNsT5Nbr`XH]ɓ'3777ָqc{ﱢ"e|e? s8]ocj'>o68?1:W)Sw}M}`=eΘ}РAݻkrN<6l@DD+ICA/&?5!dg߆֐mD(V*%KM-~ŋ?p)}B&ݪ ǐJ]&$$-ZѣG___C u^|G77?ZAAgZف8 <<...43f@LL ,--ѧOXΒ:>G_~ػwoͣ|װ~ڵ C +('A$8Ip5ţDE#G<t&9d <^T|w;Ť\.q F_u1@e$$3 9u:KN{>ش 8 \T9U_9B ѳMI 0f L<Q#~e+`.IA,49oooL4md>o<@TBT:]&O? bTWm<=֯ݻ99@^УG!>L.J-HOO1rH\~]7n`̙hٲ%Zj%Y={j]? D=EpB 4۶mCaa2wޑ!- Ag 18|BB=WBwo.(++CII ޽>}<ԭv-Lȑ#:t(ƌQFaذaq81:vsΡSN&塹믿F`` m_DA|C{d2$$$ (( 4K/E᯿4]Ů] 0z4q#`ff D733 ۇ*-LMÆ ĉ|rX$''cذapttĦM$WGGG# nP ̜9sСCыxMܿ_yѰDTQ;w/2gA|DRRz]#.Ffq8CLL 4i0e?Q^^L4o\ pz gggW\c`ǎeعΝc GGG& SEW )))۷/r@XX,,,pQh8r5kÇշ-feea̘1pvi A0c ^b > z .5 Bhh(x 8::RL'..-[qسgZl [[[tL)  QtϺR؋/xgulǎLFxglҤI,..EFF.]0իWY֭ى'Xll,kܸ1dё{5#::uAL~blLRX&MXEEcL6tP̺vZl2feewA؜9sW_}Yee%glѢE,--MjtYnc,**ݽ{͝;9AQhmx]]oZ wELL 1233pXZZ%K`Μ9033âE0sLd2 077׊8\p 4Zhŋcn݂;;̟?2 qqq,Yӧcܸq9s&|}}*ԩHK+F9׿aĉ6mr9kM5뢦SN/%%...Xr%xb˩ӤIL:PVVYfhذXn{-[fGE~jLɁ^q$L2Jaٲe7nfΜ Byú\޽{VVVg;/ KdW^ş0$$$ 33 ĥKйsgXXX8$''ѣ9s&,XѣGS733ǐH[t)JJJp 8phhѢ222BnZz=}4F)SRR1Ops3+pxdA&{nscbb 魯.˖-CII PVV`-QصkWQ,d2Tҥ  ѵkWoMQV[j/)[:u 5lOOO6w#%oAj1ŋZ׭[O?| A 'Qp~Xd ڵk'?2 Wp-2d(J`q (J( G6oW^ŧ~?X˘Wb؎;СCpk+Wq〔ʘ%M^4. V^BB=ʩkϤ$1~bb{P[k/)[3>>ݺu[BqwGn Z/\yعs'/^L$A$84nܸ^x~~~hݺ5_kkk4m!::Zˣ( ͛c(--ETTOAo0>bccz4iٲ%Kt5Ҋ ۷ocԨQPTС֯_>^ˮ.\xb޴MAQ4ֺӳgOv=spp0yÃݸqCłɓ,00۳Pc`MnݺłqK\s1???VRRc,%%3B0J._̺vثFbݻ؍˸gYDDsqqѻAꢛ/cF9hquuvssc!!!,11ֶ̣.hѢӪOdd$رK 6{OI&, EDD ~[AAAT\$ܷo_fooϺv}:0ad2ܺu ]tMЩS'69s&1uTыL&JZԦfSV] *z@WD~G8Rڵɓ'?jJqϗ?S&M趱2N>q1j(1_ <7JƔh1)0t̓:-hpttf͚ptѠKHHw ,I};AA$pBxxx`bN&L_@4=ϴ4j VVV={6&N(zڊj%&vܰk.̙3_ѲeK$ 'RJ58pزhhk>j 0)CAē RPod @ppyc-)?u+z/\^ VA#k{֩}o޻֮}mC _LÙ9s`}Xr%||||OQ\\T<>ڲa!!hި8څ͚ᏸ8o$42m =bۧObLGl%U y|S5W]׫WXh+߿ 0㧟u4{v~ 1|y3CFbȾRA5p-A&ǣ_~XjA@xx8&L(qnFʧ(B6mcp06?\0ctAm&%d|^..xK6ibrS~9Hgf`{»Q#\LOGZn.ƿ*i#ƿ]g"#/Ĭ*S,yx^t;v w1S'd߽ 7>σ/U99p8agew{Bp/U5kpc#>9k),+sZ")LF&t﮷=탍N&$ |fdc0m[ չI?x.DYYY,,,[NX=5 B'۷7a.cqq,1+-عi6LgnkyylȲe}1\%WشMXZnnn~;{,(`e,*9MX^eO>rUȾ{g'Yqy9w{⾱f `&v0:嗔}c߯d֮eYIy9;lݪeC߬͛پ X,*9]q#}W_v-;Jwbvj?c糑Vsii}͑#l,dGKw#?v-JNfeex|<{C} Rc1^ba67RiIf7l0٦)FKOݸ}o,Z}oݻlЗ_;wI?.x^DK 17oȑ#nWVb}1p 47(gÇ?J oFsWW-XJkyy|([YWP^Y ƄJǡR_R kxrs RNZ67=zhs<mW|| '(]UP]R.]B6mrVq^CUro]lXp:' DVTz5e mmTy(!CL&C^aoiYyy5Wwm+ڦop0b]6kKR_ж-:V~~+z¥TyM z/}7ƺ~T1ꃶJ71oPYZ ow'[[66U AА ~ Ð!C!u!m[40DDߵ!-7M6R!nnWٻQ#(tKͅΞůgwG^@I 0f dd蟫˗OSh֨5kW J/,5J +*kfb|)WR^ Ǐ|j*1cGhGkkvo8\;wsuʚ /+~R_HOǜxJJťAO}/U3))X? )A?57Mbg99i LG_]){\W]/ASJ͚5;# 4؀hl,cPWo*L33Q㺄L4'T8}0=Ö-@߾]T}s碁ٕjHbf&xy|Kk SIIxeKzvߪ4Ū `i4u|@۷(DBZ뙿iݤO*۷,;~Aҗ_^a!j?RR_|<:i|kퟘ OϪy s/ԥRuB &С;Xt) f"nLʂ%IZ'egW=\׶Yvq>5cTPXR[8vCn.bXlr*v;;wj[>HF8JOK1}{QKŗB~ ΤpL <4T|O''HH@Iy9x%[zMLի(!3 cy,cDUCL_ѯ2>[0,~9>3p05X?<닕`3pwtނ͗yj%4L.ퟐIm|K;={b _*cr L1Am[$i0_Op dGkІĺk~{DclALQV'FR?ظZį]u?ΛUEI2>PLĸGmGSoc7LYmnM~GJ/^:sSkjԿY'}G3x NMܿ9ŏ֯{{_;o-Z "&O9&wn4YdK/᫽{Q;%z_AΧGQ+7$泌֐P*AtɔSW+W;;= H_4ARsVW9׆?7}'Ol֥~))~=.^7-tkܯ[{BZ yY?TXs ΥIy~ax>~ܙ29s`}Xr%|7NJJœ9spz{;bŊh93Jql%`VQ#4XFW>0 JK1ÇçqcT<cB!N(h쌷wG:/}kk{ۧO\&CaYFFBz:aJ>\}Ʒ'FuOOܑH?Xo iu%* ^c11`O?aiȃYZQ7l1cX}]жY3mX\]]hyF#A& ?êU 1aDEEA.cҤI ͛!֬Y'ĉkM@^W՜1XhE̵kU'H'uˏ، r f&N|MFg|2t(8͂×{buKFVС(9o !`2۷ /`jx v?_uLu>޻Nq1ǺC~Dujb;| GԻ㐒uJ}iif/g`ƀzX`l8~i`vbPH;t۴ASN{o qH7<,, ׯG5_=~>KL,ۿލbz:rs1W uX(vbzӱ9,=2CۈOxK󾫯 Ǐ#9;vVVxW/t8 /=|L>}qL\5UP1[ 5K)*ޚЦCX{%%`.^  klMB}gзukt 0n:,{Mh>RyxL.5mٛ7W?'+tUX wEn||ww`HuZ f^JK1\HOGai)\1gOt[ポh[M 0~z9֢Eoou  Lzmu,.*'(KjIVV c֭c*J5~- e<3 0^b6mbiL`Ϙ3cؔldog7١?X_/,c`L1)Ky lڵtr2+)/gcb؜[쭵kYTr2+,+ck0^fm]JgQlN8RMX͛W OTcl-/ +VTVfmMIWfdJ=7ƴMX\Fc,&#m]A`+`Gccgƍ,֭Zc'ӰmE'eg6|#%v,.%fe;wj=5o6n4_A8V&&x.Ňua!O?a߾}2d ,@rr2:^~׾Pݹ\悞Xc`\lU/6͚#GCJΞŒ7_j{ G?UeLJ #nRR^ Ǐ|j*1cG!+7oE2D|hGkkp՟5T)MaVAhUo% Dq͚᧓'PRFH[=ΡC!5'Fj* ӸqB'{􀻃8CiVnnX[Fo^5oHSssb] lΞůgwG^U|x8'[}4რ:mA$8T*ѬY3;  AP(<&M l۶8Cq6͚a %POqqX ?QY ߾s++1ݜ|-u*) /iWXht^ݪ4Ū `ic85UPӽB|Z I3/TT=wG1" H @D~i)Ƣop0lMbNQN%&"|pgRr %%pӛ;2dW^S0תIߠ{nFŔT?Ф ==D #p2*a`w` $Ť:tw}b\~K.E@@p?>:vӧ4BRv6;5úu;ley`\Rl퍸gr2orǩDWV|Z?!׸\Z/Oc֭4tEW` (811pt۸^bm֬K_|IXHƐJAԌo.ig/_FZ,vR;lѬz)n-p_{ؾmݴ)KJR'egvDI{OM*U*GjJß뫷}mڄĬ,Rnj۷E߷{Aģ}8/]sb*:hiaaCreeeϽ1ޟT` Aĝ˭x±p&MƂ;1Otya}*V;Љ;yh_}U$7+@ WW nN&'Շu;EE_x8kت>b y$Eq$, #8i`H8rD"`oUW(1͍ goᄡ4d'9/R|raLdBRxAdl?sǏ׺~(,+?^m\T_vݩoߎAAh`ffr0pֵky+#"~>p>bat )Nxzc(n{s tZU'5OWMRco9}/^̬S|BcMd$"ZQAOര+NŘ\%0%YC%7c`[=w&ACRO,Qg#*s'Э36Mig Zy uAp*uZF}SixGټ2(d5 kEXdܽȚ:UnA1l0{OeRw2d<~  |ڐ  'ǿ&&&"$$NNNhԨtß6>)2(6 ,2 y 8>8ߏ7,6*oBQ >#|5AA5t9rEEE8p z 8W)O-\r +W[;c>|AfffR|P*P*z8R}Zuǜ9sФIxzzbΜ9y<2dsN4j>ÇתAADN\BAm6UK?M}RzEPл>VҡCL紨|D;_`ժU7o͛UVaՐdy5jÝ;wk.-a/8?'AAzy5*** }I*0)j%6ƍP IDAT`]EMJ%Tk)1P(D!P(ߵkT* RiӦa͘1c(bCBBqwpI-a.AAƨ.÷~ L5kRPRR1p`|H =33S...yZ[U%P}.vyyy?V  z;v,d2(^jN#芋 ^3w\,Xޏkܸ,++Cee%kܶԘAAԟ޽;lllp!8qDp$0uTqNmqrrΝ;GqƘX  z˗/K/P;Æ DTӂzzC`tэJ+6uW~VtR!88bܹs1k,|hݺ5Zl+Wjy۷'IA$&4$3 $ D!CRwa.cСӧ߯WŦ`72]wH1 Ak#u x׍?h DDD`x>@' AQ'm0QTǕ+W__ߧSxe 077.ې  ӑ =r˖-c syy>Xl}B  4! * #!x`[70Ty:5T*c5$ g]yRZp>0S3Ɣv& ^QHܤ (!|n./P@ Nh IAD }gZl <w rkl =O  hЭ?[o"1M o ,Xhٳg6a7CGesww7ғ@VV1iҤjqAA$8-( Ƭٳ__ff@@LLT#G""'''ܼy:t(lقw͛3f9-ZGAA1q=Jwr?d2r9'U N=`nnDꫯ8kXөFAAW^غu+7n 瑕P8;;>>>(//Oo>Xae8{V}o.Ub`ii$HMMc ׯY3f W_}PZPƍی3`ee^^^0`4i"i}>w^~e888>;Gϟ+www8p@<ʘt KKK_AA|n6m <_~Gtt4.\[[[( TVV"++ M6c شifϞR'A@&L [`Xj~L8_-`{۷?^|Eٳd9ЪU+!E˗/Gxx8ƌ h~pk׮×_~SNaӦMzY`nnX۷-7wASP )) ?/_.ځsA 犤$nJ˖-øq0w\xzzXh&''6lz#Fms8 'P*mx1 1} ƍ,g\\\锔`ʕxxb$''k]q,Y9s -̙3šk׮?~<1zhdff1fFȀ#-u: x]끸8 >a8u"3ȀO APš#} sh4W!/cyr89WZYY8p-m=j[YYӧOuqA 2K.m۶b^?# HמZKLLDAA#;;8عs'JJJb ٳGS>|8,,,бcGr]|ʘ8887nĮ]0{ld26ҵgttVkd  pQgفX&MX@@` 6do6STq,;;[w1ְaC;777JKKqc8/1g3u]XeeϬy8֬Y3AT_e666W_e ̌LR'O@fooBCCYnnnJJJ%cNNNlڵkbmkOyyy j۪@A-th $CU=`Zїk:809nU=S?*)2$25QT*zK_מa$4 'QUG^U_W[A&L6-r|Pb;Kآnݪ| GgA!u+108iGISs0 T5  i D:QVf\W>%SaS  j䫕灢-,LLu%,0bDw|CO3S  jSǔ4#*tCAA'%&&"$$NNNhԨ=z 4ROcgW Ql~s/2/<9m`ʅJl.AU ߺq KI3AAX>m49s;vJˆ#p=;vLkŋR3ģco^T OTx.nC^Efcᆮ% )Gc rsAsD(++L&#Y,[kӮ3>oܻP`0_ B+~1X,X6 - g`]b  ,KBUo|~-tycƸUz wRllaQVVfpR  T{1U*mAвeK-xb1.\ ^q!T 9G GQ ~Xuvֆc "u#|y&d\ O`*T( B4U4?/Y|Xc!O>sқcz_٘iAA<"jI5&O Aĉ8{,BCCize`gn'~+qT C}b(K< B)6#c"8Ni۹͠7;q7}PYYIM x{B&a͚5YhzFU6, w,w/H^C*k ؚ*T&`ݽcO4P4fz` M xR9Ǝ LLQp:u G ġJ<9VZMcppc +CgȟoS)SUSv{(Vhj8x;xmDAϼ޽;lllp!8q1tA􀕖BkFQQyq~#rsx*Һ %,[:GDi߸=J*KPxPn CqnAqE1VLZd*yyyx 'AA1g8|M}’f3(,ƒ% +`giا& -ꌫPXk)ޏxsExp8[:#0B|P8px;|`ݧ]?E)_U[ nntqAq&e$ ݻ^,AA4$3 sLFW N:ivW 0Al}cZ^OuZ11he,0Y}qY4ǦB W3ydlذ۷؇Act%ЪU+ 77VVV !CV#)) 1AA$8 y+AAOന6daA B<>h/ x8Hᬯϼs7ހ?9xZK͛ 5kf8VAmڄmӧC֭t*maap{tJ 6?|ǩSv|<Ǔ^~zz44| oY'NwP 7od^` nތoP/]1 `O?Q;<"Ai<\͇Πz?7V}?zy7qWXJ%iX0j?dz*=-ϵbq}*,Dp%x`ݿg/_ƚ}pe<)t$X3[|_Oƍ(v?/Q>Wڟ|\-/ǘ޽Qx*+؞|W뗫ulVI^~eOIQxx8͝;ڴiCSii)56Εԕ+ֹ_ddPVAz5 :1/x~>r%ӅRʻ|F,\HUUy@:}%.*oשv8A-S|d{uz=嗔[\L:tFr ^1L;:D/_n|CkUU zfb~o?-^LS{(޼IUUTUS:|;G:\Io}RY@JK٥Ki׉tffҟ|{ت?UZ-[v8A7ǎћ}=fpNYG|#zի"Pn.Msr7ߕzڪ[q}mʕt(7r+ٳJɯj٥KiߩStCfϦ2t:gN-{ܶ>L`jj\m.ғ}DO*l&zl"|S+Ueҋ))ujlO2Nhٲ%z^xA~:^={ -- M6믿hz6Z,Z3z^ ."oMcz=ΗuXɛVGxep!Z-4FJoDZovP`0CAX|a#{Ko BNAAPi^nnܩΗxFgC hHxA˵S95jt:\~c0sg5i/wwk4o +WЦY3kzt //NC<  o_-frD?G?Y? CvEX4qsCqg5jk-.Ƥۤ nTV` eK,WO{ӑa>δϖW7^3Ũjц #%*Jyؓ5#qVhv&MK~dcW_`0`ߩSA>\m_\-CPBj/ww !>>|JG/W[hO{N퓽^Ԑ%{ _cڵ1bAk "DQDPP0 w|K%$ Bc N!*83**Bf VP]wy'}=hGRv^3֪ż<*5Uר1Y][q)\@N'z5TQZԲ95N!:4EG[|Ѱ?ڰ>>h7i7]vU?G|tm熡s5~ V7l#sD?Glh\,-5 vfg{6N7߬|EFJ&g Ί?^jOG-8SG-ݣoYڥ < ھ9p{RۓSGg'/k4t:FFbH=~ÌW{ھZ>W۟D)ڑ(ԯϟ%~rD=zQ^^c޼yW `(//Gqq1ϟGyQn+)ÇҐ! 0!=Bp]BttŠ8p D(D['V9>NNrTm닃gΠFǚ&i"cGxؓ;F (~o scO[9կ__?}eUUx`0tD?{ج?~~ؙjN/`;gpf*H w µkM,w^t~ز3YG]m߲ ѹeKQt#z8nVWcKFef*/ "vSTT*.߸!w./'20JG IDAT=JQ9ӾR۾uy/DVAcg'ջ~,Y3g&M`ҥ ֭,Y7x_}Zj9s.tPV#^>!hoތgjq|f{4qw|i&%+l_V7;vK|s'b<0 C۶!eVQxy0 WmHz7ߥ۱) Wި͛Νxi`Ea~~`ɓxK\ҹ3oق_~;mZu:7# 5ܧΦw5͏٦MXkTW~Mg[EPRn;v3zlJ5>߼Y\I ܾ9RW(wC]-He{ǕM}FB7g!BC=ؑ^mŋۮ])ÝJLD0?cbLFzGj\mZm٦ ;?飴ȯkGIJ7팟~iA-o$W|ƀ` 9 o,.Iϊ N9w[CCnGt|ޟvÖ}ꢟF!$wk#/VH]{]-l?pK?,ӟ2:"![O]C'0 SEkעCd$ҥA3 S7&,[ Bg(@ӦM{>>>>(++O}3 0NFhLd~+>""f¾}pi$&&bԨQJFkٳg+QXX VVVƍ͇MEPP :WPPt?n ap˗/Gyy9Ο?y!>>^ 0'M ܹs|2.\aÆkPvygw}Zk~]mu;wuVX~=JKKqU[3awE?@nqv0 4 =zL@C5Y5k0hܹKҕ+WͼG޽4f2Wh|~D>>>G<ݻd(ݻ oooҥsdϜ9CO?4EGGSPP 6N8ȯ_Ϗ @S:Z^z{˲%K_8ўok7n\5 ڹs';;PZZZ-=]oO?"z,a;C$" WGh!'"` n)?8LRwIa,Z#5Yz뒿-Io+cX{ѢE 8q"1~:yޮȷ 6 /zzՏa_}_Q}ns~z|W:cbbqFnݺN ɓ'cȑ#7o1 p]1rH|foo{ie/aǂ 0^oWoh}Q;gCgl:sСO"0 0u 8Ǐ8pGLL &L̗?#Gs~9 ]Y6+J\WW23334a1ǡ9z^[.\8\|VRQQ޽{F˖-!Ǽ]P?ǃ1:k4[%… k.{ѡC%OOOTUU3R*0 0<b={ŕ+V!C٨ 3p=Oޏ%6K-aگ4| ٠e} ZV :avc{ :F ?}<3u& P?엕)A93f???N-[(uV@iXPa7qjѐ'Zl={^pVWW?Ǵi՗dV] -}[W}M zl?FcV d3a $5^ZZ 'N4 6KY%Y& 0 1qN<]|͛7#..͚5k޵0u|D dQtr #?|}5fu`0ॗ^BXXx Ec 0 piFhL<&M"_~>ڨ7niZppp [%okQ:^LTqi"""0k,۷OFbb"F,caq9ѣ/_r?C|||Çk׮8#Ao_bE}xz2 N6 ,lo555BLLVZaΝj8wɹs!66Mba8-|rOhٲ%v3g`z2ϝ;?vH3ī^ŒKh>a;YfYt0h^|xEaa?Nm NoO>%R 2Gt7nDHH0`0sLaa9NRqN<]VY44uT 4pF^xN>SDxxO.2 0 0:m^XXN:XÙ Ҋ-Z(s>^O>q,YN:i={aa{%\|9~'l]vř3g|re?=zm۶E֭w^(AףiӦmaa?  2gS"I wwwwaaCQ ʊw:lgaayy;􄟟VXQ/+0 0 swPU J 0 0p2 0 0claaNaaN˖~~F̝  ׺ucaa@l,Pϊ޶rxz'+)~eZl׷-99q~LM%_7!"#% ^ba8R^|rT0 pyZWz. gX)h[ R4i=ph{}\$ruV u+j]?K7dZbb:"C+EVoϞփDGrD0 py9t4H N,Z+Wz4 psݻ=-͛N$9))/ Jy&Hå>>@b"0o.)m` 0`HHzZ9,q.z5 GfdHuv@f$_ڴ6m'\~qDBQ;(}%{ڪ+Ιy+`dKzJv5Ⱥْ0 01ACԿ?ц Dj5ѐ!D:Qqt,DWTD9[J鼼>h%y!!DZח pA:r(*(4(-h̘[qw'Z(5U*n^gO*tTGx-GV3}:͛R9%Y%%R~DRڈ"A :|X:f8"Ӗ[J%%Y=m9s**ܤzRP,*;[СD'NH:wdۓ0 0Uʀӥgԫqkupiz̉5'c^ Fo_I^ApH'8P9ҰoRowIr;vf̐KRϫ ZIrFp]yf+#&$Hemҵ! 鷜o]~kv%Ӗ Ӗl$WڵfΔ| HvdE$ӦIv&zo0 0{w\G?=]:׻4 H=aΝCM$͋2EVn5qqT5TRݚٳt|t.>V:r8TE8vLERn$yrٻw%gK+9ȴEvh\[uZKݦ~4kVixSz8ZƌgϲsNn@7&//ƍÅ ]k~'O`@׮]!8Ӌ/m߾SiC'|@c…th޼9vލ S >>… `ဓ|hҤ[֭[ U=o`\~cǎEPPVZJUo?~Dߩ(Jڸfחߜ9жEիΜ9~piojڮ 6մIDATzj1W꯵s۶m3f Ǝcb…?r=p<oSNE^^ϟߠ# sO_tO>G!'L@j4l0-[Rtt4>| Zbbbӓbbbj!"ںu+uؑiٲei޼yFɔFAZ>rss#///={65k֌VXA߿mIŋiԼys޽{OQ`` >| D4oV藹sڴØ1cL56t7ǖ-/s_Nm6RTOZVXAږHV͜ٳg(*++IVtZ:wLnnnTXXh'leyY\`QXXڵϏ/_N۶m^z?8ǓF!y&5k֌j5]zUֽ&OG4 ͘1Cil#9sʯk}y&jjݺvaEz=PTTiZ:tՋt:]vwNyyyJ}ly ~DQj{$"mڴ<=='@UUUԤIדJhtV]z(44ҨƌCv"JE={**))!ZMT]]ӂ (55T*VlC҉'HVSΝ-6Z|}} ]pZ[wee%`###|||aR4dtT\\LjDQիWVˋ>Sڱc%$$W-Xʕ+"ǚk7s[V,w^߿?iZݻ7Hk֬3fҥKV@PZ9tPCVy_IRIY-[Yw>}j?zUrwss9sЗ_~I*ޣR9G}p%RK:.^hb{|?yxxPYYM:;ڲ#J}R0 w$JJJhڴih~o_&Q_~={J2yZjԒsϑ(4sLEF]r%H4w\tRTԯ_?GÃZUy ht:%z&%%NSuF555G*ڴiC:&NH(_ڽ{7T*z-2yyyV),,LioV}RT4p@?$J/7iZڲe+RQQHr`D555TYYI۷'Ajb~3-ͱe [՜2e zZfϠAh֭V{W\ՒZJZo,*((}JCZ,lo̒Olʒ_zZ>0mɔ}ݡC۷J8VGI:aAڈ#H)~m{uLiĈ'(KkkWwiS+j5r0Eđ#Gw^lڴ GVuoyӳ> Q1|>;e Z SLAJJ2lhڴ)]I&):v숛7o!Uy "w6-[XwFFݺuݻ>>>M{aaavGff&;wJAD|`Ik~gsY*y[cꫥ۷<裈Çjs1ZVY:6ʕ+@ejbbEiii "w}Olʒ.]Z6mɔ}hROxj8&&=n|?ȶ6Ƚ&/))ANN6o\kn꛽v5;;ۦ|Wɓ'ADh޼]?/ax=D۶m-Œ3 ",Y^zAܹs "c "Ci^}U._  \JJ *++fe+DΜ9"D;*!&+y brmv 8ϖ322!9xc@DիAPlG0p@/ u)))K-;b-ݭlaZ AJŒ3 |}}\ohDDÇ!}Q_,Ė,R1)/tX׎@~u۵kB0{uL^y۶mCYYCi7{=ԷÇЧO8λPT?Al˖-J(@tԸo&׮]3goJ_|r@?* SL(Vɽ#6mBnn.O"W8(@XȄ8 6(sK?s4?[{du;wŋ憷z (*nݺaɒ%믿n$v=(|`Ik~gsc~ޖzƬšnc;aaa&=*++l$%%)61zh=UUU >e+K6}oKyk{{Q烈PPPٳgc "t{M:u@TUUaƍ "i7{=Էm۶0x`8YsYRT?+[e>PJJ[zHS.]iӦL;v8gmF!!!o(sPRRHg&^4СիtݻtB4h e|@M6ѣGNf͚eޗ||E!oooL_bĉ)::իW-ڲgϞ$)p6?['V紝3f t:| Mޔ@?uޝAd5~*,, (@vګoU{Zߊӓ-.cnD :* @CE`0`0@Ee[E^" &i^ trr`AE\t hٲ%233VMd[GD嗇|͏^&}j Azzz^)yz2 X,\%[[WrzkT7kV&G`MWt7ǖ-/kuZah^M[N> iiiٳ'5[qZgdZ;6=~w^3>/˲tRZGꛭv՞ַ_~˗/둜= SOdHn7l-Ňͺ׿6ly;G59#ޱ#>Tw&_g G|b]'._UVal2L=s8`aܸqؼy3x;cǎ!""*^|Ee9g4PYYi#Ʈlٲnnn{[b޽9rd0̽ 1mu;0~Uy˙ި?w/i{a{a8daa /]vu = g'`Xp!t:7oݻw#(('Ok[[x3uT;pumۖW2 0p4vG&MpY,Xׯcر ªURuVU\vko(Śu8mWQ^z̙3OFVՑ`/^6Sa{t`ѢE ʕ+a00aph4⩧رcQYYZ_~;w.L,X ‘#G0x`bpkVVDGGUV0oFF[nu;t  NRmI& ^^^ؽ{7z-Z`ԩg`0XԟPTT & 22>>>HLLļyl]׿ЦM4imڴy TWWc֬YEχ^Gvvvb\~AAA曏ai]j5HW&JEѤҥKV)66t:]xj55oޜZ-ݻT*ٓjjK… jj5y{{SuurN ,TRTKZ)44CyyyԴiS򢪪*$www'V[+ ӆ HVӐ!CHVˋ>Sڱc%$$$"V+W(**BCC)--h̘1JyJDA ÇIV[޽{jw$"Yf̘AK.%^oVIVoklݺT*%%%ٵ50 -]$##C>|8A@aa!H,^B:uF%7oDXXVq v Jw3f !!!-˗b \pիj5ӡ鐐`'nܹ(++ѯ_?={QkzB.}݇ɓ'Cף fB~~>/^޽{CE|wطoM;س]DTTTXLeff&:tQk'ܹs~jgYY򷧧'>i\x8ai,𐺋лwo\~fCsA@ff&cǎ6 82wn"gϞPT8t >>e[׭[7ݻ>>>v<\ݹsgT*xxxDUVaʔ)HIIJjj*>LJelmnWxgark<~8ڷoxGÇ#55携ZVY:Fvv6(/1 0 =p&&&ѣ " 8(*A0ڵdtb`b-`pPAp9yfz.I;v D^zAzB.ovJJJ999oU3g΀j`vW_}("''jϭpڞ1cAo̱Na{ E7K,^u| 0{ll߾$X(**+.H>9В+9ٲer8വB]nΝXx1[oAEz 8 ͛7Wcޒ1116mBnn.O`Z'.]4eر 3 QYYigNaQX]cBG7nT7%$$O?Dݻw'A(99t:}ԴiS=z ={$A(-- 5k֌Ј#H)/^&EZRRRϏ}h4^xNhԨQ?y$Ӈ())٣fKo[WfF!!!o(r,w^ҥ y{{ӠA(++ ];س]tBM6dڱcLzz:jՊA(U͚5?;dG:u* hܹ0 4Q=}EA(Jϓ``P~7 "eyϛ< ~u\zUfrv?jZCE, /#&e6͖޶0/\6k[+v-df\f.\~m0 0w;JElmn_k+`8}4ѣGOnooQFYXXt_~)o.I$IBkVHDD̙3>3g('++kSL?ӺujuiWjzҤIׯ_]]]7nhڷoߠAZhFB>OAAJ?##]v-Z6lXLLv6m888h4]sZZZ6m/_.>xM6ZzIq?~,_Vܹcnn.Xre|||ddG}.@t =ON8@ф!9R(رcǎ[Kr@X.t,uqFӰaC!ƍ ~g9w+Cܹsfff%h._lddk. mh J.OЬY3[[[kkka6mƍNP7tEߧϋ/Bܽ{W>>>E)h/jsQ2a^^ޮ]ϒD2\TEڷo_(pssByzzv=33¢ٿ#իWBnݺ-\˝:uvƌƍۼys@@@nnn~~./233=]P(&&&Åڥe~OW .!ׯmӧŋE)h$YYYJٳB9BO2ںT7@U(2f̘Wի׮]^^O^^_|1j͛͛7>/гxZdMraggWnk׮i4\SNݷoߒ%K\]]<|PXYY9;;>o%o۪̚5KP;wNZP|w;vhԨҥKoߖ㳗^zI&ol.lR/_$m6++K[hQ7P Udffv^YyӧEIKKBˇ-ZB/G0\GB,^8666((AB*??\QLRy٪U4ݻw۲eҥK "&#֬Ycjj*OC3119s̈#>>LǏwr{-O?=z*g_6QF;+++Z=bĈݻw !---̙b kkk%K!O>}Ʉ!Ē%K?~eya=z-`Ν=zѽ{.]Tn?Oq|EPO<}?Ջ0aoFΜ9[h5k!6mڴvZRSS7mt]I\oӦM'NիW'NɓBpBh~ R)njsu;wٳ71Q $sz@xx;Ƌh۶mLLLmU 7N0^*Uzzk󓚤>^!#@%c8}/QS /o2P&#o2lB@/B@/B@/B@/B@/B@/B@/JC@p_ť$d2,Yd߾}N:9998 !#!77wر.@m^cMFz!dBFz!dBFz!dBFzQ;v$$$|wvj׮+ d.}||RRR222Nn芀"dJաC ]&#@ ILF.""""""++3gB.:77777۷7id۶m.(B@/B@/@^^ӧ.(!#@U!₂ ]7JCh۶m@@3U3腐^腐^腐^腐^腐^腐^腐^腐^腐^Ei@Y;w.66Ustttqq1t^\CWjN:999!#RnnnXXظq ]pwwwCWo2 !#2 !#2 !#2 !#2Z-%%%!#5_```F$I2228pСCC۷o˒$u!++K{ӧ $IR׮]¾kI,,,:v$IJ%Iׯ_~^}1c\vM;fll1c>{Оڶm￟S^S1''g666acǎKrclll``` e|__Ċx| ,c/gΜ:tӧOϟ?/?l߾]ggee޽{?/hٲ믿&%%nСCBVZ}_ӧJe.]9"K/:Tabb" {뭷J}SSz|[[ j455򊉉QϺ_}"##C9r$--؞#GӅΝ{͛WBźs΋tR* .7oQ B@аaC!nBXhBѻw;vi !Zj%533+:ٳ&&&rڵkj֭[uwVZ={6<<\۾f͚iӦc !8vآE׭[q%\,+W?~|lll=JաCw=y䠠 xVM0olll/_>|^ 菐;wն5id9qpȐ!m֬Ymڴ)^~}!kLSSS=]ȁjڵҥK=z4w\Z=gΜ`9,*22A$-[Ln 622 Byyy 8ɓ'Bp>}W-[lΜ9K,|K;xF166>x`DDDw1s̬Ϝ9s̙Ǐr믿.-C=zt>5jE!Dhhhtt|xرrk,-- ]'HMM-M%I9rd.122*J;t /\뒳K3ZML8Q$9Ӎڷok׮s=K'o~_|bYZZN6mB׼Eܹ#(<ɓ/~DzKII)'2e$IC a@y;;2(BFj !DnnncNN΂ ԩP(>|(Wkܸq/v@y_BYYY򡙙_|Qn111*LE̛7/##sڅ޾:t wܲu֞={Ν;W˂ |}}O:}Yy11+>}mӧOzz|kn8vvvw-m֟y .ܹSye9TB$'' !ze@@w9s!۵k7hРCݻQF6l0`C~m!;Shې?hhuw}Wqm???BS-h߾}pppjjj߾}o߮;Η_~)Gw}9Zzwg͚mW^TTԄ <<<}}}5jtR)oi&GGdzg϶m7h۶w}שS3fǗyu yB(O2֮] HMMݴiu.Y$;;{͚5_|E 3g+֭+xBpww/۳e&Jq뎒$â ={433kٲ_-P}ѣG;vX?={vlllHHӧM&vڪUVXaffs{EDD!m6{Wv͛7OLL>>99y߾}sټyѣGٳg߿W^;w oS{B !lmm7n|ԩjBFPm\СCvZNvkC]6l6l0))I>KY~~?Ogkkk!ıcDŽgRRR߾}{ a@YDFF6h@e˖-FFF!!!BT???//zyy=yDQPPѧOe˖͙3gɒ%>>>%޽{h4{njP(N>]T^4iےףGBj냃喍7j#|=׿@Y :tŊB'''eȐ!ӧOOOO֭ٺu6lذo߾.] 0`Ϟ=>B!Cܹsg˖-W9s{J5k,X`oo?3g~/^UƍpBNNʕ+u>ܷou֭]v0/ҥK:vbbb8* IDATK8,@@hƌ͛7߶m|cyuݸqcƌr{ƍ.]f"bٞ={Μ9%IBR988p͛7[nu%99wޥYT>}>~~~!!!Tַom۶=|p֬Yv۳/X@wgڹs˗KU9P22211yN8qͼΝ; !.\ _8ŋB9O4nZۢPV( IG%P=zt }9bjjjժ1Baff&XxǏ[N!R| a@YZZN6mB׼Hm;w!\]] ].a9s|WB޽;--M3uT'NB?~\CwwwyNl244Tjwߝ5k^zQQQ?##F9sFTfffۿ$%%mڴiԩgϞ]toammowԩ}-[422*C=}ݻ R:u{xxhw봰x#Fdgg;wNw49?7o;qDcc%KrӧKe˖+W$$$,ZHNݽ{7!!aoVj$yupww=n8[yQ0yXTGׯ_4i?`{"b4@eKKK[|9`4@eyܤICBF+<\^腐^腐^腐^@MbڅT?NNN>>>srrV^lcc/^\1!#~lmm4hP+&&FV?_ѣGB Fsȑ@sرEm[nƍK0<<9))u+W?~|lll JCPyV\9v}Č1"99~.  #22A$-[Ln 622 Byyy 8ɓ'Bp>}W-[lΜ9K,,;xF166>x`DDD^^nFݲe۷o:ʪ{~ϯ6j0bŊy999-C qww>}zzzzn&L !>|ثW#G\rjBZ=dȐf͚޽[[nmܸY&--O?]fСC_{f͚[AV?~O> Zpabb3/m699Y;h1̌37om6pǎ֭qƌ3ƍ/]4..n͚5Bkkk{9s挗$IBJP7oVoݺեKd݄Qaddּys!ի3a„ 6!vyr| @ @ ;q͛7bbb:w,pBk/ !\/wW*Y !#0iӦmٲcǎ/>>^VaiiY8!Djjjn*Iȑ#_ӦMCO!YZZN6mB׼Hm;w!\]] ].a}m۶^^^B-[?>Pc2kժջ;k,mKz&LۨQ3g(LyM69::={m۶oF۶mN:͘1#>>^Ν;=zѣ{]tnݺBw&$$̟?_ϔ۴iSE{]vڵږ>}|CJqΝޓ&M*53J!--m!!!.BPǼ<Bn޼7iuU3OFF{/^X+BLFcnn9HLFz!dBFz!dBFz!d et̙aNNA?TKmڴ 1t ]jKPͨ?'O~W $I$ !ϟŋFMLFG;vŋ999u͕O)ʗ_~>2lUqoݺeB*-66v׮]G'-! $IFFF{U*gsT?U5PyyyEFF?k4bl޼]vXTuRnܸa*o2P}:kll}ϨV,-- RUuV ]P2P)}իWOw>ҥK{iªJ%`4PwڥVCcc.],[̰UU;666@#dz=z$IJ>S*.QT񇡫jBF۶mkРF nݺuWUT*f2厐*ʕ+۷?p@v++g?~ dӪZ=zԯ_޽{:vڵk׮1F$CT- 6<~xϞ= ]K u};;;C,ӧ cQTB>/BFP׭[2勐"$lْ@"d#BFPT*f2刍_ WՆLF1 ^x1))Ѕ࿘/BF*\TTT&MvjB_**!!!??Ѕ5!#n…Β$ӧOܹcB@XT*!e !#ulll,#P^@m@9"d3B@9;ɓ' ]CR2児򔘘8v؝;w@m4u?7mڴaÆ@mdaa!OfT(|!#ׯVZГ].??999.D_...G~HHkX...5!#PPPhѢ7mеRdd(76m2t N:5 FriJ!<<|ƍ.rssTaaaPA(C=^2t!P\R KKK3tP0RP(VVVBFz!dBFz!dBFz!d9RRRƍ`B"d98immmBj/Zq:w-,,Lȧ\\\"nݺU899رcҥ}uvv~zW>k|__#Gmڴ~-55>jذ￿pJ*]lmm4h= IIIȘ:ujzzzKΝ;1J… y@`GļKe(0,BF#(((??U^:hРiӦ/O&BofرeѱctJաCX̘1#55u)))6 *:Tff5k5j~T |TG155]jUgk˗addTPPr:ifEwCbDgddXZZ֫W *!#tCW!׿ﯝrxyٌȲ}lRRRt[L"IҐ!Cr)>yDP~?X MZ`4?cy3ЩS>7xc˖-BZciiٻwoñcZhѡC2"==]E://Ogӧݻׯ/矩.\~zjjM6m޼"99YѳgbGSN- }F(׳tA\xqĈΝmugy_~WQK*"))iӦM=z8~"...((E?8yݻwGEE 8Pn \`9sVXd>L Y5HӨ(J:}t7AyѣGVVV}=-֮]vZmKJo߾'!ԩSN*ܻwYf?qĔ)ST*>uUdx߿ AYхDEEnذ>&&&⯏9!#@Y~嗞%tKJJZzӧ-,,JֱcG!DBBB9W T BF2رUnO>ݻw_Jo?Sy T8Pv*Yg^d{{gj4$d*$I2555t@2 !#@9[bɝ;w ]PI(ƍ7voBPm޼YR}G$?%I2t!~>GG?|Ν7t9@e dЅܰaòe:u4`"22PW_}֭[666.LHHHf,XtippG ]PGNNG}tRy}{i&CT8BFq_~yȑ*( ]@ ңGoPB@/B@1233CW!#ňTTBFz!dm۶]pAi:vرcGCW3腐^腐T'''ݖ;v,]o߾ׯ_/QVoܸCݺu h4Ŏ)777I$IׯKΝ'dggk;&&&|/RRݿlek4O?tرK,6mKuw T[[ h}||RRR222N^s΅펞?={vlllHHӧMVt|={l믿BꈈOOϏ>ѣ.@Jr…N``ó{̙?ի.Cvz'O:w{ dرcAAAJСCEGGGO<ի&&&e]|||rrvСC   _^=!D:u bݺu{WFolll/_>|K.{M !PvBBB@@ʕ+ŋ'Nhcc"w  ]EM IҳΪI&M2Ʀl'&&nذA{ڰaäRoggd_VT~jѢe˖e(>߿^ڹs 0BF? }ިQ)}M7ҧOFsμ  2… pO>N: 6믿;uhmfn֬/lbb"*ja.]JQ_JJBzVZ"}kJyw@dOOxSTYiYYY)JȴiӤNԴ^zZ~~~PPPDDDǎ~1GUd* IDATIub"<0 !j׮-\977wdJF vuu;EQ/tm[[[?|pʕt+W#Gݻw777777W*O^sssPڬY'_ti̙cƌ)Y(x[̿ $M3b|MGG+VDFFytw#Px\G7nܻwo{(+7(..ҥK˖-{˄wBsss;Efcccǎ)SuZ7o.X &&vڥ?{w~)STu...~ڵk6o޼uVmXF޻c tpp;J ,'W*AAA/.Ly;P|s)S|ǻv}ѓsZhVFaQf( vСCNQQ/.\ݻwzzzO\zK/dffVN~CU^P27nݻr~dOOSAQ2`Zz) 7^p&Lh4LTF߿ãG^xQO8aggW^=._|ܸq 4}_~e~ FJF>իo۶MV&%% !MviBXXX=VX痞nbb~ܹsڴi[laSco۶4i !]zjiKZYYM8q!!!nݒvINN^|yzzzZZZ>䓏>ᆱV#SY5(h__b=h iCᩕ{D2d>} <8==],(-JF طo)HPDDDh4?P,(-JF HOO?u)*A֭ov!w %#XZZ:88ȝ| &3ƍrgA(0ʕ+r1kN_BKZtiz{=dJQB(U c5k[n޽6l; J@#GU2 2yGG>}zjjYPJFP 0qSS^zխ[oٲej՚9sAPJFPթSwO~~~DZjYfƍ; D:??Bo,yP{w#wF:sss33}֬YSH(d͚5)))l]nQ2`Zz)`$5j߿n$??ذa2FB!ӦM[p(0`޽*J0aÆk׮oʘO jԨѼy(R4hR7 XdɦMN>-wFNNN3gΔ;GTy*111iiiF/t(Tnܸl2ݡgo޼)J`o޼ٽ{w9bO>~oΞ=ۦMwﺹJÇ\ՆΘ1CY-[S7geeEFF !tJmۤ8}޽{kOhݺJzO>fff<1-#JF طo_m5j?۹sg˗7klʔ)'N1cƷ~[[n]B1w\i/455B<|pƌAAASN:ujzzBDGGwM{ܹ'Nrbnz֭۷kZR}~)'''..nڴiM6z:utOP'ofȐ!;w޾}]Ξ=eeeվ} ._tf8w\i>ɢ>+!DVVVXXX@@ɓ;vh"Y={>}7пի}}}9"hܸnDZx9i}Toݺ%شi⥗^رcZ:ut!+{ݺuW\B/NZzk.# iWs8p2c)VV㺑;wtԩz~aNj*!Ğ={ä!Chڌ͛ϛ7Oy͚5KOOjҳZV߿@^|ԴgҤI#F˛?~|||ڵ֭[ !t3^1bbӦM.ZJBgdd5nɧOB,\PՎ5m۶E֊l/[XX#z%#ݻWjJ|yyy]tٸqk׼nӦMӤIzK: >}V6III͔͘1CJk-**Jq9͛h4#FH._\1viZ$P𫯾r:88^*d, B$$$H㹹6lV}嗭tܻwoԨQ_~صkW!Ǐuڵ;w#GM o];;;͝iiƶlRzҲ4YIϕ'&&>yF㏯z֢E޽{;;; !RRRt݅=z8~xff֭:rHiګjCii7%կ [o6}!(~7nI[ !hM4iذa+՘1cՅkݫvu!gӥ5k٦9BQQQ-0lذo@@BBH%`\1%т GIII۶mB/NU˗ `bb"-BkveҤIBG !233ӧO755 ܷoǏ8 {gubbbNZhӋ9i'IB^)))Gg8q fggKfϞmmm}^z]vܹ&Lnݺ=zH$׸qYfI!222̙#1{FR([nՕǏwppXdwٰa3fΒj.]!BBB6mq^{xьT<0cǎLJq)5k֜>}:99y֬Y/;%͚5{=H5BCC[jejjڠA>Lq+V5sywGݦMgg礤MJ;M^dd+u-,,ݹsGڐ:)))&&FH炃CBB[䰰QFկ_ҥӧO8qիϟ/M .|nݺI[H#G,곲=uT``#GBBB|||kK!GU m֬ĉ/]4s1c^=wѣ>,=-177:uÛ6m_n]݄cǎHuJJJrrɓuO[Pv0&,OPDGGAIӞ xNNNwT|MGG+V<]asss֭[?>>>666ŋ_+< AAAwx3[u3<1mdƖrJJdT6׷믿RP2_|FFF޾}[8M-.^(wjeΝۦM^zɝqttNQP2UDDDƍ|M!Ĝ9s{Rd4>JF^dɜ9s C+V;Wx޽4T!xbSiݺo ɑ1U%Ӽys!˗RP2`烂NJO>?u2;weLU4iڵkrB(P(J[[ׯ dOOxSxFD֮rST!lXhdlnݺ%w*MÆ ޽jRUP2_~~~RUP2iРݻr*(PP2%#۷MQzݓ;HUa&wʣSNɝ/֝;w:t'+1++wi޼qdVzu `%#XZZ:88ȝ2ٳgZƎ[~V]xO>1cFPPԩSN.0y;.Z+ٳoի}}}9"hܸnDZxܹ !Jׯy-!ĦM/RǎkժթSC_w֭r"((HǬjԨVNQU8iii_|EvvիgΜyƍ?trr* ;w\|yll/jǏOUj.]H> ;>N:k?3vm۶%%%ifʔ)4O>fΝyyy 42d~iУGڷo?tPTww]v>}߶ٴiɺuƍעE~w}'pssӍwر̔!nݺBk׮9;;Č1BzIT !4ѣB/[쯿8p`Ϟ=Ϟ=۪U+irN4Ͷmۂ޽Ijժɝd155ӧO&M,X }`jj)S-[Qwx{OT=ZӧO^H&&&KѸq+WH[nɓӦM311KzKŢEǏ/6lpΜ9}YFۗ ]sȑfffݺu{"qqqVVVR'(HKK\nݓ3k׮-^sssM7T_111͛57n^[p×/_a:666BؠH"Jenn) % 5%}B知9yҤIB͛7?Bk!k֬XN%{Ǐ4hʕ+CCCsrrKBVZF4*BTh,JOV߹sA 唝4 Bw<_~'66vȐ!~miNJդ_҈$*--MئM/bjjrܶm[9-ZPT&MB4jHvwwBݻSSSG)ǏjR*m0mii4.S(ʬ,STd{T*S_RTgIřO񨨨E(ݨ WXIݫ~S 6{IJJz=B/Nqtt\|y&&&IJСC/ѣ3ftuȐ!;3sYPj;t;EZ)=[]հј(@i899M:aaaQQQBǏϘ1cԨQ/:ԙ>}O"5uR.SRR=C!ĉ'_.-sΖlr&BkveҤIg϶޾}{^֮];w &H f!ݺu{ѣG75kVxxYeddϙ3GcZFP(n*-B?aɒ%GsΆ #Վ]tB4mtƍ% E\) JF prrv{ݻw֭[Ǐ(~͛RL2o޼:gpΝ`!DRRRLL̯,NKK[fMRR",,޽{BKZYYM8q!!!n B$''/_ԩS999qqqC6mZ``` offfkk{ԩ\x1$$_π !JBCC&N**J Us^AV !(rwޑ;ͪUvڨQ#777S@~*"JMMe1Q2`@zzSN+ݻw),--Nb%Q2`7*ݻrӡd42JF"T*!sChdEjڴ)%c^^=WWW??y?=}ܹ JF")&MP2<8}w}矫+VZXX8::ZJ%#@qBh߾}AABdggk4i<;;;11qǎ(CX!nZg}f}d5%#@ 5jTV+Wxxxȕa :u+@EѣG!I{~7..믿dIHHؽ{w\ 5k&/:u7ʭAx*Pϟ ;6AP2JF/w #@1(0U(GT*UzzzzzAd*JbF JF+JJF JFS2Q2 {Ed(JF(RJ={֪Unرӽt 6 4SN2&4u e˖YYY񘘘޽{+ 77?~BP(cƌp4S(ݺu۵kѣGG-M :qDە8"RhZ3P۷o֬Y[̿ $M+|î]yZmaaag~ӧOYҥKn[oO uuttLHH(;>ׯ7i"##J1v؈xRRRf/rRRnݲy󦝝5j԰ONN6x'< y_O:%w /*J^vM e[nuwwo۶ƍ~w re+ڵkC}w]k׮֭u/YfauװaC!t(Ŕ%Np(0A(_ Exbzԩ5j6L}޽k}57nիWR)VZ'[5Q2`wPj֬iccS`}ƹF9tɓ6maooO?4 ͭW^bƍ.\} W !222¤'٤IqƽK]v6::ZV<8778AAAA={1Ӣd(JΝ;whѢ gjժ'''sR*MLLzꕑ1g!DF+!uKk׮=m45;uߦX;yTVk׮K,9yٳ_'Qw獵i¨d%c8{)ѣG蛚wܹI&ƍ[n^9y͛7<|p>} .=\5vvv.];wƦ49M<رcaaasghda%#@iUyҤIBB ebb"[txIgggza|k !u?שS4A+ 6899z̬`~~~9) 3:T*URRRZbVBԬYS׮]S2~εjڱcǏÇ߿_^\9555//y&ORrssoܸ!w2&+Ǐ/^pš5kulPZ&) :ThfvRSSuOKbcc=<G>?~|MLLJҴiӍ7%#B+!D*AxyT)S̛7/88xo0^mۤzb ???wwwggg??7n̛75׮], }+?֏?H;ތ;666V?K.#-Z8s挽}nݚ5k~;wH;6qD!DJJs=zؠAuyzz8Az)99ɊRgor*h__ M>ȧ]v^^^-2}#T\xq[R?!/+V2Dqҥ Ο?nݺ˗/ }}ׯ4iŋ;uc[[۸Y!=rrroW_I >;y/ZYY%%%uɓ'_߰aC{{{Keee92((/ JFPB۷oWQQN!ibboveذaׯ_t'Oh'[^|T2>|~>u=zTm6!iӦM4 +۷o4iR9oB! ,BXYY7n֬Y 6|W&&V|ҥׯojZrihѢEڵc޽|ŭXB֨QH_|E^277W;P~!ijgH׷`GY]\jutt)*] 4irbG8q"33P{+WjJL0APꫧO>|x>}ƏtRx=$&T&''Y/7oq ܲeΓS \)H߹sGÇcǎͽ}vdd 'O$%%%#:H/; Z>})RLKK?&777$$_/o߾9sĉU*?O!DzzU߾}W^R.]}CYZZΙ3QF'N ~d1cƌ3J_BPhH?gggk4nx߽{)S"##-[fjj* +˥ak==juMX.mߧO1coeeվ}ɓ'oذA.Ovqq;v={z4w\iC%4nܸZjEEES:9:: !?#MKl޼vRlժUv nݺ˗\R;SP(%#@OV|>|8''1{U… 6lnݺBYf%&&BH*!SSW^yEg+Zg2o^wѴDW^E]}ȑ/9(G0tt |ʚ5kɓ;sss 9x`\\vYqxx˧깡C !rrr*JakkۤIh=J(>>>>>C[liW훯 ^zsa?SR=?|Pamm]Ǝ,Y"w :;;;SSjb:==]s77~RڹhѢƍ߽{W B]vڵW8  ڲeKJJ"##?0aS믿 !CEY`)Snܸ!]hĉ:_2c M^~oÆ ҷHIIٰaÜ9swLKϩh4O?TRJ{nݪ+ T[lBJKU*ǘ1c,,,#ԩS~}9'O6lXFFƑ#G M'Ϟ=[j̙3===U*ٳu& ?~BXv bcc^s(;wܼy#ԩ3uTԝ hݺg}ֲe[nH/ߤٳgz?.]vQx|||lliƎ[Q B۳2۹sʖӶs* 0`Z]ڇBƍ~aDDD^!˥ʮ`()w"̛7 jhy\j!wHFGG^Yfr\eVⲳCԥK#TKZӾj(!22200P0jZe@EW\;@5j-w@N JFtpss;%#@VVVrqd(EV߹s'??_ l(EVgffRMV !n߾mccS KxuuJFri֬Yݺuo߾ݧOʻJ6mv5a„ʻ| ץ)EPnݺxŋW% ߊ+/_t3ʋLWmʝ:P2CXX)`4(Fttt6mN(!))rѠdd4X`aa!{ZatjuRRRRRA30Q2Ð!C/f*ULLL~~>3 %#@yjJREGG:V(d4LT*mmmX֭+w@PxKe҆P2V6^-m(ѣG ڵktvvnذիUDGGS2,՚5k޽)011 S(r>RSSݻסC@7f2CZZZLL)`sMaBgee !4骕HFӮ];@7JFtؿZ; … 5C _knݺA%#@u}РAJѵjҲ#UW7nppp012PbG0/GT*{!K꯿j߾)P$JFtprrZd)` R dtqd4dW_ܓCX222bbbh(o Nf411ܹ[n3ѐQ2W@@@Ɍ2f/336mEd(:9LT*{)wjƍmڴ)KJF Pp2#dXZQ2Tmzxx(.]Z~:%d@@S,^XajjJX^xq-^c(ʕ+rڵk^ZnWvmTJ}իW_{ʻO)wPCEFFzyyu] )/////OzL qԩ+WV/]ԠA;;ʻʏ"""NQuޓ;ET/_P(**('K)K.VQ2@]vQ2@EEEuI (%# ԥKT*UǎbP2@]|\ (%# o}10PƂ\\\Nh =d4 萔tyS5ڙ3gLLL^ux`aaaoo/w Q~~ʕ+;t`aacF#JJJ>O>-[|ً/.\G 8իB[_=O6lP( B$m|N[[-[?E Nv* FsԩӧݻwﭷjРA׮];& 0`@E} t̙ZjjicA'Ndff.X@Ѿ}{!ġCGU*"//.kffaÆnܹ RT&M1bĖ-["""LLL2g!Df֯_/0auCן>}ݻw߾}{|| ܹsRYYP~~~bbbpppll5k|||oܸ1`H!ē'O*W3gtL (*KnnoHHӧjcǎ?LMM+=z?~+2pǏo۶m۶mG;vݽǥҳЦs !7o~֭s !ZlٴiӈB'455uww>h"KHHZbEHHHhhhvٳ={;JӧϘ1cM''Clr{NIIh4S*+xW^6ϝ;׾}{rXs !ڟ4hYoBZ[P(*_FFƕ+Wx d@S\SV ٯ_ӧOxʕ+JBBرc+;ɋ/ܹVp7o.W\uŜJF#BWr@}Ti+Й3g6lئM n6,@9RMKK޽{K>cB?HK+VVV"77WZ\|gϞfffǎثܽ{W1}/ ! 6lӧOOHH׿U~}!ŋkժk.AAA==={վ}{OOϸy:7|+HNNgЙ-44ӧO>}ӧk׮-6mZއr:{,k'PܹCe9RV BرctgƝ;w6olnn>tPi` x͢>oܸf*bcc>ܯ_:' X. j֭[ȝ°̛7/$$D ߱cͻu&w%#@5qѫWn֬t;zƂg2T[/^9sǟ\?<<<++KT*))ڵkoAPJ0wwwww{zqQT*ʁeLF={WAPF,w%#dvǏAPFف7o$w%#:ȝ)<8x`B!w%#:$%%?^@ӧOVڨQ2)СCB777(aȐ!oߖ;P#8pk׮VVVrAQ2@6;ʅzÇy dl8ФI^n( swwW( _~}ܹo+WС;4hDDĀի׼yq=}Tگh6m4tP???77S&''{֬YP(Jy*^Q߽712nJͭgϞuֵ?Zl޽{t~+11qҤIn ySSS?!ĥK.\8u._<44ÇB_~ҤI/^ԩǏmmm~gJܹs>>>sSAwbO<9v۫ҨXԩSGann.mۮ]t6lץݽ{ɓ'?[o j.Jri`4hФIׯ;ĉ ,(s+W oժF0aBxWO>=|>}?~ҥzqԩ_]-[x{{<ԳgǏܓf & 0իŦݟcƌ)ʪ}'Oްa{]N>2v={#((hܹx=2qDOO[nI o.W.H7Qo !^Z BIi{1bD\ U>|8''1{U6PZ[n]!ĬYknn?l2$$dѢE_~ٵkWڵSw&&&>yDIj׮ݪU+=W4o\Q^bGTW}UyQTJF`f͚ecc3yd܂s<޻wBB :T#PTB[[&Mh9T̂7oh4/,3uJLLBӧؑUfǎ})f/K#.oaw77~ME5nݻfXXX``bڵk׮]zg}v!DPPЖ-[RRR B_U!Pϡ1cFS:_运oggu֊JEHIIٿ:Ȏ?~|˖-BXiJ3fvd:uׯ/d>>66vڴicǎӿEsv.(;wzxx#GJê0S(;v F2qƇ~Qlٿ[*+KhF'%%e޼y!!!-99?^`4:DFFݻG 0r-MMMBdggUHիWWXo߮P({ʻ3ʕ+rŋ3gJ/?~ɓ'+]t7>N+*bd0>K.]tAJ'22 ,;*3ͭ/PB۶m0`AP(Y@zzzPIU7FP=ѣGUaÆ ݺuرAP(P3aRP2mݺC \6l]YP)r5… 7n(w Ǐ޹sO?<%#:EDD:񉉉; 222""":wlaa!w `ee#]vU UΟ?/wjnɒ%rGGvvvGʝ*DGG޶mAPx&#:XXX˝Փkv9TիW،9R D0dȐ۷o˝Ֆk )7m4e%#@UsuutRvvA*]hhJ?~AP(Z׮]333]&wʕf͚?~rgAdjׯ+w?\ tUĤK.վd\bŻ۪U+Q2Ƞk׮gϞ;E%:v؅ M&wTJF_iiir,f2dHn@iii111r@uꚗwET#G8q_ "~Z-w TgZjҤIu]1=rAQruu~9rȑ#GN<)wTf2)PUגq޽APuNNNNNNr@5:Ć ʝ=zTz AP WWWF!whѢ޽{C Rhܸ]uZ1ᅦ͟?_ jNe͝6moݯ_?Q2ٳr}]ttҥKP2ѣGqqqr)s;A,%#l\\\LMMիW'''4"##NK%KL6yrg<(ʕ+r@P hѢZjdT2߿n:oozɝd@77SFڵkrrrttAh̙'O;DVVVr@k={V ergrZy#GgϞ]pS\0\fffNNNΝ;Kxyy; dF@qڵkfϞ]pɩ 'ܸqclldz6oܮ]Q0wlذ!&&_;ri*Lv킂,uyyy}{챶NMM- WW׋/>}?m4{{{@~d"ժUKfYEJA?sXfff^vsrg)s֩SGz,LF TllB(vկ_߈VL_|yƍK,; 3'==}ݺu׮]|Xacc#TT} WZ󬬬^ĉBziHɓ'˟Fk׮yyy.]ٳY>}z ͛'w&Ð!Cn߾(zy@xxxڶm;o޼= Qy]힦M !,--˖FήI&C_ ]Y`X((bѢEҞwڵKG)!C!0Qw{߿/pss+wp%gggC.333'N{ <::Zyyycƌر# Qr,0 :u277?w\۶me dɒ˗/_p7J`@;v(c#""ŋ;::F1cƼSNèQ2WW׋/VuݻqF*#1(w P]vMOO~zU^رcK,zd@+Wʝ5Tv,,,rtbbQFgUEQ bjjڥKG6nٳ'Nvݻ{]!OHH477OT*+(-""BYRSS#""Ξ={/;vW*SSS+*ٯݻ۶m+ظq㯿6hРB.N$77M6 fff999F_urÇ'''xSN7o~׼*j$j& ?Rٯ_?Jm ]\h׮]**///==}ԨQtpp;wn5%#AXjU$ԸqڵksrrMFO޼y'GMF`6mxbuME>ydrrr=...!!!r LbҤI:tP*[4LMM+{UTvfggO0ϯBPnڴ)//NRYQ3wܩ]+]4}2;;B;|Ü{{… qqq/WT666ǎ *UP3Q2CXX)PC-[N: )wmffVpB3fիW{]KƢd@˝5TÆ ,YR 0R2رhJմiӃnܸ~??j2JFtZ(wIzyfy›7oFEEI?k'0FFF4YJFt2d۷Ndڵ`ZhP(y={HyT*M49pQ( QǍ'ppp(~'իW\sZJ!t)`XJsRپ}QFUTBZj6mڴʮHAX|ynlmmo* ssrںQصkѣGGYeWdPxyyUe)*Cv؋/ !G%ݴiӦEEE !M&5jTxxxxx޽{Kv֬YP(^֭˗ ;s̸^4^iPsioرT\{3@!% !ͼ{]|OM:>XvԩSkժ%ظqtŋÆ pѣot(88XqEF#J;{XϞ=B888p|IxBѦMiѣGWTGh4R]պuBBdffjlRV෣hΞ=[vQFeeewy%Qߴݻ_K,~Q7M;\ڛV,+W,hjgϞEDD||B ]|||ftcw5jv/x ----,,MiG-ZPپB:իW K:Pb}`ҥK:tHMM-E}|MQG 4IQwMnZ,/Y. VVVr()kk뀀wᄐ*Sb [FjذaZ.Eu23ps:Z覉7nݺ^^^+VHMMP:;Q$%%ݻ؏ׯ_РA_>??_s֭[׭[̙3~vnCe ZO>QwBbf2feemݺ}&L*ogժUOּt#8yy9o155.u%%%yyy 8nݺB33kiEaIdccciibCA=a4JQ4kW^)fffRӡ?[ѪUuiKǏw޽VZSN-t=$˖-+jTϞ=m۶O>ݽ{w˖-bbbGK2Kɘ|-ZXZZ}sɠ BBdggHQ4ͅ _}UI.m7MSSS!DHH囦K^iYYY[l߼ydd4ՄR?&777$$_/o߾ׯ_fMBBĉ7o,OOO۷իg̘C4cƌǏ`TTRѺukѢf2О={lmmWX߻w﫯j֬Y?n࿝iӦ}3~++O2v={#((hܹx=d$Ո!!!x򄄄mvԩ^zv&Ny֭Ȭ,!Ddd۷=YX` H]= 2nܸZjEEEIn;,)ꦙtرs&&&h8(>]Ϙ={ԪUk…z 6L!=cn֬Y}577!!!0=/111F IDATu?~k cǎ~;KݻwڵyW(~;//$h4Bkkϟx`[i/_(>>C,taIh:4h gg/^=zĉÇU,\\\NTieffj\p-))~v.Zql)!DXXX``bڵk׮]zg}v!DPPЖ-[RRR B_U-P*VZZBLFddd< 혗/,/߿?nܸ jjժU?nԿxT=$O<BSxb;;-[<޴ L2ƍ҅&N]M+:ueȐ!-Z~ګW/mjr%%%?^?~\jjbccu*c̘1ڑuԩ_RB|ڵr˥*UddU֯_߰aC僙'33׷{F; PnPy,Yr޽ B,@PI.^hѢM6; PX. vuu;l__ߎ;N4I,@9c&#Z?^RBBBnܸq%f}g-NƍgϞ5kV?humSmٲY ri~sQALF 7cƌiӦjJ,@EPQj1c4i2o<9e۶mǏ011; PP!߿7~;ʝX*[otRhloo/w}OjՒ; Paaarzg}ȝ _|abbrJh/w _~e۶m; PI(2)?>~xoo޽{˝<,(73g^fAJLFqȑo߾ޒ; PP222FݷoO?T,@eP͛n:2`4@Y:ujʕ|MÆ Ȁe5vݻ=Z,<(2ELL̲eNdɒ;wlܸQPȝEFrJ@@)^x7mT,l(2Jm߾Y9􌊊;tݪU^zE&rZZXYYkN-ׯ_WT[n͙3'((IT.Pؿnnnׯ_BѣGhтgwq1CCK.jju֍<{"#@19rDVK˥.\hllnZ\N`4@jѣG5dڳgѣE?>}wTpܲeK˖-?.O,@gPd(‰' WTVՕ îr8rB(FM4(P}Pd@˝"<<<'''NCCC//˗/jJTfffrN?}~ﳲ*-6PePd*77_ҥGZZZqNٹsgNNN5{>/_nڴvSL_r/*,5PQd'++_:`gg7o~91""BѸqc[[[!˗훐0uUVΜ9ÇN?zhƍO8Q~C@x"88ϯo߾ׯwyzRRBZ+-[Ν;B:uL:!!!hڴرcccc^ٳgŜ5 Tؿʕ+޽dɒ sss!B>}c5Fyk׮999?^:ԯ_>}`*"#=JMMuttlݺu:uJу=)))B ki``M6qqqFFFTiîr@~cƌvZӦMڵk{#GGGi#o . <… {׿UN#&hryS@-[nݺΝ;|Q}uֽ|R@Vk=QV?ѣǎ3662d&<|x-@EHCCN:(2*KTԩSG @G->*w R;PQd@˝e .@FPd@ 333{{{SLΞ=LFPd@ //۷o˝eT*4iҨQ#UEFP5@FPdUJR*<@t4@ RiiiE @@TAJSN>4PEDDV4"###..N(Ą@aaar@))5j.(2FTiӦf͚r h/w Rd4P.rqqqqq;J#55ʕ+f͒;P0T)gΜQTrJQ*oAj"#Rx #P(2#''JFT.]@%RY~͛^(2ELL̲eNS*:uR(rhqʕSN<GTqqq>䁌@QQQr@DDDkN @C-URjbb"wڡRZi@@UٳhȂ"# "## EF@Fr(QF~Z jtӦMrvJJ @uD'N;ڳgω' $whQdz&44TjJPoyQFx&#Z>|U(gff@F@."%%r@qEDD888(2)P\Ji(2۷NbQՑQd͛O<Ȉ"#w޽h" tPDDY6mT_P>|P ݸqc|K݉4cǎwm饎#(h(:(2@w%&&V?|РAR_h"88H֭[Jto%kku1X!_E@%/(2@GݹsgȐ!zZ޵kKwfjjZ._W%/X`(˗^": ÇnݢȋEݻJs/^W}o]Imݥ|HCCN:֘]u7NړpBWW=z\zUݳgϠӧxHwGYZZ˗/{zz*>}<}t鶶۶mN߿``СCvze!DFFƎ;wކ v!&&ҥK={h߾}ttBV:ujڴiM6{n^,--;tk'ſGWMMM>}z@@ԩS{9uԔM{M?**44tĈ]tQ3fLF=zԿss:\zqmذAP( !DZZZppf`|o 9 ^|3yd77Kjm!###..N(R_jnn.wzSݻwjEѼyssnnanܸ!mzzz۩jٹnݺj_~}SJըQ[hѤIW^͛njuVVE-nccc{aҥϟ?tgϞj:''B/ĉ;w433322$ w ϼΝ+m&'';::6k,%%`?O>MLL433B̟??>>>,,L^Yλ˧/(xlOOϡCT*ZqF!ߔU@ww.~|8prˇ?M0A>W3 Mzɓ' zKj*JuVk׮Qxjٳnnn7oΛp7nT*H/nhܸq]kkk+++ͦ"33S\jbnBI@y?Ϝ9Si !O^|C{w5C͵QFj޼yw"cIm1BF򜘘i3++kϞ=+$-EFallk."~_\zܹs-ۻwo!7|Sv)St!##Cr9mvկ_Zj޿}gϞmݺunǎ;hРիW/X 333''G/-ը]vMKK˼ 5jԐ6+(\w T*uҥ"22`?wSPXZZfeeh\s_~o2=*ʚBccc++_(ٳg=<<Twܹw\x{ϟؼys ڳgɅ/?lmmipɖ-[Jk%eװaC%"9Ixkkk!E)ܛUUҋ$-uVqqqYt)P;;;[[[EF.3ggW^-[Ls(::z͚5BE9::ڵKR͞=;߹Z?Bٳ˗/7o|ڵsj( ///gOVqR%k׮V#[E(ILLBxzzRK9k4mPs~qeT._&bѢEٳ( IDATRٹsgS]ecc$O> ,ݹsY&O#XjǏޖM4w6v0`f1o!>}:**O>޽g}ĉYϞ=KJJR*6mJMMB={611Qgegg !ӥMfD4uT!˗/şŸ݁|U3cƌ-[~<]s'N,؏jX5#]ku\B ܺuk͚5ҭ;tJT{/XhQi3gδڻwo=֮];{qILrQT]@:jŦL5j9r_~?i=zsNO>Сv?;`yW}ӧ~;KK~mٲEb &4o/3gNZZZPP">>><<СC BOYFX?yD`ӦMO]f͸!DBBªU222y ._W)))yOZN:5dȐ#FL6mƌ?rHfff~222/^,~HHȒ%K-X 555$$$))IW4.iݺu :tСCSRRrrr}q$''jrss?Zj !lllΟ??`7n̙3'11q۶m ]~=--2@Q#44ۛqrryfB5P(v=h G{J:144U?֭9sgϤ(}BJeǎ0? >ǰjSx ;(2HOO1c1cDFFʝ|TqJwĄ<̖/_Vj֭[NT>P ]rEzitR466СAA#Rٺuk^h%w Rd4;(2UvNRSS\[_A3gΨT*f2"#3JZ EFg"""@dgg9s2:"#'/_Ƞ@DDDԫWEr_îrJ]PQd@˝ZDDDV5^/..Ç]C-n߾-w ajjھ}{P*ڵ311;A RZi@ܽ{~;@o;ٳgы/;(2#((hǎr象Cn߾]xH!3DT ;@/ 4(33S(RtrrWA3~`#(2EFFF\\)__>wEF@7Qd@0S.\!wZPdz ""wޑ;-(2)_JSNro@ S@Ў@ݼyɓ'<Yjmr"DDDԪUM6rEFZ[)JCrd_~EP4R-w oLF>> KRjZPj׮) """lmm4i"wa4233EZavv"==](UTBgϞ*7n !Ξ=۰aCKKK,ׯ_K}J_x!ʪQFeT*>f2'999((H~С!DPPӧO׬Y/~bŊ&Lh޼_~iee5gΜŋ !߿d $%%I}zJT*Udd$EF@1?+VXbf].B'N81o?g}&}^dɒ%K45'Of2)ƍgggܯV;Gh"wjQFv~|5n8CCF͝;W<J.޽{ּy]ʝ`177w֭5k֔%"((hǎrPz&&&_;*J:ujժKiO>֭EFT ; ;*۷ lժUr'P\<&Mh>^^z2P"d:I&FFF K.Æ ;`&#Z,[LKFrrr źudW\ 7FNNή]oܹ#裏?~q㔕!CZ6bST8p@Ҳo߾r*EF􌊊;Bj E5 VVVVڵ;ʄ"#TT#P(2X~ аgϞ{իW ݻwwݪU+BѲe˗/_jN  ݻ;P(]\\ _SSSB?H Enݺuֱc\tIgll6ޯ9~F͛7OcܦO^J&66vŊ;W7ҥKz*3g,.''gƌݓ+"Pd/nܸ!믿9r$>>W^'OBDGGOoZÇUR奐8p`)O??x oʕ+ӦM˷`͛7׺I&N:p#^zUeVVV۶m ={v>}*]177711QZ=0!{jJZZZ:tݻBK>\zAMϞ=5-Z(iO>DUњJRz%K>ŋ'Oji)SjuvvСCU*ZewB5o\t>%Wph'N>|xvv_~ennk lY{V+!ʅ SeD+ !v+J8*H?0*ޠȈ2*EQSRTմ^V;vTBYVV͛|yL*2JGJUnݚ5k4^A**77733sjZJ^eWR綶z6.\*Bxyy2*&[5(2D(2BgU7a``РA)Sh]P(6o|ڵ#FH K~_~e&M|}}No>6rvvBXZZާWBJBEOXZZ޼yS9vիW?<333''a>|hccSyS6V}{C"yQ!TfBPͤ+ԯ__s qEeԹ;;7oi&U,-->,dh;zxBR;TN}zO?-u>>>Ç߿EʖT6'Olٲܹsu%ehhXg={~;}􀀀%K̝;W!-ܾuV*fR{BK˲+ɨQj׮f͚yc!ox4@0(Z=tПY*T*CCC鐳͛tb źu뢢Ν۶m^z2LU+#B!MnZ7p666=*uBRl*ڵkOfk>}zѢEwǟ;w._J^I,,,Fy1Qic=LF@t!Dfffޝ_/LLL BXrؠA&OÌ !īWԳYv={ԬYO?ͻ 9_0½xB^s MR:*Y&))M 91;;;oQgϞ%%%)M6 !Ξ=X0]tyⅴ|lV'/^|'Oo7oޔ9s޽{{vٳg7N-^iӦ[l)4 褗K;^iL4)==M65c!DΝ+|<G/22r„ B-Z|ᇽzzׯr=zOBѣGjԨ>sNNN 4عs pƍ9s$&&n۶MzH{޽+}ZլYsŊҫNfϞ|!̙YxJUf>>U"## 4zý{V( [n]vmӦG}nݺ%UVl̬}[S4Gw^N =Z#P}]>}==='M$ ?To޼yРA:u*m{@>]eO'c۷ZJR*az ?+::յ+I޽}}}tA]ݻaAMJ*M6|Κ4iҧ~f͚I& ![ŋw . 2Dywu/^T>^mT"={L"Ȩ"~_1nٺukXX\/?aF!m4n͛oiW{%9uTllʕ++8#sTPjBH}xu~>!>>;v0aի8 XbtѣvhӦ֭[-,,N8!ھ}oÇN:I ߰aCHH{5n8RYo׋lT*_~=;UV366B|ꫯ/_n`k7 9T 6mRT>X. ...K.;EqIuJΝsvvׂ)Vk>[ZZfeeϬdGG›͙3k׮ iXYY9;;O8QzϒTE=}ȑ#,A ?T: Ȉ [jܹo .L;zhvvB߿t…ׯvB3g>}7117nr@@Uիe˖iDGGYF/MN^.f&H9sƍO8Q3o!ov޽9shfM}!ş mmm~m͂B(_@eff<>}8::.XwΝf͚şHM ŋϺd>/_yjB .xzz_v.Z~Ç5k֬YW_M0"88x˖-iiiBW^M>رcCB׮] X`ߖ-[AAAϯY&RDDĖ-[ Recccoo#FiZ֪U\zaddd~^zu}ٳgW1cСC:v(53fBXfͅ 5^,P;c!D||ʕ+?֭[ !&O>I&rK1>|8h JUB%K߾}{С FFFUj)f`%#TqS߿]v믿۶mqƓ'O<<<{h4zzz{9tPvvv˝>>gںyr"#)J''۷ !:uk׮~xTU7o200і-[=sLkkkOOO ._l߾}}OHHx @vikԨBi֬YTT.]*Pk׮U(999ԩSBB~͛7sɓ̜[l)y#6(+OPRP\B,(PaMy BWuEFB6l)+(2arss EFKMMBI/333&QiǴU\\իW+xŬ, hb۶mJRr"#/[L#˗!|}}SRR/-[fhh{\7::Zu=...&&ӳ3BMwر&Mj j @f 4X|Z̙gǏ_͟?_ztu~[l3fLEGpԩƍj V2B<|…AAAr5kGɝjV2Bܽ{wݺurO.wa(2BtQTlP!dn9T_-[T*SL;7ߔ;PE(2P}u977W;,,ڵkrܓh]XX؀N@[XBDEE%''˝v~u@[Xɉ_Aj_~ĤGr-d@Vߵ:~|||[lիYaaa{ӓ;m^۽{7Ohj.{{{ nmmݻwo_eee?~|͚5rEPW,[lٲer K.+N>S_ڍ{2[~SN+QdPO @]?8P"#:gƌyA޽{of4PQdP縸rj0SSnݺvQdP(iӦ}wrgT*Ց#GNQ˅999Y.9wޕ;Hq JuEFuѤI233ydffΙ3gĉYjӧOgffRd(OOϧOڵK }VX!w,,,߶;qqqh4rg=##cr4hʕ+rn޼y}@BDӦMGfZ-whKӦMut+*@>{Cxaaa}U*rPu(2Bo߾_~~~~rjӧOWk(2:uo&w_sss)2u EFNNN:t;*RNQuܹqrP(2ܽ{ 9uԻw.:|0:"#ѣG[ZZ嗕5nܸ#F˝ιvÇ)2uEF?zzzSN JII; ɓ[lQ(rgs~MvE EF/jz֭rAhBTʝ. ۷?{/5?~nnY$%%%22@D >|w^5ju@W_}uȐ!V;PuL d@uH%v駟ʝB>|@EЭ[w}O (7|3jԨlZ-w?|@h}پ}޽+w__ti`` 3?laaѱcGP?E֭;գGٳg˝Bֿ@P4R'|7IIIrggrCF.Ś8q͛TkǎS(};Pdb}RK||֭[^B5jH dCJ'111$$̈́ ;&w%;;O>ՓF_+ qrj{JR*ܹaÆEFFn߾] (˗cRג"cZ]v۷ooܸAPZ?[o%w/P(6n8|uF0 ߿#k(2رcذaR EFq.]b4 EF(Rc{{{===!Dbb܉j[n ><55U (&MÆ #Gtuu#w.ՂT;;w̔;HuŌpڵ@++~ /xǏO>ĤiӦ[=N@ftqɝ ;EM?"#ر4LLL&77W ''z999;;;˝l(2((++KhmO)ql!ċ/:t!C8:{2311)Aoe)Jzzz&M:thGP}PdҰa-͛7_v,yT@nܽ{w> j"#(SS/Jŋ;v(WOȨȘ@JFP*fff pΝ::A 7T AF-w+hZ^fM6lإKF#rttTr޽r\ۻcϝ;G+ATTԪU+>sssg͚6o4zEoؤ$!D6mjull wɒ%чB888H=n% 5}c,^ҥKᑑB˗O81555Y+W,onݺy!i!gvvv̢pӧO| 'iݺu˖-5MvvI۶m333|utt5͆ 3fh4999}qssST&((Ho߾"#M0w)R>_:BzrQ{'yviU$77'((ή\p|#7jHBP4k,**jB[[[ K.rk*{u)!!AMaܹ3yd333''g͜9s̙e|9==JEq%B$B(Jss;w888~45eڴi҆uօߺuKg񺺺={,2Җ-[J. ,Xqܸq϶ڵ+"_* SS{KP(y:XVϻxI^U1/jq/_:bKLMM+2ɔ)SvZ__ܼ߬~ŕB˧O !#_666R7===wwWIPj6>T}J qqqH%P(Æ +}"իWW^)ܮT*+}^qzF)I"""F4p]vp7klĉgϞ{˽;wtС%D8h/ DDDC:-- .0y*֭[Zaݺu;sLv++r_LZYYypwwwB1p@!Th4ZJT*յkΟ?oܹ.]"x񢫫ki"jKЮ+V!֯_~uM6-,,,Á^yۗB4effȷJN:*5zjY/%/͜9YaB355UʦYH,u7BNNN I"##lْ"bcc ˖-;x`DD/r۷oKϝ;QF{qrrڰa=<|>WsEEEUz%K3&q:::666Z*Sϟ/\0((LWT+@?3ӵtG-]رcƕD5kny9sرL޽nݺ;"#歷/6lؠsrrvرs͛k#Ɏ;NܬY=zӽ IDAT\|988_cǎ.ҕ;iZ5k6F^ G PS@PdXjU&M R۷A>VP"DjAtN2001?<55URI-G駟֯_:r7n̚5Kސj4~Mb5}0~zSSׇr(fPQd\`|ff̙3 ttt=zԷo_ooo+++騹q;+##C^RKWggg !rrr*8#5EFjgN6Mݶm~}=zhҤSHHH޽cbbϫWJg]xÇB^zGDD̞=;66V1y]ȑ#|>>>gϞ-2ɭ[|}}q㆖@fr+ψ# tҥK.6lأG+V>_~ۺukIx ___.`%# B(2*"# B(2j*Zf͚7lذK.!!!߼yyzeoo"DEEZ/Z=onoo_d)dxxA Bxylܸŋysssg͚Z(@WPN3fHJJ:ujTTTPPȑ#SSS'MT)oڴٳgiii&LHMM-ɓAAA۶m+ĉ۴iSh^H}}nݺ5hРE'NBC͘1cʕ뭷gWZղe"[("#):::!!aΝ>_~V"c@@޽{ wqرc\_)WF^ dQ!CtҥcǎC~|…C 9|Æ l ?K>|O>7~QeP(;Vnjnnn^YWt ieeoݪU+ooZC@RN<٢E8GGG[[w2KvvCIIISC<<<222|`=zS{2U|||wwwB1p@!Th4E>ZR]vzΝ;?~ҥ!!!/#a!5w\ӧoVY_ Y OPdee !,<<|ŊBׯ_~ݺuӦM +m̙bWaBw={$''EFFnٲ%%%EoѲe</={ΝۨQ={899mذaR)xuB̼˗/ӧ?Iӥݺu+e-Zضm[ij=V2^^^ǏwJ١CxɓݻW$nx۵kWرcnzܹ}J-V9si/^{yyuԩ7l0iҤ5jGf:ŝ } 4'~~~{6\y̙[ !bbbze`````7bĈq5lذgϞ1bD-qqq111Ǐ/ۋ8}tT*Uzz,*?#wn 1bD z>|ԭ CKsbmڴ/BZwĉ3f$&&VB[XXlٲ7o3ҥK,a EHHԭ0mڷoΝ; 4>{ܹs:::}-r{~ҩSٳg˝yJFW E7oUm۶uҲg-Yd…KVZwJEބB;w.**JZYd.\TaQÆ 6lXF{edP}w/EF#oQaaa?Θ1Cb)Iw\ftcDFs9sرS*&/G.]zر[޽nݺ;~Xh_|N(2@9[_|ņ Jʎ;NܬY=z\|988e cǎ;w6o޼!Dǎ˺:ڠT*Nl hժլYJ¢LEJQzzz>>>%P+jX\o;wN P4TkovpphѢYhܓjM6СCy j"#݇~(w( EFAppB;"#UaҥC ;!uH@A[:?~ֶ>.]uvvHPY(2(>U*Aj5k֜;wN*wI ơ ]]]2Q#]]]΂jMV/Zhr2@gIGj6*"#U;T&TO9Ν;rgJÃ_4AAA%wqѣG?'N8qjR@-[,)-[T*SL)M5kh;TD~ԟ"#sιrypOFBHKKSr@w;EF*Yjjj>}^y1cȝ~?~aq:"#CrGB(2SnnE&L wEFiѫWvpp;Ȍ"#4{+WL4I 3. @9uIP-@PdP!x }kT*U)O,M6͘1cѢEJMM- %ț9kW)Nn(8p ;6""BQ^M>}4'I@@޽{7HqOAR5Kܫ|7"#T_>4hP K+KLLL͵1rMτ&MT|x z{nl*߰aӧO?{MP{׮]B\|믿VTƍٳb۶m7nx䉇GbccgllܥK?SDFɓ'[[[;;;w*СCU<<999=zP&))IѦMP5M6m:$޽t(DZ۰aC!Ē%K>,ppp(.dl2GasrrR4MPPb߾}?ϟnz…ҡu֯jrrrYPA/^ppp(P|'>|̘17oޔӧiӦ)))'^仚 &ܼysOnJB9rBVZ}Ϟ=?}ߺu-[j4lmJPPd5EF5T^B 6H_Tڿ``.+(T*Yz<_TŅ,*E,nիW !nݺ%o ?ܹsqqqyG;!wYPq/=" iݺ"))Ij,fԔ]-0,s gaaѨQAAAFRlRWWA[`4Tvڽ{R**''g׮]B;vIݤ%ёI&/^(/ ivvvgQܰB*T !5jTB##iٳg7 )_x]v4hPE{g~`4hi}SL>|ڵk}}}rssz~TPuO~Ջ/~W_}Շ~yׯ+J<5mΝ;e:K*EGGXXX!LLL*3ʪ߿Q'hh߾[ Qd3dEo6mԩSK8+oX֣effJ/hS:w,XtiރSw-}]uv$B>}Tl[h!=-Rkŋ+Vkq륯Oj)]E{炙B8pj$PkPd;eʔ_~e֬YBGG6mI !ҤVVVqqq|ڥ>Bϟh;Q*fUE]srrZڵk'sSRRGQT ;wFٳiÆ jR?Yڷoc u6}2M;6nܸÇDZW3333##&M?3gMvҥ={6oرcE;`>ѣG/_>v옥ƍ{2KSݻw ۓ&Mzi``qq33Z__Æ +_:kٲ# }ŋH=LIIR*Æ ᅬ9rq 6HwW?q!D滋پ}<2=UƌSY`.󏯯sRSSܹqFF޵koV РA6mԯ_I&O3fLϞ=5hР5k֔@`y3)BCCGQ¿^J@1b飉Q(!!!y a4 =*wC(2j{g.\; JFFƳg"##uLoŋNu +TEFBP#8po߾YYYrPd@k׮:tAJJY<P5M6;@V2jMRa"# Nx j*00_9sJ; $g}v%R)w@Ix4;TXoʕ*5tQxT.PիW!BѠA S%'''$$ȝP~9*Wm+777 __xYG*_ƍ3*_GEFFƜ9s]V\Fcoo+VTe0@/nnnG-ШT*ojj*K*3lذϟ8q>zzzΝPӱ@Zvss%OS}T[#&ׯ/c駟999KJ_5k7@K(2 0(eNNѣeS5,Yi&JQPnZTڢ?|p===eF+o-( IDAT$m[hZ.ЮV+K*Qd-=zkXOOoȑnX|3gh4Eh4sʪT*@\3|phё#GLR\Qh?~m۶* "@Jرc͚5stt;DFF:.iBahh/466t OnnΝ;333RKH%o޼Y,Zѣ+Vdee)ʼM4hڴE ₂*~V|@Ç7NMxxxxx)K*/}kرCKʊ"#Gsps=@^Ç2PpOFB@PdP!TEFB@PdP!TEFBv5kִo߾aÆ]t h4!GGGE!+U켽+1ܹs+qDEEZJ;77w֬Y>,mի͛7 'ђ"j4]f̘4uԨ#GN4)))+Wlܸ… Z*U,,,J366yv횥eƍ4{mll\Hqɠm۶I/=bϞ=?^jU˖-:oڴٳgiii&LHMM-DKtuu(2Т脄;wJ/?~ZjҤIױc*BSNB(M;6""v:ykdɒ˗9ssKU7n;ʕ+RKgQ\lss 2 6:X[[Ꚛݻ$Sd$@MviZC??}iܸG#G_a駟\\\fРA %Yd޽{:$$$h¨Vnjnnn^E ~VZ߅P(* GthQ---dgg;88ymۖ**44tܸq={v O<:>>ظk׮b۶m7nx䉇Gq=~xϞ=FOOoϞ=zɓ'===[hhkk[ň)B|r+ecgggׯ͛ݻw! tIQQQsqsssttzJ*Mh4~~~F000Ȼ"##cvvv_~Z.I[45DHHHzquuuuu<@--""B__=zѢE6))IѦMZ+m]dIttÇRO[ CM>}ر999/t钱qxxxddb'NLMMʕ+[nE^hȑBJEاO;ze)|NҺu-[j4lmfff&$&&j4 6!f̘hrrrR4MPPb߾}EF0a;S70-}tX!"@ܓ@ +p(##.\(ȍ5P(666͚5?ҥKjڵ "''޽{:uJHHnSؼy;wL<ɩY3gΜ9sf2;wDOORfQ\l"""ǧ4ItttJΝ;߄0Fcdd$pqq6mڹs֭ u4uuu{Yd-[T2{Ȃ ǍW?; koY5 Xn4D#` \k@%jTT(`DƂr5*7(-5bPPyd׬Ys̙˖-[[TGGG{{-[֯_ǎ 4t钓SttO\\֭[U">>ÇӧO/Ҁdصkװa ܽ :TʧիPd'$$QvgϞ"eLzzҥK SN]t ;.@EʘcǎknРA Td&r(~Or3 IFEB@dP$$ IFEB@dP$$ IFEB@dPVT6mT^cǎaaaj_~lkkke˖A Ǜ6m:tm =گ_?BP(zѽ{?q͚5o޼Tx葁r0;($77ϟO4)&&&$$d)))...ڵk_x_9uTHHȖ-[刅֨Q;;;~ ޾Zj͚5;yBR:tmҥ?S۶m&&&|-[{Z@$IO> 6?>},[+WlԨſo n=z+W̊EQf\sYjU!DJO бc8͛Rڵkϛ7o.\^z%hc42ѣGM:u<~BJ5jԸqj׮]\G,v A~~~۵kgmmn JRyԩӧ7k,>>{M4IJJWK..̴ӭz<|PC\]]_~Ꚛ/_??{zz9{׮]#Z0bWWJ*)&xǍ7}Ν;/^XRh ѱcǴ ~Z2{O>бK(֭[uVoo1+ϝ;͛Ϳ?{l~oݺusBtA{/Ro߾u1BZۣ`ww'OԮ]m۶'Ovss 4h?`dd~ &jj<¢^z7o.U*w={nӧO !TT]ƍ޽;a„Zj+fggϞ=;$$sκΚ5k֬Y8d̙FFFBccڵk߽{W_~gZ;!&O|y!DppѣGo߾-3vXnݺciXZZ !"""4IF 6(B(gH2(EZZ OnjS2A"!!A%'N|+^x-ȑ#&:88!N8!7x'33311.ɵDP/J'@ٶʕ+{{{OQ*U6mZ1bĈ}ܹ 6tvvwޢEϟ?Bܽ{>(DH R'Ndee}gڅc"j$$a~葏$""k׮u LhР'OKƍP(+R{j:S+7n\pE:tpႿXX41666**jȐ!0 s1g++)ShK,B J !faG]dbժUV >>͚5۲eK~{$ iii~~~B3g^rpȁ:uM6m͚5...E~~~B\֭[uF-^"Ylɓmllϟ_f͙3g.[LG...-[_;4hp%''h[VV-πO<9uT!Dlll@@իWs ٳ_Çq>} ?lذׯx "22hذa?|pR@iصkװa ܽ :Tʧ|xӦM]ܱP0@E2q!C 0@X(H2<<lذar@F@5gΜke˥TPgΜYz?Pn]clc&#(--mر 9rܱPdPxb͚5r@yriٳg6lРAc<`&#͛7...}7nܱPN0@뛘xq Zlի5j$w,,PQ3gϞǏ;f2(.\ѣ? BX(WH2.]hѢf͚ ˥YYY&M;!f2(/^|Ȉ?ʹׯ/X`ܱP>xʳ1ctiڴir@LFo߾}JPrH2(n޼ |R*ڵ9sܱP1@tڵ+W 3CsjٲܱPdPH oFX \{% YddO@mWtRTT Om(/;w>3fP4:tI2({yzzzzzmVX( z72dHIEVǏocc%w,T P~[ܹs/^d4o/~PNf͚5k| w,T,$Bf͚͛7OXpXA<شiӧϝ;WR%ca&#2/..nƌnnn;w;*" )111,,߿T5*&WWW++ IFT166NHH;}}}m۶B7"ɓElJ{:w^@Yu#Glذȅ$#Pxqqq%Ν;{Y~BjjElD]=t*jeZE9$<~xԩSL;*.@!=x/(}juhh# wVrݻMhlsҁ<==E9֭[w…r@ۥxQ~Je{ʕ>G)cF(';w}zǎիW711DHAG@wW^5o|޼yfbbb}dv?.RW. Bٯ{Oڨ 222zZ^zMZVVȑ#JZ B߿@@! iXXU eȐ!֩r#ÿSPd I;?xZS"00PTR-G*:NVZZZjŋ;wi&ZT*{=\1 J_얖5kl6o\!m !Əc :ڽO?ϙ3G?!uѵ-ZhR,--엍hodӧBMLLB|Gդܾ}[ܴiSRR$#|ڽ{B8~܁\!riDEEn:Ƿ_~BkVVͭSNiii5j=:.d}!Dǎ曋/~N8qС+VΖsޭVbd###!9`!f)tG@w;wN;hJu&m'G״7 EfffeX&SҬIitw}T1l0 M7:NNNżڿpo6|O>䫯Ztӧϝ;aÆ/_ !.^'vdͲҦfDyر-[Θ1Ckw2@#w91>mڴYr_%}zj{{)S#ZAMOO5+uB??wZJ#G('N… <q;wh_n]iJ9sj֬gϞ^z^UJÒ&O|ժURlϞ=aÆgϞ={쯿pB*Uٳg !>|]ǧjժϟ/ƌ3sLu?~<###G;iii .B<~8((hѢER^|/͛7%GAAA#GҥK֭G鲵MMMuqqqttoٲew!uF&&&B \t)::'..n֭9 r5c cc+V/-:tb׮]%ҥe˖w):Ҧ}gϞM>]RO<榝`B n9tڵkذaܫO?4hРjހ<G N@Ef"wPNΛ7/))I42270ʇI&}d(X. %ѯg !5ςܶm[1 CTIFKMMc8a„H#*%ׯm۶M4i 6ڵ˗oޢE:֑#G6nRvmc`4U^\,--W^]':99 0@X@To޼yfz1@u>44N:rb&#R*55u„ >|ܱCH2(^zS(X. 4Xr͛ׯ/w, dPyťG;7f2(u|||>}zIB@rܹ  K(E]\\z,w, }#GIFETTҥKW\٤IcriBffs=&N(w,`T ܱ!@~.]Zpa@@@f˥,++nr g2_zxɴi'wJ%KܻwFFLL*$e>ƍo|8֭[I2vu??Er 6tbo$!++k̘1:u>}ܱ/d}UJPC@n7oܱ"!@Jٹm۶f͒;PT, W^|ĄP1v]//9sjJX@1 RTcǎmժܱo՚5kX( @LFoٳgkNX@!-QǏ;PX- 9{쯿jff&w,8dP"ju```TT͛3335zxx̜9?;PH2(O<]v۶m'O w\@j/lܸ~+w,LF%VIqyټyS6nXR%cŏ$#aggVÅBxь3O޹sgc%JwÆ ###ݻh"wwwpuumذ܁B@P*7nܸpB-i?ٳgY( @9F@Xp?Fk~w U ӦMꫯ˞={ xKX. 8U\9 @@LFf߾};vQܱ رc׮]l&''O}СCC ;n +ضm۷mmmEǎ7nlll,w,XgҌ$#PTv;СC@Ybkk>4/$#"!HH2($#"!HH2( ˗r@EQ~vsݬ8/}Tc>> **00M6իWرcXXZ>޽B qΝ;k_?uŋ/][nk.qCdgg{xx}z  I~zݡC>}޽[sӧOO8Q꾋˭[3g 8PPtm6333,YO 2cƌe˖#ͧh__Ço۶Mwޟ_~d@mmms=Y|f q~ w?\~se 2d PKdԩSZjԩ+WB_^V߸q]vK.7WWpG߿… 5k֬VZVVVrrW]N<9bĈ7oԪUPϞ=|7J5jԨqƽ5>F@Ү];kkkk222%'#×>ifЁIII?K";vdgg'={~zÆ %q8!Zsww +W~gҦ۷_z>xiӎq2]*UQ]vy6ѣ~=}4?qBbN+=c (LBt%GIffn#GXYYjժP*{ 3g:tСׯ_'$$ !-[Vzu]>>j_TFDD߿#FxիWg_h={Vaee)iܸڵky"{\rذaڅGcQQQǏIhh_ٳJ*{ynffgvYfY3_~E맃pĉѣGر{9lٲ֭[殮<..6667nݿ~3fL:j֭k-~֭[Kcbb曖-[k.ώ|rƙ7n?-_~v7n?~FF5Arĉ?޲eˍ7.?w2u뤦jW֯_/MPlL??$ ̴4yuG'Xv)S֮];a„\'>>^X9uwu17Ph#SmLFmfff.\/o ׬/w>|"33SS"_Uݹsgҥ.2j: ˗/ !-Z/`ewriaD[[ʕ+O: ;wNzE=,=wB/%^~Q|G}k! zyWՑ'Xbc4?P_OJeF~GMɔ)SF5~FgΜu_!D5ƌ룏>XnRQ'ןKׯ_V-.[```jjTr}hHs>xСCBi1 ::ZP^=݇N*ʘ!ő#GO!ŋ_xqU!D޽57o.477oժ4^j޼y͛YYY#G3$$D~^kOT*U\\( B:\ij{ァ}ZsljT*?1cƨT*Z}=iNk;<88XqAi366Vz@>O}X@W>~sT.!^ %l٢QZZZo޼YU*UM6577.5O B8;;.}􍀴ho߾RիפIB$#֐d'K߃??B3g|Sͅϟ?.ԾwUͧ_Bo^SR\{ٗ_~鈾fuGCReeekԩSժUKKKӔ(JJ1zhZo_){֭#Gi?7?IF u։B&&&jzg $4\JVZUTQY]MBJ[~iSKK˚5kj6X4˂5Bww˗ !n߾-gffnڴIz PRYZZsĴ @j965ۼyڵk/Z;jcѸqO?Tڜ;w˗9X@W>~sT.a4Pt}̘1?iҤfYSA< 4-F@ZJKZZ… ֭+K߅ vs*U/hKl2""ŋחJ 6,p7W_}W:to߾mjj( P(&&zo\CCCXjUMkB MLLݭZݻw֭NZn]OOOM)-;;Tٲe .$''K+5>C!Dfիi@0խ[Ν;B֭[߼y3}sWVM{BjP2qė/_Xŋқm ;wwєtM)jkᛚ7.?Ajo* : ueff޽{Ĉ)AC_#iҤ`_֣G!ÇsT晁*x hӦ8 %Gto`nn.-½{n*4}9?x@f`]\\Ujժ} 2D*,ʩ/wu?7PFdJÇ?zG_ګTRSS5woA ^xaB\\qͭ׿%ȳH3kr\innw]t)Peee !r7W^&4i_'O~y˻Z{I JzJ޽ݼ_nڷoɓ?CZDD[<==fϞj}33uo޼Yd֭[/j+q)}VǞ6iDto >_3 466vE 2` fhKeffAwȑ"i`]\\6o޼k.͋r q~X_\X. G]d.)T;wKB8piӦmڴ)!RRRH?bsխ[͛7hIwIIIҳƏodddEfMyS{{\wTRӦMe1`7rȳg>>[noǎcP''9Z4EKzjjj:{lM5__899߿I&gΜ3gvwٿ:ty>HPjuԑ:thwvttԍA׮]#dk~_z%߿y~~~?ٳgttŋ٣۠t5$G?55U&}T*III/_JnݺN:M>}/ΏT___ͩSJ/~σJڰaȑ#̙"4V^vGYudRVzĉB=<<|ԨQQlmm{ݩS-Z >9*߼ysM6m֬ٻ;|\:_|hѢN:5nܸ{{>|޽{5_4hp\Cڼys͚5ΝT*5o6mZJJf6[zz9p5kZZZ?ɓ'%$$̜9SaffvÇKĩS>{lʕRK,yZ޸qc͚5۵kw̙5kԬYw޿&oio&%%zaƌ~~~񇓓1ch^}-… ?ϟ?ŋYrNF_b~A=VکS+WteȑnӧOwwiҤv:qcǎi2(KC2mڴgϞiw*?>Xʕwt߀BsKETd u^IOٵk ",,L{v6lXDeirÆ *lΝ;8#y^"Ky} _qP(666ogB_`QttQ~7c p߼d`Ȗ-[s}[vu7E=T|σKԫW͛WB@ёd`޽{rX-{cjԨaZ۶mCllԩS/^NLLŋ=<<߿6cu޽/жm ^Cmj8(XdI|| /_.=2|.|nѢ#ER$#Y[[˕aBxŧ5 ^5X. Q(+W; *$#dfffqqqr$#իW+V^tܱį<$k֬ٹs ~ -HKK z|r#Ń_y !ąXYY9:: !֮]3ŀ_y !d/]KP!:uԥK Eů< d;v]v ҔDDDddd(:~(ׯ_?;;ӧO(. f2($#"!HH2($#"!HH2(@KC_$t]d^^^mttt{,^xҥݺu]v!M6 :V!-[v~) BѣGݻᇎk֬y捦rvvǣG IT6mT^cǎaaajZ`(^s)~"ʕ+ [>((L,--kժ\v/RSS˔]N:eJxbRY6jh666afffoo_Zf͚|ƍ۹S(2?l8y\tbbb~eeelgBRR"..N*$$4w_~eĉ{95֭[ΝܣG"5_NNNQQљ3g9sٳ1aܹsgϞ:tΝ;UUUC$_|ʕ+jN@ri6M]]=oH7G3PPPٳgO}q㔕ٮn#yyy{ݻw~~>-aҥKiiiǏ/**jQ4cl%K[w g2+&yūWfgg߻w/&&&&&ٙg𢣣SRRVѣ1cL(2+aaa ꮈFFFFFFG۷opppDDDBpԩ׿}vڵaxKũdddjjjL&nnn 8T8mڴ=LRRReewstX%%%Ǐ3gO?$))yӧO{xxDDDP4i&H ÇkkkLҢN| 8JKK{{{SL\fͮ]BCCyĩ&7l'ܿ~sYgࢶ-$$70 FMM͛7o&MTTTD+GG^z4smHJKϟ?;v:P'#@{r&6Y* !ٳg|PQQi(##Cytz܇HJKʆ#FlǼt ]RVVFMVWWBjjj8ͯVZZڳgO:YW^,+00PWW~kNwQQQAwEJJʚ5k|pIꄈiӦdC= .33SZZ 069p-^^^vvv:2Uyy/!$??߾}фΚ5kԨQlچ=|POOj9x̆ "&&tMݽ{7<<w^mm7:ǐ;c qqqqqqQQQ ŋуm<hC<7o7-oaJKK-[&%%%)))!!!###!!ѭ[7iii ݻS\\\JJ{'ݛCYn݄ ߜ!wZPPfUwܹsɓ'pYdƌgώ/24 ͜9s̙ǏӧOx6Nbcc핕y")|d2߽{wQQQ\[WWd2Nhiiݻ{xx4."˗444tݺu666 :<]@ڻwoKl <֦XUUUUUUSSdQXX{| B_II֭[CBBEF //Eꎊ81b_`cǎ[nIII560 d._׌k@^~}' aر۷o?tP颦?s.U{w \*((̞=.'ۗ~Pd6l8]us ˆ;wݝS/SLPd c۶m!!!BBB KL&ڵk X|/JѠЄ~-66VBBBXXjc@@ݻwGz隚zEF&%%ѝڐ~rr(ڥKJII9::>{ܹs/9r޽{}Fw^ڠbAAAÆ kǰ@G0vؤ0޽{P"""fffKMMڲeˠA֭[Ko`ZW۷#GNmnСO>500NilhǏჷŋoݺEKT,/_URR:Onn4ݹ=]zuʕfZz۷o|;iҤӧOֶgNƣGNNN>vXvvիqbb[goo?f̘'O0_4iҤ9s 5Q ix_o߾URRf͚5k֬L''[[nɒ%[̙3Ϟ=6mڟidd`0ϩ$,,!-Z>ĉںu;v6t@ g~A?vŋ3g޸qCOO]{ #22ܜ 駟~Z|:t+Vٓh#______S@gb좢ƏOw=!#Fptt3f̣Gܹý|?sssl:dȐ;vҝ :e˖]t͛0B߿0`ʕfff߿ܯ_?77bs<<<._t.D\\ŋ֭ 6loyy9ݹ]8q5((Ғ,"#t-%%%^^^Ty_Ey%--啝뫢\SSCw.h/^tttܶmҥKZ(2BWA-[O?ѝ n߾ݻwk׮UVV a2t6`mmzj}*/۷oo޼ݻ7ݹG^^z5UV;t6_͝;w…{; YUUՁFk׮Eedd\_Ϟ=;--PO/_400 a0tsb2'O>|۷oׯ_?s4ѣ>|I&-YP#G ;ߠ QVVvvvۿ߾}bSNMHHqƣGJJJ񣮮n>}uFw~B:Ϗ7nٲex t::: 9r$  JKK_.%%Ew>C:w睊5*==ѣ;8::˖-|1ݡ*++MLL>|pM:%A।jkkKHH<|ܹsG;'88855URRrԩEEEtLOO8p q ^zennZ\\wݩS m3&.....C 򪮮;pb,YrX:1A 899=:---22ӧ:::th?fJOO;vl\\݉i6mrɓІPdSUU5bĈ˗/fff \MLLlƍϟ?WQQ366~=ݡ޽; ""B[[,m EF,+""bĈ{]nݛ7o\\\@'%%ȴ;vb !$44-44Ą,mEF 111?Enn֭[{Iw(ŋK.5k֛7oN՝?yǎ/; @{@:t]]ݹsN4)33OVVPN=SRRFƍk׮uss; @;A:vĉUUUɧO4hݡ:c>x`AAA&Mz1݉OӝQuuݻGx<DDDV^ާO555''2CtgƃKA:۶m[~}ffvTQVVy={mmLt~999zzz'N ;y IDAT@B:3fnٲ[ntT...颢'N|p1117C#uźǏ߿_NNPrbbb``MM+tB_~vZϞ=@fuuucƌyfHHH||ĉЩ0 GG'OԌ?~8*++ϟ׸޽{(2=z4ye˖dff:88mdȑ>tuu500ϧ;@gP[[kaaŋ6(=,Y.##ٳ={HKK ~tGVٸq۷TUUo߾Mw"VWWgoo޽7nN(2B{2dݻwnݺ5bCegg/\iiizzzL&Dj.\|q@3]ݼysرׯNg##"HIIEFFN:۷t'<;w 1cY"#"zyyu֍P\ L&3>>~͚5ZZZ ڹs'`0IBHYYUUUuttoZӧOkjj&Lp9G8qbܹtgPd6bBCCGq͈8\-p,X@۷opppmmѣGsssO>ᡧrJ%%%jf))uOXe˖=}THHHGGlgggaaTUUEw"lٲ}Н@Ǐ'MbŊ+WfeeѝZK\\\]]}GGG=z6~2))9r$unc\\ǏݻGCthO8>ŝth׮]ٰaʕ+ЁmIMMMVVٳg>"s>ydԨQedd=zGUUՇJLL455; @ǂ"#ӧO9ҥKaaan6l݉6eee DNMM:u?q:t######Gw((2ᅱ/VSSKKKŷN˫]YYIMVWWBX,֨QްhԖIG={x7olmmwtنjjj;u:?4$BwTN:"%%3g?)((7B=!VD#Fz򥏏ݍ7}FqㆡO~~/?~|`07n=z/_>>999u֥R3̜9s߾}TqjԨQ_|}y._nݺ?RRRtn ݻw?<}ON_|ѣGLL$q:(d2ٳm۶#F<}tt'6agggggGÃmEE;wO޿jO&Lx䉩ٳgΝKw"ϙ3,11QFF8dVȘ>}֭[=z #@gO?ݾ}ˋ82;;;.._~tp&#d8p}Ȑ!ݛ2e ݉DDD92bĈk9r.[]]]RRRBB2q::V^}vŏ=ڸq8݉ ^EEEgΜ-+Xr+WƌCw˥eG]QQ #@W`jjz̘1Çth[ǎPSS; `@Z //O__ѣGG;55;w|AKKz8@o߾m۶ѝ@`?%&&nܸe~dQQQMMͬ,ɓ'׮]{Eѝ@?gssK:;;N;G+VILƮ{ , ;tobbbÖdA[QQ_~#QQшCCC##ġCҝGXXXLL̔)SyE}aѢE?|#qcٷo߮]R8pwARR2::Z[[())INNDu-Yi(Co6NKwE7oDY3 ={^~b͞=8ExĉǏ3gNEE!ĉAAAVVVtlPdr"""MEw|f…ܵk޽{gaad2]Q``ź{zXXҥK ؅TWW;99YYYX"!!o߾t'JTTʪʲ䴵?"%%w._,|ԩS555ϟ/[Ãh]EaavxxٳgNì!QQх b|֎s玽=q@P؈P1bȑ#MVVҥK=ڼy3Y3;pV$%%}||rssЉ ~ÇӦM;EQgرcCBBs9@TXXQ111QQQ܍7уxMLLa>o߾tԩS=<<6m;PdHAAA ._wNwm޼yذavvv,,#L]]իW^p!aaaCCCvÆ yyydq"""7n\j۷o[ٳgK>}D)++cXQQQ%%%| LիIHH]633}9r+**JAA˲)G֔\\\?~tzFꊊ)So֭֞4iһw"5Z-V3qffffff\f P$Vb2Vb0~~~]3>l++7nBNMB ѣ'jr-#--mѥ͜nذaǏ&=<<~///qq'O=$oN>{i_oȑ# ;qfjqNNN-_+KDFFr?pa~>2RB233eeem,hZ:=?wh%d*++---9qF4-//o޽{ϧ&eee !'!8??ƌޞH76n8ee_`jҥKiiiǏ/**RUUmgddTTTԦ\ʕ;Fl{l;޽;BCC?ntJ\FB D$**:iҤϳ{15_X̳ׯ_ o޼yڵŋӧ]>Y]]N;00pȐ!...?޲eٳgbݻY\\_蔔ٳg-/##R]]yfGGG??#G6A__رco޼!͚5Μ9c`` ##CM.\`X.\nTEEE@@5kLG]}7WWW77k]˗/,ʕ+󥤤&OBNwR555TG5OEEe֭l>|PQQ2dHqq臭999W^%Ss^.WYY$!!A友spp(--3ip!dNNuuʋ/v]]>}|dΚ5kuuu,œ6}q0yKeۢ C6F4 ϋb)))5|IN¨QzEۆ4i?l6S3׮]#$&&f%1r~◆#-!ԩSB޽{նh,VQQQRRbX#Gt Z.XN8uAA5p4o^؁dff2dذao޼i^36mɓ'6fggR/[1_ b}2uTKKK7*2VVVR#888paV2l0 ԥg,w^^^^d2ꪪlmmY,-l!/_&O8Q\\L=~SNB\]]rˋQl:Bϟ[SB穪8p9s-[X,j;6aaat.|Oq/6gsڊQ(N rXMpлwoBH`` |Ul!<qė㌖֌3ZPd6R )(($&&~… \0NZ4X,&$""B7>rZ-c,w4o ~ KwSN0`GVËUUU좢/#,,/_Btuu BabbBy._N~PzUDDΜ93o>>..:zرn4hFII Ś9s&!NNN񩪪9cl]P?w(2v/^љ5kV\\\;$$$oްq͟?1cxDD!<<<;@ݖnΝ[n^f2\'׏{ 9P֏n_]]}y++&@=D]!+vRNNN} Qܓ`ɒ%ݻw |XVVvE&McN[>255MMM5k_5}'Nm?Ƕ555CCö@WH=s䲲gϞǏvvv\ew}e777_rY]PלRQ*++ΰPPPྪ7oB;v>''fڨSH?y,Yv9d7BȨQ_?[FFFPPu7ZE;8miANO]:/;vܺu̙3L&q5;@s򑧧G$N#uRWWW Тޞ`91jcyA~EFyyy9;;)qqqT%(((((˗/`mmM&}‚|}}߿dff_YZZZZZZ?3z$ ߨ(**Rwdj*B3g҂>}6OQQ!dڴiOOOEEœ'O6^ՙ3gLMM ͛7^pAGGСCΆ6l=zzСiӦXUz+GAA ==zoّ(++0aU;&888k֬_~ ~ΝBN6l}qM_tY9qT7ӂ>/BȨQ!>>>^ Nql)R6xchʔ)^^^4fh\FjJUUկ`0Μ9۟[4$%%RǏ1jc椩A~EwpMpdXBHH(((=gԭ[{ԚGN}4]VV6<~qݻwaYYiӦQc;rY-c,ќ1ҜA߼zxɓ'?sĈ 166 _ӧOy$N--- ~>4_+K;w‚˷^?>O8!..nll>ŋ>oo[R?B L=ۗ/_tuuoݺ ck$&&B=ɓM6 /^tqqa{oco޼ٷo_3Twԝwq-)))ׯ_8p>={Kw`DZEmRFsAPdlWyyyIIISN;`ߗ-[ޯ_? SSӔpyДcn߾СCW5nܸ|Ϗ,Xն(q™ٳgCEŗ/_~ъ @Pdl>1cݻw x*++O>Mwhm...NWW_~y󦌌 qm611#G #@@m]t ::ZRR8())ٿݻlժUϞ=; ]FA"cx… MLL111Ç?{lhhhaa!q@PM0aʔ)8K (Pdl++*ŕݻc:::t'tҥb@{ @Pdl^p>,,,Lw: 0~ȑO> E` (2ߡCVXs΍7ҝfwvwwg0ɓ'O>}߾}tAխ[7ssǏF"#|3nذ!//iѣG_~dXMn4211qA:Io8 IGZffffff\f P:_cǎYZFݻVVVUUU߿իWKW\\L>|8.X,u"ɼr劒bzzz">}?7ox LVZhQPPЪU$$$!ǎ{h=3jjjk|܏?\?~<%@c!_|ԕdlZKK˯_JII: [[ۃIHHO$]?ssM8,СX{ZYY9;;3)))gfkחxqkp1SSS!![n5l?v؛7o~xuu͛F}ӧۗm%ma۬Y~f &Mӧ&Z3dee͟?ӦMZZZL&3>>~͚5ZZZ *..KEEE@@5kLWWWi9}Hj/NPdSNmݺ588,8p5(((88800UUUUSN_?%%ECC#++0hLFFO>ϟ?oبd2ϟ?}Y&cǎ/_(jRRReew%oRTT˗ŃNHHHGG~Ϊ5#ѳgvneeU[[+&&vӧO{xx鉉qꥶ$--[dɦMyޜv8mc3 I8 ܻwyڵtgp5ճgOB)!ÇMCiiiQQQvooo]]]'''__]vSMi"//O>=z޽M6 Bwޯ_嬭uss 2eJ%oSDOJJ*--;fdYn!DXXXNNիW$8::脆уS/swwvvv'NysalⴍԸ_#QNN΂ [S.E]]b]zB栫䜅M&&&fSUWWAEEa Rר6\ɋZxq} ߓ7la0"""-J~MMݻwYSUUU[[KS^z;wB @&**jooCt3CrҚ4xHj/N%i%%%TׯÃ^~sNNf2\VE4ҳ[E;w,Xa#U+{[aȐ!,+00PWW~kN}6o0%%EWW˗/ /**"L6.EEŰ]Z~̙O>5's\\?!$(((((˗/v7nTTT}.o{ot>#uE}}}KWW7"".\ rҥouvv^`APP˕fW\;w.!DUU588Ç͛l1cXYYY[[;w,..YݻlW(Z`֮5@LLL߾}?~ljj/Z7y) Ng*++[j{\)M=%&)Y_/>W0ƍw5778DYtׯ_شo^OO177W ^JJJcC o8/I5[눬;09h d^ӛ3gAF,<<<44?۴iS}ݲe4O}V1egggˤ$333i䘼攤!C+| #3'O1cF#2}I6ΕE#צzuJh8.JKKGcc㰰0={`HJ6mdd4rݻ:#G8D&K/^H^VUtԐeIIɺu+S!攢"??}Z2(> 7n0@RϵTH gٲeYYYǏ`: ([nBBCCKQzZ700qwwZ%?Kr$)W!";;; ZZZm۶   <<|…:t=z̓"""$ڿ7ڵRɺ .٩S'H3frrʕ+*ooe?y2x'(,333 xM\Y갖zjkS}: EQmggG[EϦD^|9x`QJa{|\^"::z^X>$|JhhH\?lllt;sssd`*ƍ={G1(5ssO2i׮]۸qMtsss޽tfYW2Ϟ={ҤIK.e: W۶mMLLc5JFww{X,ౌhwxx:tt#Fܻw K3d,((pvvvvv:u*Y@n̊_xttf[2OAA)P'@W_?x૯b: Xfe:@kaaaǏf: LLLj֭[׏UnjUTTϟthl#F`:@jv3tqqe: 3RSS)h6l@ ,`: c ?0***ݻ7Sr|!Sk̬Z|ټyA```qq1SlAК]K-[{a:]rϏҶm5k01zJLL}rpp,ѣ;vd:TTT\]]555 8p`AA۷o MMM0@Ќ*)rss333[p!Y䌢(GGlB… 'Mt" 8b=z Lh(ͨ1<<}NкvzΝҔ@ӠP8;ut,F ٸqYlŊIII3f̟?8J͛***LԿojkk3hEKիWwiɒ%L={Btt*&o߾E*4˗/OИΟ?@s[2?~<11q۶mL7޼y2vصk2)(+++++4iY@zJFhLaf)W2VUU̘1cLgWBڴi￳MDn4-Z8vŋ'4wt EԔ&9ty铃'߿ȈDMe0 IDATNr@dسgOn6mڔtd+׭[ڥKȓkVV!mڴiLPP={,//O033;ttd+:TPPtyڻwS! e:N3k.t )!e9233suuUWWg:"j...Lg'O,_t悢iӦ… LgikNOOe:xLP2MWf:ܔϚ5u־}2aXϟ믿2011AKFIIIIhhhcbbd8L@4c&焐~a…Liv ݋M$--Lrss׭[?2,J V28p9s̮]!]t9tq/# v#S2޽555|ZwM6͛7xܹs !***ẺL'P:uz )@ɠnZ%#݌qժULgoogBѣNo߾uV=:w\\\\PPtP8 qqqLhRT%# ;w.1BxMBڵkrk׮3f EQLgѹsgB۷o <<<,MJdf:|B;46+Vx왗b: T2BM,맟~qǙФ4: njkkۡCA~~̙3 !ԩӉ@,ccccccSVZ顒D̛7MPi?<{Crss(@fffB͛O?ɥBE䋮Y}uܙ>*//Q"(S2f̘ ߿o1bDrrrEg̘39p@LL !w۶m~aÆyzz֜daaѦMY59OƗl/5];wFKfյsu5 5%D*Ο?.nݺ3g\w@*˖-#hjjFGGhBe /m/^3'ғ\fhuɈSSm۶18]=;T26OYYYׯ_߼yLߟQ((&Lݻ[[[ رcǎGGGԩSh*//+++#_}L矂;88ЃSkjjYe"V7›@sNFl޼s'NXx11DנwC022e:4={FGGuKjs>k .jJ)d|}xxe˄Ny#2Zjճg!SNOTVV֔)S>| hks6ᨪ.[MfdpjС)b:4*6]FRF1>ӪUs/ϝ;VVV{77777iӦ^~̘1?$++5kxzzlLIIƍƏ6hfΞ=cBHΝ>Lιr劚ZVnܸQPP`oobƎKE>xp޽</::‚rȑ$,^Eff 'OB(ٳ;v˛6mСCk=kk,]TT$xbʂ/_>lذ͛7|M@BHaaU<<<<<<>,&4X"HE'Y{jY3gSM3ʺ=\UUgE(E @,Jf DEETf=k_~Tؘ*mm޽{:g>}|>EQSNgNNNg/,,lЍ);;m۶ׯ `uug͚ErGM>}>ESSSBȚ5kϝ;G5j`A>٪U+BFFF\\!dȑbX~)X.kiiioo(ڷo!̙35 "???\nݺ˺ EKq{+aAڶmlǏ)ڵk!ݽ[\/>$x QQQOvQ]]_,gx<1éuN'44=y򤰰P9 eмTWW3>֬Y# !v_Z[[hт>{={tɿ<<OOOOSS{)M %u-W,=СC5 M{QBD''tR-->})S4@믿^~2ru։ۧOcҿy<=v!$<<ޞMvslvvk]P%ѩ}+WBTUUtuu%Oiݺ` ,!!6A._ܫW @^"e ʆ.jii)kj׮c6ȝ;t 8k .j5KPQg2>}4!!a޼y_͛}]Njeccq ̄F:tPIIIzzziil<OG;~x,^lj[lٲeˏ?ϟwҥe˖Yb/_i)p !D[[[̷~iӦ]ʴ%ܫwvv+i&f.*p( \D(E ({%{9v/b&MD"jl4 ˑ<׷O>`;wo#33{ԩS}FFF֭+--۷ۿhѢ J(rnmQQQAZz Bc'N t-33(U#-[ʺ%ܫ<ٳgw :}U>M룒QQuܹQF9::ֳ(5 5Mĕeees4- Lɉ?p@aa!!޽{eeeD144ÇӧtdÆ 񇏏˝g9puuH̝;Flgϟ"3ԴuCi闆999R<"3B#~֞Y5ϵ>}B6l˝;wҧۅ xLONN!X۷?0ƍwյ{%KOڵ/^|XWWw͚5</((~kٲełƹ>}^000 BCC+W,++S빖>v-Z :ÇF/ׯ_شo^OO177^`j"OOOKKKOO 6TTTP%hkMPXGCHj/ _btuu#""WƆ L{ZOXzYY _|.jE~(%K ;;;BHttX,VTT=[C9r0 ŋfffׯӉ@!\paҤI...7nl׮qdGɹzǏLxO+"Dtt%ܽ||gM5I~(e.ѣ۷oK3 bڴijA`„ gΜuVAAYd?{llȑ#wl6m0b޽{7)g @(k%{1~xԑӧO !e˘ɓ'&&&L[!g%&&^:<<`:::U]۬oƛ6mj Ha:PJƲs@)n۶ҡCHn FDDta666<ٳ'@}aaaiiim۶e: "::lܹLgggTTT"##{ڵ.--xJWo9rȑL`ž5u-=:i$Ȍ>| xzz3DLlll>|teMQ323U2_~}L[^v2bĈ 60L~~~^^_YMA%#Suvi@RJcPFo!hkkGDDp8J`:@T22';;;00p͹[4@s|uG9s&J޾~cccRbXLP&hȬ޽{hjj2dLLL|򥽽=Ad6o޼ׯ_B̙ckktC #@sd4hAdshBΝ;MSjj*:*˗ϟ?t `2U2rǏϚ5 IIIYh!DMM-22e˖L'&믿z%YNVJJJNdxCZZZb<"L*ϟ??{lȠ.--%<11 ͍0iXPP!_@;w.88bhhx!t"h8L=jLh PW (M%ɓ'l A;gggl#Gڵkt"f -/99OZ)M%cxxA̙GYbӉY8v)Jw͛ՙH9zս}֭[Nb:Xwxː/Bh4h|~yy+WtV^vZ6[i6 "111##CxJff'5АX1::u}AIJ׆ }bjjgccth(˖-2e )|~d6l@\eAXSN1 s iSJƬɳxbĈ @=B$n߾ ̄QDyyǏ;ut4JƊ uuutϝ;<mllܺuk0 333((д)h%ٳg[lw}q#FDGG7B$h옎vڵ~z??e˖ N~ڵk鮑U2o4iq.pDGGO> KAtL8QSS P%Kxyym۶@x:uqqyIrrr'B*\ 󤧧(N5."˻qO?tWڵk'<}˖-EXZZ2PT2^paĉ?~0A7nܨߘ$P]ɓ<4==ߟB\˗/g͚J1&&fE%'';wNhFt:uʪUVL,_\ˍc0 |N9s?d:Tϗ/_F_ir\<oƍLUVV3A#G̙3(Amuuݻwk׮1 @Z WbҪXp.s̹{n#GVYY` v=w\>/FUUձcdž0 @& WxԩoVGG m۶egg{"rss9@UUbdܾ}ŋk^r獟 + D( "XZg>}lhbv yCXŧX/^D_ii2*((`:aXqqqVVVt;t6ݲeK\.se$v޾}FBˍS3Ūd<}A㯚燆۷UV_uTTEQ^zҥKKMMݼysPPE6mXruuWVVV"^xa<==_NEEE@@#$|ֵ~ofĈ/+-b\nHaG,ϟ?紴H5 IDAT777ss-ZB8N-x<^dd䯿֭[-[&x&TMM}&Lc"#4no>x/Yd֬Y #55u֭ W~0TWWŭ^i…1cFqq !dϞ= .|sss;uꔕuԩFNxڵ}9ro޼ɱp/̛7o֭/xرo}gϞo߾m۶%%%E={oҒcZeffvI r,s2+,,ӳdܹ/+2’G#Lq(|6>__ߍ72jfUWWWWW_jժU?Õ+Wǫ^UUsΉNG%$$ 2 lذas̡s!Cܿsr {Q;G*oݺ?4322>|I{k.??_hӦΝVZIEQ 2::رc׮]ѣMnݺeff&ߜ"n޼)a?S^s׵cǎ;r8Zef(,9~4C#񥤤 >Ã.**BӼr5554;;4T22NMHH˗/mll֭[w!fQ(u ?ݻw=YYY!!!m۶Φ_͛=eʔƌgϞ$.5 0{aaaÌ~okk2u>nݺ!󒬬)SȷQgyM*h C#:ue04no?M -2ĉ7S4 Lz(P'gN:U5JdUUȑ#v޽{k֬%9r$))nnn<ڵk˗/ڵkNNΘ1c:wÇhGGG BH\\Ä۷RRRqF'''33ӫwEOֽܹsnnneeetr77B)^c 5wT~~k+8> OJJw ...ِpM?p)޽u-2N6mcƌy bH#n2$,lmmmmm%@).+ !!Ay͛jjjwL1bҥK:ڶmlǏ)ڵk!ݝ~kƌ*̦///ٳEjjj)))WQ]]maa1}tGQTII 9HxB6mDQܹs $4ױcǏ ,^_ZZZ7nYZZZڽ3gѳ暘t-???33G9r$='!Ԕx :KYy(ھ};!$66~1sLtKKK}}B7n8GGG>OQԫW|e]Vd/n+Cֺj]PEQ+AKqB>}}8u\ P+%9~4W|3P{(w/ ~}1qpp =zTٳ !o߾Xn`LLL)ݻ b%\ ]|dpy|8V.]g++**۶m#8;;K^}#={ҽ&MDQrJvZ˖-KKKSx<ϯtpp(/^/:O?x>''G0ѣG !"9|RĄ'u`N:M<~f͚PU3 ccc>Ly*j'"frSⶢfT%nAqA((PeOm۶PSxC|$8yJ B%LU6<~f޺uɓ***tgϞ}Q񌍍9=C\.L\M;@%#A%#@)JwgN}SL9w!DQRuI[e%<-KVԜSdG[P%.@BZl>tRzg @5XC/_ܹ F]TTDQرc%\őfvvvۆ *++׼p+nZWA$cR9sFCCcƍ?}7ߨ?~B͑/__;;;ӏ۴i= !:t otsq$UUUuĉ3gZ!/_Ngdd-9IlrΝO QRR.EN),,{ n+꼠KV666>7nܿ;jԨ9%M^7cǎsNIIɓ'O̙Seyf߾}cHY\0 Hy_.\PSSoqk M}H\.!DUUҩS'}}hq={vΝӧOZ~!а@²'J^nGn,`hh(/B$V!$ @'NfIo5vRvqq9|ptt4؍ҧO_U0[RRΝ;6.\iLmKϋLpM H 011|rdd$ǫ>4|(;q7ŞG_bdqrrbX&M" L܅C\.Lu#|T2ZXXlْ .]kXvܹs۷/Z&Bbcc !YYYyyyӧO{؛7o?>!!!%%E!¢X03;PKKytڕ~KB|||"##=zsΏ?B9!dڵ]v=rH͢"##mllj-ɓ'Ǐk.___77I&yyywǎq-wennxb_Mw+chhcJՎ%%%"dРAt1!dÆ 񇏏˝V\uҥ+WBa(EKBl_ZeEⶢfT%nAq!O> 6|rΝtc .xBhh(}hM>]GGK."R@Ju>bZeeܹsY,Vddd* L~~~NNN|| ǽ{233k^8+$v@L'86Cpy<^6ml"RRR||f͝FQ߻wСC/_>mڴիW".^/v]]݈^Jp`K.}~ )_`yκ@LLL߾}?~,2vEQwfٯ^(ٙR-Z\rn[k9:: SUTTiiiazdAC/W\YVVva]]5k֔o-]ΥdժUPq-[FY)۩S̛7///~ѣGnݺsׯ=z/_\]]-[(.|||!,kϟ?~>l`H 5w}^cǎmѢСC>|8j({{Yfᄐ]_~SN}M͏n4\=X  +7Eֻ:|{CQԣG 2k֬y-2n^vmݺuK.ZZp,YsWk֬Zh׮]H{#::zĈtm3RSSeZu6mB .ʂJFN>} Ґ__߁?>88D$''4@0%**}fJI&YZZ+m&&&5nݚR\\,NNNvmʚ2e JԩS=8pd-dݻwOr:e@0Y)(}M8FwEh|>?77wɒ%"=^ KKKg̘QUU%T-$ !h'niee%2Ǐ/- 4kcX0}B ===WZaeegBNjvtt]hњ5k<==KKKmEQ!!!3gtssSWWgSx-E={յcǎyyyӦM:tӧO !GIJJzB&L3z{) {ɓEOQԫW㵮n۶ml6ǏE}f̘A,X ̤{gddBF)yLMM !^^^/^ܳgO-RRRE-^_ZZZ7nb<zWBȦMĭH䛁zK5W{/tDYa%cVVdr)k׮yfee%Toxwލ5ٖ/_ncc_mEO8rȑZrQUU 'ܼys͚5˜?~NCܿ?v/_?>=]__'===00+(<<<ݻw766( ![ont)X 222С!׷K.'N400oii9-[07!ۑ#G|~ZZڐ!C>|0zhy{ƍիWlBH˖-]!O4hpP.LV2%Lӿi#G~:`۷www{2:tP^srrP~v֯_k׮" clٲիWUUU))) "ǓH'$$"~9BH=SAtC"X< ։E%ѩg8]AWΟ?O~4Ȟ!B042\v ҏ?ئMb'V֭cbb:p̙%\\\ZlsӧOZ lt[9mmm !Ҭw 7mGRs5zC 1n &";;+B%>aE PFE۳X,LO>v횸8b޽O>~~~t@˩Çt俖zgfft .H.ٳ;w>}zժU# !Q]]-TԘ{CJ={$UTTC7 544za\SS"+W켼yI3jh?~rWM&Nhjjm۶ƌؘ+^zX[[8p@$ ^"::z^Db~h(hVlتU+A?5={#f9{#G5%%%֭i tn߾tPh W2[k"i> TfQ *xg;p* X@YD; l4S<8EE΁ij Ic'/$!dL63M0ΦB+޻wogΜYדاO4P7__D---s\Ù .\ahh8dɴ=zlٲԳgOź#sff+)?eddt4wӫrSTחdSN+WAEdff/(b0(&dԩ RUPPt&';v옓`hpYYY۷o󳲲jѢEjxͱcǦLbaa! K.3bXCZ[[>((D1]rׯ?ݻwׯ_ttt;H30AzsQ8z^ZaLƯ_1PRRƍ/_x'Ox:TҶmۉ'>}ϢXdmLL !ի׮]믿,Xk.sss8UV-YD… ΝiӦ=zal_@q fc(Vo.G P+LN2vܙ33@Cpvv޷oZ&MZjUtuuklҬY3B:,7n\JJJii㋋-ZlذaܸqһPnnn^^^DDĢEsyBHH* WYқHIFcc6mܼy |Y\]]vt6ѦM͛7VٳgN<==׻waD5QNLL\8bĈ#Gdggr777ooo77۷ Tbii)K^cgeeM8q3gδ~KJJZl۷o۷o'1ڵuٲe ؾ};ϯȠ7߉Wo=/(fعs'@{zzz~E=zXhG :uҷo*'NXbENNˇ ѣ.]-&iݺ'O*ZXXxӧO{yyy-dzmӦMTT훁AmqFiiW.(cƌxgϞhժSzzÇKKK֭[w)555Qp &8qf9rlu\.W>JJ'^]0|&?|sfcPE5oޜ@u[UUUںoݺ>ʢTTTT.'$''Ç_zj6M044UN.<`)%\RSD@է/_8-ZꖖFFF777MMMQݻҥKk׮ٳg;vʪλ#\>JbNzyDa~e˖Νc6eiiIQԅ !mmmn; q0a!Dp:Du߿755\CW" ʯzIׯvqq4x%,a|dSLٳg͛ʸ\.]Nʕ+W!ڵ7SUUuuuS0(͉}DNzyDax2jԨsέX$Pݺu Ν{ϟo۶MԪ***<OLU)=+VM\rbҤI BĿ^:tCs5idݺu <J>}qrrSppQN~H{ #޼nv2h@i0 &888{ʪ8۷o׸b6m>| fW^B&heeeڵ+eiF˻t钖xbT„ _^~$99yȐ! \}%PՕb5BQ3Eҷo۷oFGG޻wѱn#޼Dun@0|4!VMM?`:Tu䄄7o>{\VVV_~#EQ͓o߾Bܼz'lmm?hkkW~y^^!dРA~Vozɒ%kFEEݿdtҎ;!߿޽{-Z߰ɫ_㒄SӧOo߾qFHHHAA!ݻ^*++#ӊjccsu͟?9!>!.z^pttttt!$::VuR5~x;;ھJ}ѣGuuu{y ]]AN#,Y$--Vrrr \\\޿/Il{"+(;֭ \L2^pb={/_$]xxx@@ի-ZxȑK.j41f̘s fddWJ2XL^$##~W>33gGq…:LL2V"V#y=տ$#$#@1&#!dȑ!!!;wd: φ  !l6]v:u%~mM17k֭JDunʊmܸõDW/),,ܰaCppp7R! zBOUo^y$$l6{Μ9{ݼy:qBRRR!M4(*55/<<\gΜ U-U;3JDo޼MLLҢK%\^%ֻnI%^{I}۷g:( )l(`o^y$_hgpˆCC!C888EDDe=lr饪mQQQFFFž={J;Iff+^|I.**[fZ ݗ>}e%ԓ,{@ P+Ziwʔ)ձXf5yT16@=8а9:uы֘>w"yHY~}zzz\\AhgϞ[la:ԂM2B֭[w+W0$%_4h֭[IFB֭[/_tM2ZYY9::zxxp\@ncǎ/^2ja::tX` O!~㜜`F(''cǎLҀC MM͆᯿b:$#!رck֬YΝO>.`͘1$:v쨢K#daat˥ !ZZzluV nݺEIMMmj !z*..nؚAxxxʮo߾\.G;wL4e˖pIЧJKK'M~YP7od ]r:HYbEnΝKQY@ޅ}?CVplǏ̙!R~~~llի 9`OOϳgB(rl6gLS111NNNSLa:˥ !Ǐwuue:ȯSNyyy5x******ݺu۰aC7P]^^^?tqۨۋ_XTTT;3}􂂂y1@i~XÇLgX8sпQi&Ѳeᔖ =^MMC,K uwwg:ҒIFB͛UUU+,,lᆆ ^J_\\\>|TbD=f5kv͛2B[jնmV\)K$ޞ={BCCݻwҨ\p&#W8dt***B=}#)UV8F' !SNuuu>}wrԩS&MF' !\.7//oܸqh2ooo*,k֭666LRhGqvv%ԓL2B٣;Baaa'Onڴ4*r4!"55MT~q8+W2JM6ٳɓKKK<cQCC#22ʕ+AAALg9t)]+Mp~8qRj3Wykk3fBTUUMMMCCCqz=ztBBBRRҨQ~t%~aÆ +VxY@^wJJZ lٲ) Szzƌ+A_&MX,ٳg544L4\c?2@($#!dʕ{vrrߜ"##gΜ):Hr>>>Rj| 4###A_ͣv`<%?\v۷/q"M2r8輼ӧ㞀pҥw͚5KzMTdlҤ !xÆ ǎ^^vܼJ{AAرcI̒ʆZQEd$o/^Idp KKΝ;K Ҫ,Yf666)))/^ظqcv4ȀIrrl2hР;v9s,/_;wNz|-r8;;ӧO)66vРAXq@i$%%ZYY=x8J& !K.upppssa: 0_eXSNj+gϞ&O.x… MLLǛ7o2@!)$#:vX۶m'LPPPt`@XXظq㴵 ͞0aB˖- 4*}MOOg: ? C]x8G!' !/_.))1bDqq1q@SRR}44X[[۷ƍ: uuӧO;88;ԖN2BZjoggtZرl)1 L:5448D;wMLL\x1Y@F(:q9Y@ٰXkΝ;7008 Ci pȑ3g~w .d:H]JJJnn+AjPZZڤIS@]lܸiӦ޽۾};qO2BOŋkhh0+,,W^}S|#Ts%%%[l|oSRj*mmEBmbN הaᡢZ\\`〴lܸ l6ƍo߾$#͛7kjj2n..._| Jb($#!dɒ%<oѢEl6ݝ8 ϟ/))1c1&NxapR|9smۖ P_NNN͛72eJAAAXX*ӉR=cƍ .>~„ cƌپ};EQL'Ĩ9::2DiӦ]vnD?GmTTԟ٬Y3)@۷o_S\cX;wlѢ[AA/t"43ܮ\r^z2y۷ZiUV8p`ʕ^^^Lg`@cd$ 4Æ ;s qݹs'44~add4|pfS@cPZZtPl ,߽{? StҾ}ׯO>qɒ%eeeL'Bٱcǜ9sZn䔐>}t,zRUPPhѢ~3Ϝ9sQ.tis7M49t磢z#+//?}333ӞLg7v;88T>=6??ԨQ fF=H;vlzzz-g4vx<!z:x iؼyӧOY,Y@㏗/_NII=ztQQ!&!!aٲe& !7o=FTNm|… O&B,??_ IDATPG-[c:(~]~ӧo߾6lXff&EQ`:@$qqq={LNNf:Q#%8 8j(DAAAbb"A@_t)''Ç_R\͛~t:I1f̘gϞX[[F⾪jO8!Wikkݸq P_޼yЈtҥ{O?~ K$cUZZZ<}SSSNԸT_u^piӦD/biit f |>իU.Ԩؾ}O  dFFF Xx1.i*g2X,u9CCC"Ibƍc:1t[nq8@rʓ'O .++ }$$HǎN(TSuQ XzuXXX֭“'Ov%Y.'HRIdgg9rcǎ} Ӊ\lYPݻwӧ!D]]۶my.i$cttt>o'<<(C)-~)S;;;V~۷ŋLhXdԄ QUUɓl6P;߿ߺu+)W^/_fق ڼy3 fpjAGG'(((##CSSo߾SLlx<jڴs7otP0>~|>Yƍ_|sNCCCVQQt:+Z׮]CCCW^ݫW/K>OBBT߾}cXsIJJJJJb:ڵ5)@-[Ĉ )))++K*k޼5k.]񜜜Νt.Fz)$c]sN4iݺuڵk׈#5k"""W d:ꥥb6XfMΝ]]]e 044ȑ#d@w޽{.)GP7\tuuߟݧO{{aÆݿ_Jm9::R)ʊwDYYYtt4ӽ 0CQ= &}aaa_|߿%K^zt(`F_~t Ƃ1 cذaG9w\.]~|Cy{{r'(,,\t1cIf]\\s)ccc//ϟ?3 dGMMœ9s"##D`effzzzu500\ӧsaXLgB0(%ZZZ7nΞ1cwvQXX(j{>}*˄C-[d:?L2JQV޽{׶mۥKygϞ533[t)ϗ}N$iiiZ˗[l9sLǎ޽f8pɩrɓ'LaQF444.]sȑ۷ow933_ݸqxg: KNNի״ipwIFRSSsvv~޽{SRRzuVUUUz~٨Pgo߾e:;~xΝ˗z ߿g oL22@]]}YYYǎs^QQ!x<77wϞ=jÇ7op޼yt9EQGutt\fO?UV9v‚>sAdeeـ\Z[[`ٷ d.\`ddtq3sݻʅޢ_lY6mX,3f]߾}Gqi׮]swwgX,맟~Ƞ˯_>~xeeeu9A;vݻwΝG|r ZKiiE-jB.gee%LiӦKR1!7`OOO7*o>nI; #0 GGGGGG1BkUg#m6Ssp8ZZZ7oޤ7񝯭'''M>2zhM6􉢨O>֭ ϟ?O8Ν; իNNNeee7ϟ8qbvvv4-ɓ'L (z%j<rĝiЍ7Q^^EQ/gϞ-((?BLMM釥]v )))!Wy-}7o%=zׯߣGٺu]j_(*..խh===Q5%I0)}hM;v֭[e?Coz-F߁:%!ǓGIU?d0Ȥ6mڈp8M40ɸk׮o߾///Ԥ(*77TmٲYfuh̬K. Xɓ'FFF~ݿ~Zv%dLHH8{l[PJ4ɨܝimP>ߥKG׭[׹sׯ_ٳgƍ*mM6رmh\.r=zСCiii}$ܑʺvZϩ5H8(@eME3x`:!UjiBs[sgΜ# |>v:x !$%%Ү];bX|^2RXXk.C#F 2ݻByI; ԫWFկ_AwVm P{n''竫ĸXYYB.\b֭[G****՛(nnnm۶ĉ/4322w7$++kĉW9s5C-wfR3224k}ށ?hnݺ/?G߿׫%SI#0V=L0aРAݺu300hڴiHp4x~mȐ!cc={B֡~ߋJKKoܸѤIBȶm.]4wYfeffڶnݺ>âz9EQӦM#Oq\++Sx<455|F)3gN޽#Tm.1%BBB|DXB/sSThK%Cy<^۶mO:%(矝+**6mڔuuQ;2cƌ>|٫ _+^ n޼_EEEΝkڴ.t/B!?N&IQTaa -߿ojjڡCϟ?SձcI*?/;]rŋ:ԬY355gϞU/1%@t5kּx"..B_*>Hz[UlB<<<:޽{ !\'''Q!|+zX㓛{BU`LMMk̄ n P?~Hrt+V\v-22RSS/Bhu<~䑄#Jeeeݹs'!!ɓAAAݺu1bD7ӧ'O۷ M*))lS\\LӧO2^155%|~\}R566VT9EQڂ Q%]t2hx !dԨQE:IFLk23^}hRR`Jx</++svv(J!:u?ϟ!XlP\VZU޽{UUU܊/1y~g(i[S'N xzzR`@Bܹs엘ϫ;PxzzzM6J BFbIx>_yʌVZfMZZ+ |D[7C;vp8Bk|7k Se˖Ctl~@@!d޼y5P'Տ<~@i10HEEe˖PK9]]]]]]sssmmmggnݺ%''ކаͱlB}EBȽ{͟jdii^paĉ*:55Kzzz>}Ϟ=?Y1MWb|B ˗2CM c>xٳ=<<"""߯U%J}ܸqB/WuMI#0 k2!?annNy)nF^xQy#BUNQQQCϰ?^Ǘ"/ AA;S1$CO>$: mʔ)]PҾ}귂S{ :ї< x!D=g J !5V[b>; !ks[֭;rܹs.m۶aQO?{c=ݻӗcKV)^>nÇ_>vXݚz/D̑IF 俿;f_rEիWUUUOرCP~Q儐6m|EPN_Cw@+-- *l,q]v-^ ҥW_J}C%''[[[ ^]v^^^EEEu}Y)G%^"= B[R_eoo_\U:|$$y un=~~~~gϞ.:mm~)444&&Fp'&Ms]]]Y,֨Q'j555MLLxT!;Pw^aS+-^yjO?v&Kqq1c̙C/f͚Ν;OtiӦMuhBM311!B*++t3gNDDĚ5klmm DSEkFIKK+222==}߾}U澾E[844S5|2eʔ֡!3yduu7|eZQ9:u H؇:99K=!ؾ}{޼y3`UU;w6PSSݻ˗/齾tR.]*HQTjja׮]o޼IW{4&66VCC?ݻ***Zxbqq˗?۷ݻmV,%K Di&M"x{{gee۷3>>v֍w9>`[[[z;t0cccJHI;PE۶m+%ݯ_A E;vL`ӦM;v qӧOBڶm[y6yfAk_-d:˃iӦiӦeE!E씔#GjՊrJ f[hxYfW\Qu…Yfѭ[XX߿K.ӦM{Qx3gFEE^tiܸqÆ ;w ɔlwKJGWtү_ NR(--^,XbD.^(X+QTTETZt)}fM N۴iӗ/_BVXQP]]5k=zTWWgϞׯ_ յPݬ>nBɦM~3u~jR1Cm˗*m?W#\#JEf:zQXhz3h@5󍇽}׮]3V׭[7XAرcCBB臙fJMMm111SNG'O;ʪcHB|g*P}(4rm_l!}ի2)Ed7+>&Gws{yy n)ƭ["##釅6lrFB{n&Md@mIҙ2؇2)**ڸqÇ?aÆO>lvv,--;u$یHv/pϜ9Qv4z Bț7o|}}g?w>} 'fqzg^7;St $;;;00P7'%%@(wիeRnVWߛ @IFP<=zزeˁ*fffX˗ooϟ?GEE ӧ$?@dgg9^>*v Pz) .0442dCZZZDDD.]dRn \۷777>HriPH:uZre333A`p˗ѣ,, )MUU}(Ȁin%HvS@SSsΝ;wd:0=t`&lْgӦMjjj^b:@Q`Laa={:ut? ۷ׯ_g:0OFe dԵkד'O{8=ztLhHݻw]A(Ԩ dH}{uf׮]L'k׮?~#AǏgϞt`b@0$88]vk}tP|qpp3g5A܆ IDAT@a`رc K@`@<==޽t x<@`@bbbNPէO͛7yd2ebbt G}***ڲek߾}mll=zD\Tyiii#Gy{{m߾L|T.)//Մ(l6;11r#9-k5P-[foo/@~񒒒7!*1 L2*1c:tb``pС>|˗aaa֭SUU]pChhhjj*Ͷ),,(JhyVN:E۷/fdd:th֭7JKK}||6!N֭}ZhI^^ϟk> srrd4Bn:s ).d?xrjjjU*c6-dTrꖖFFF7777kt҇_~ իu떶*ׯ__z5&hhh*' 0`&p8K !BKBBB޽[?X[[ˬ9hl<<<|I@Mh.X}Fcpr7~i?==={ͫG Z^ݻwڴi#x@155vQ[[[jB uttJX,Gv_Ç!SS'N̜9 `=xބh @No^x7 A!UR'xH:!Ϲs4in:1MqʕI&U.g9)S.\tՍ1 -ҍyIIɎ;%U^6m|EK.8A7SNHHx'?TiB2oovϕ镀*žz Bh[0ɨ !(,++#p\ҕcǚn޼͛7Æ ̼{ڤI՛ k !K,9uڵk[hann/IK.رch?{L[[{Ȑ!U LKK[lϟmm5B $daaa7n={$9GM'fFo`Q<}422ŋ} 80!!!77/̞=wjjj/_^xٳg/\0nܸH---B*CCCoݺeggG8s.\8`??v={L|ԛ7o?$))ryvvv&RRRBCC !/_UUUNR}7oS -y˗/-[/(;w~ߟ,Аd?xݻ4!&1` 9VVN2e !$&&Fdu,Vtt44yٳڵk``guٳz# Ǝ"$33s֬YV3uT4LsΎ;,XtzXƣ1*gdUzd`MFǏ_p훅bi[neee޽[TIaa k[3S6ncEaYğL2B9s۷o՟WND֭[}͛7k<|޽}zΎYs| Ծ}{S?MZ 0uףG-[8praff+^|I.**j&Ĩ222UBӧA}_5k֬ XmGV5SN+W\bff߀%ބ^^^KѮ];>D$}UVLTUdg2BϞ=1k=zɉ 3dACCcLyxb={0v0 /bbbZht$#hݺ5k2@`@rrrnݺt IFڱcw}t5zDDb:HtPk׮]bA)uuSN: wܸqLhHK,Q֭[}^ 5T$PD~~~LGhԮ\S0& tsT`Q1o߾}LhHlڴ#AdI&nz%&diit&>}Ҩ` XajjsST۶m;vXXXt3:ug:Lijjfgg3dwwwHdft%%%LI&LL23:wLQTNNAb̙7ng:,ri`FΝY,ݽ{w4ɓ'3@vp�Yf/J Ν;_{weq ,j:iK[dQ, %J\1TRr4J)aZ׊H+ sME!@uf~̍7`33yPS/y*Jv(JD`O4Iv(Jk[._8qBV@?}ٽ{=Zv_ R)--urrH>ͩ(!R<… cƌ 6sx|?K |Xp@Jeg@x d|m۶{2TϘ1uֹ hٲ͛7|͉'v}С'NB|G'Ovڔ)S6lP( BQXXzj;ٴiɓ[hڳgO58`Μ91113g>|̙3oݺuoGѬY&<<|ʔ)sI&o 6B,YŋwB5[i>#nݺ 666ׯ_h4BhV@=KT3g!/]$h۶+Ǐ !^uo6m4Myy[߸qC@V>v]8ؾ}g/r|Fw^7JF?!Ο?/;,\SNگk߸qC0##ο|g;:??fWXcLFӮ]Wj]U?믿.ͭz?B̙37> L !z] /n۶_~#-Vۻz裇zA5nMRӧFEE]?,++ jqOFפIeرw}… ՟+xb3B77;^p|⌕>>>id:رv-vM4莒& KJJV\Yɓ'׭[#EFU[[ !4΢lJ{/6qKU*կ?Z*---&&V01N&NP(F)PdIΒLT~i__ȭ[.X`ƌ'NB4o<77W_u!U*U1UHUpmw-RC̝;cǎ?^}511'xW|e>쳌/СCOݾ}ԩS`1@INQQQ!(**j%7o=x`rr6ÇsrrU (a"JxZYf;vLwgy&--m֬Y[nuuuB,[iǎڷ^zРA }|r\\\1 Bؒ3fg͚5wܦMݻ.rӧOQQK/SO=۷o֭֬[ v١CmXˋB\xqϞ=3޸qcݺuHW~vss6mŋ=<<-ZV-[&rJBBk:- BvrBB}va@m޼yŬ5S)))ׯ_1cBV۷/::Z[jz7eGGŋ΂q.vݥa|}}rrrZn-; lɒ%qqq7o>h۶``ML˥aJVL[N;Tȑ#߲ehX4`R(a6mf=%; [l:ujJJW~甔vɎ0JSITTTppo!;~xzz&&&NO?]p!88Xvp%#LR<{FVXX(;`x޽{LW2T(߲S믿*;`<==_|EA@S].Vml,QFɎW^yEv@X~sT*KKK\";ꆒBT !giASѬYFY/T*՚5kfΜ);(aBeQ6iDv@ LRd4`mf͚b f+aBJ%˥kӧOF0w0!Fv;uTTTTqq z@T*\A ))ƍFUPP ; '#LvslllΞ={Y{{_|Qv.sssB˗LRXX?JFHh++WF4`Q(!_|1j({{{BQ^^^[^(0?^v: 6TTzUR5lИy➌GuppatppӧTiӦ4 mmm|vРA͛77~*`y(!>Tݻ?%7nFyF3n8eJ֭[n=zTv x:ײ 2~`(!C=4k֬;;v @=uqԩw^ ȑ#k̵F6t@_(afϞ\IiڿPP_m5YJXJF77yU%9fgڵnnnj5jT?躸9RV`y(a*^{݄ߐ188xSWmgoo;99M, %#L˂ .fTT]vɠ4!;cƌ~]QQ1fyd yW6meңGvNGFF| ;nڸqcM42d<P2„899!ڷo ;,YDv ϟ?p?-چ988ۇU6(aZZh!]knӦMiiil! f'""}3gnn޼())pg?''1cj8q`9.]_tF/r wĈ-[4t(#F,[nݺurGXؔwʔ)<]DDĖ-[yF`|,Fm߾]cф•ȞVHH> dTQQdɒq0^x+VtEZdvڎ;***dbٳBPlqԩMԽ{w+9Ų芒/55I&k׮N0{nnn!!!5ԩS9r$tDV*66tƍJJJruu`ѣC iԨ/|\灃1g? 0{M4aĴ %#L|ŋ JNN0aBի-Z ڱcǭ[?N׮]駟Ν;װL* ˥aB>h~G-R*Bcǎ=$`0U@ dI9sv*UYY9v ekk+(--O2mX6 IDAT?::ZV0U@ JFZhѢիWK/.;,oacXմ Hriwy'##UV&**JPo(Fl_v'lɒ%#$i&d~~~|ܹs_y{{z흓SQQa;:99N,Mt W7l زeKINNV*-*..k@=;w\LLY۴ HGS3!\\\vgg T*L׭[';0ri\!O?]W^ݿ^s&*66v#F:u*cZEaV^iӦB!DIIɜ9s&MUuvZh٭Zb* ѻw? H0 7o\vܹs#""ccc{]}RRR/^\f{h"`}} 0RP2$,]tҥzuРA ڸq1##U@ hJ΢1GKs!DǎeLRZ瞓`-VڣdĚcC]zDNVZٯ_?AVi%#[RRSO=%X`u١swwoРkĴ 肒ftժU ,P(B={7!!Av.3ô 舒flϞ=]tygYhQFFFYYTwO?Tv QTT!;e* %#X``_|ac8pQbۻwewرĉ+**dg@V(i+++eO0`Çeg@d?c) ̙3eG@(iZjժU+)tri:dCWZ%;G[o>+++âdѣGGGY0,6~4hРAdN(i~۷oJF@ׯرDvԟFy|IZ-; P2!4 9sDGGٳRva)4n鑑:ta3+ W2$\pa̘1Su֪U+Faf+!˗U* f+a*j3fhݺunnZly͢7|sĉݻw:t'}ɓ']6e!Ć  BB^Z1}||6m4y-ZS;"&&fȐ!$W^`YGtppذaCvv͛.\8|p{{Soڴѣ666C-,,\`YfׯBDEEiF;Y:f>}mۖaÆ_GNǭ[$ `9BBBu_F̬c4 1 ٳ'OnܸСC-[lٲ~m```c !Μ9pB!D˖-====}crr2kիW>|xӦMβ3+`(a@666Bƍk9r?++Kc !vww˫zhgǿբE.\بQ#AV0e,]p=aÆ4S JFIIʕ+9yu_WVVV=ziiayyB/Q8qBP:uJv`~YB*++O?o|||dd֭[,X0cƌ' !7o=_u!U*U13檉۷߳En>2[WZUZZ~);fVQ2 /^(5kֱcDŽ{}gf͚uVWWW!IJe˜vء}ի ѷo_˗/3111;;[_PP+-))Ξ1cD۷s5֝ 3+`uZ(*HMMp ۵R?~mܸqU"֒y}Ћ?p͚݉5d:%RSS3{yI1c12Vy0...))IvI&M4Iv L3+pdΝ{w0 @w̬}P2’uUv .YYYS`Q(Xwu׮]oߖAt5j$; {2_#(G=zKÕ4١脒N(iZj%;_^^.;V=$$2ݛo9iҤ.]TVVU`ԙcXXXXX հaCPO&L0`@~dSRR 5aQ2ξ+WNQg;wWXakk]\\FiT0oooooo)xK=ژg|SN5Ij)(Qg#GϞ=;utԩ7xCv!hٲe˖-y;v!BBByR` '#EV,Y|_UvիWeG&Z###U*,խ[&LMYv"6667n|weg6lܸؐqcYv.۷Y`egYYY謬^v@믷i_h4rر5JFX7~6mD[/ʍ#JFX={N>}̙W\cĈUbWYYo>)P2J-]zd/}yegD+윔sΝ;w΂{:s挛۱c~aY=Q2z (XPP';JKKԩSjjkdgAf͚[ 2y4YT*LټysY@}P2B9dȐ)SvvvF;zGW\٠AA@=Q2BP(.\b m۶ɓeg1!G JF7xCvp…#GӲ@[[[ 0}!C}Y$Ճ۷o{gE/ҥKOv޽>qC rilmm7nO?͞=C+WTjzwiժUgΜԟ=/,6f`(iѢ_BB¥KjIiӦС,<<<|>00a4P7o}B0 !S}+R";Ѓcڵ+++ǎk<"Q2RYY}vBhjYRRҠA)#00ٹΣF XKrƍ?ΆQQYY??999=՟tpp6;CԊɓ'n\2JO? 6ƍ3fLyyygnj#+<@my{{wӦMB(/ݻWR{]VVv)`I Ҹq4nxȐ!CԁڵkSRR疦.++~aÆlRv`@vvvU+ƌsCرcoo=cyyѣG%ggI&,_xxxՊpy\K.U CFFTۻO>r CԓΝ;׬Yckkkkk[QQo߾ڿ ̙3KX-P(+kk`@G@)^{m׮]...СCj---B8992m޼uYYYF80YKcCK/i+z{G333R{zz?2zwƍǏL>{oAO3ge'ggg!̙3e 戒BBBt3''E\|Qu_$fkԨQNNΪU`Pov޽}||d;=<˫gϞN:~zqɪϜ94dff !T*Ujjhv5y-Zĉ3@>}uޤIvϞ=G}9s7iҤsӦM^fO MԢAz7n?زe_~yǎ/^|ǢӵTMgΜ͛k_mҤܹs&L8|ջ/!DqqqShye˖}g_~CN>#̎L-ŠxUjD!͛7sss<\PP 8|pNN۷W`(;tдiӄ/^СÓO>9jԨ~5mt͚5Cݾ}L9re!D~~222͛#}t!ĥKbbb:t0})**z饗z'x}͚5ۺuA@LgjMgϞl!Dll7֭[wE!ի_.xܦMxbEe˖ !\|r  1~CI`:uZ*HMMp ۵f!555,,̌V\~}ƌBZo߾h>;`tK Copyright 2011 Red Hat, Inc. This 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 . Success Stories =============== If you use the gcc python plugin to improve your code, we'd love to hear about it. If you want to share a success story here, please email the plugin's `mailing list `_. The `GNU Debugger `_ ------------------------------------------------ Bugs found in gdb by compiling it with the plugin's :ref:`gcc-with-cpychecker ` script: * http://sourceware.org/bugzilla/show_bug.cgi?id=13308 * http://sourceware.org/bugzilla/show_bug.cgi?id=13309 * http://sourceware.org/bugzilla/show_bug.cgi?id=13310 * http://sourceware.org/bugzilla/show_bug.cgi?id=13316 * http://sourceware.org/ml/gdb-patches/2011-06/msg00376.html * http://sourceware.org/ml/gdb-patches/2011-10/msg00391.html * http://sourceware.org/bugzilla/show_bug.cgi?id=13331 Tom Tromey also wrote specialized Python scripts to use the GCC plugin to locate bugs within GDB. One of his scripts analyzes gdb's resource-management code, which found some resource leaks and a possible crasher: * http://sourceware.org/ml/gdb-patches/2011-06/msg00408.html The other generates a whole-program call-graph, annotated with information on gdb's own exception-handling mechanism. A script then finds places where these exceptions were not properly integrated with gdb's embedded Python support: * http://sourceware.org/ml/gdb/2011-11/msg00002.html * http://sourceware.org/bugzilla/show_bug.cgi?id=13369 `LibreOffice `_ -------------------------------------------- Stephan Bergmann wrote a script to analyze LibreOffice's source code, detecting a particular usage pattern of C++ method calls: * https://fedorahosted.org/pipermail/gcc-python-plugin/2011-December/000136.html * https://bugs.freedesktop.org/show_bug.cgi?id=43460 `psycopg `_ -------------------------------------- Daniele Varrazzo used the plugin's :ref:`gcc-with-cpychecker ` script on `psycopg `_, the popular Python interface to `PostgreSQL `_, and was able to find and fix numerous subtle errors: * https://fedorahosted.org/pipermail/gcc-python-plugin/2012-March/000229.html * http://initd.org/psycopg/articles/2012/03/29/psycopg-245-released/ pycups ------ Bugs found in the `Python bindings for the CUPS API `_ by compiling it with the plugin's :ref:`gcc-with-cpychecker ` script: * https://fedorahosted.org/pycups/ticket/17 python-krbV ----------- Bug found in the `Python bindings for the Kerberos 5 API `_ by compiling it with the plugin's :ref:`gcc-with-cpychecker ` script: * https://fedorahosted.org/python-krbV/ticket/1 Bugs found in itself -------------------- Bugs found and fixed in the gcc Python plugin itself, by running the the plugin's :ref:`gcc-with-cpychecker ` script when compiling another copy: * various reference counting errors: * http://git.fedorahosted.org/git/?p=gcc-python-plugin.git;a=commitdiff;h=a9f48fac24a66c77007d99bf23f2eab188eb909e * http://git.fedorahosted.org/git/?p=gcc-python-plugin.git;a=commitdiff;h=2922ad81c8e0ea954d462433ecc83d86d9ebab68 * http://git.fedorahosted.org/git/?p=gcc-python-plugin.git;a=commitdiff;h=4642a564e03c9e2c8114bca206205ad9c8fbc308> * bad format string: https://fedorahosted.org/pipermail/gcc-python-plugin/2011-August/000065.html * minor const-correctness error: http://git.fedorahosted.org/git/?p=gcc-python-plugin.git;a=commitdiff;h=4fe4a83288e04be35a96d0bfec332197fb32c358 gcc-python-plugin-0.17/docs/tables-of-passes.rst000066400000000000000000000266651342215241600216610ustar00rootroot00000000000000.. This file is autogenerated, using: ./gcc-with-python generate-tables-of-passes-rst.py test.c All of GCC's passes =================== This diagram shows the various GCC optimization passes, arranged vertically, showing child passes via indentation. The lifetime of the various properties that they maintain is shown, giving the pass that initially creates the data (if any), the pass that destroys it (if any), and each pass that requires a particular property (based on the PROP_* flags). .. image:: passes.svg :width: 550px :height: 3302px :scale: 50% These tables contain the same information. The diagram and tables were autogenerated, using GCC 4.6.0 .. _all_lowering_passes: The lowering passes ------------------- ====================== =================== =================== ==================== Pass Name Required properties Provided properties Destroyed properties ====================== =================== =================== ==================== \*warn_unused_result gimple_any \*diagnose_omp_blocks gimple_any mudflap1 gimple_any omplower gimple_any gimple_lomp lower gimple_any gimple_lcf ehopt gimple_lcf eh gimple_lcf gimple_leh cfg gimple_leh cfg \*warn_function_return cfg \*build_cgraph_edges cfg ====================== =================== =================== ==================== .. _all_small_ipa_passes: The "small IPA" passes ---------------------- ================================ ======================================= =================== ==================== Pass Name Required properties Provided properties Destroyed properties ================================ ======================================= =================== ==================== \*free_lang_data gimple_any, gimple_lcf, gimple_leh, cfg visibility early_local_cleanups > \*free_cfg_annotations cfg > \*init_datastructures cfg > ompexp gimple_any > \*referenced_vars gimple_leh, cfg referenced_vars > ssa cfg, referenced_vars ssa > veclower cfg > \*early_warn_uninitialized ssa > \*rebuild_cgraph_edges cfg > inline_param > einline > early_optimizations > > \*remove_cgraph_callee_edges > > copyrename cfg, ssa > > ccp cfg, ssa > > forwprop cfg, ssa > > ealias cfg, ssa > > esra cfg, ssa > > copyprop cfg, ssa > > mergephi cfg, ssa > > cddce cfg, ssa > > eipa_sra > > tailr cfg, ssa > > switchconv cfg, ssa > > ehcleanup gimple_lcf > > profile cfg > > local-pure-const > > fnsplit cfg > release_ssa ssa > \*rebuild_cgraph_edges cfg > inline_param tree_profile_ipa > feedback_fnsplit cfg increase_alignment matrix-reorg emutls cfg, ssa ================================ ======================================= =================== ==================== .. _all_regular_ipa_passes: The "regular IPA" passes ------------------------ ================ ======================================= =================== ==================== Pass Name Required properties Provided properties Destroyed properties ================ ======================================= =================== ==================== whole-program gimple_any, gimple_lcf, gimple_leh, cfg ipa-profile cp cdtor inline pure-const static-var type-escape-var pta ipa_struct_reorg ================ ======================================= =================== ==================== .. _all_lto_gen_passes: Passes generating Link-Time Optimization data --------------------------------------------- ============== ======================================= =================== ==================== Pass Name Required properties Provided properties Destroyed properties ============== ======================================= =================== ==================== lto_gimple_out gimple_any, gimple_lcf, gimple_leh, cfg lto_decls_out ============== ======================================= =================== ==================== .. _all_passes: The "all other passes" catch-all -------------------------------- ============================== ======================================= =================== ==================================================== Pass Name Required properties Provided properties Destroyed properties ============================== ======================================= =================== ==================================================== ehdisp gimple_any, gimple_lcf, gimple_leh, cfg \*all_optimizations > \*remove_cgraph_callee_edges > \*strip_predict_hints cfg > copyrename cfg, ssa > cunrolli cfg, ssa > ccp cfg, ssa > forwprop cfg, ssa > cdce cfg, ssa > alias cfg, ssa > retslot ssa > phiprop cfg, ssa > fre cfg, ssa > copyprop cfg, ssa > mergephi cfg, ssa > vrp ssa > dce cfg, ssa > cselim cfg, ssa > ifcombine cfg, ssa > phiopt cfg, ssa > tailr cfg, ssa > ch cfg, ssa > stdarg cfg, ssa > cplxlower ssa gimple_lcx > sra cfg, ssa > copyrename cfg, ssa > dom cfg, ssa > phicprop cfg, ssa > dse cfg, ssa > reassoc cfg, ssa > dce cfg, ssa > forwprop cfg, ssa > phiopt cfg, ssa > objsz cfg, ssa > ccp cfg, ssa > copyprop cfg, ssa > sincos ssa > bswap ssa > crited cfg no_crit_edges > pre cfg, ssa, no_crit_edges > sink cfg, ssa, no_crit_edges > loop cfg > > loopinit cfg > > lim cfg > > copyprop cfg, ssa > > dceloop cfg, ssa > > unswitch cfg > > sccp cfg, ssa > > \*record_bounds cfg, ssa > > ckdd cfg, ssa > > ldist cfg, ssa > > copyprop cfg, ssa > > graphite0 cfg, ssa > > > graphite cfg, ssa > > > lim cfg > > > copyprop cfg, ssa > > > dceloop cfg, ssa > > ivcanon cfg, ssa > > ifcvt cfg, ssa > > vect cfg, ssa > > > veclower2 cfg > > > dceloop cfg, ssa > > pcom cfg > > cunroll cfg, ssa > > slp cfg, ssa > > parloops cfg, ssa > > aprefetch cfg, ssa > > ivopts cfg, ssa > > loopdone cfg > recip ssa > reassoc cfg, ssa > vrp ssa > dom cfg, ssa > phicprop cfg, ssa > cddce cfg, ssa > tracer > uninit ssa > dse cfg, ssa > forwprop cfg, ssa > phiopt cfg, ssa > fab cfg, ssa > widening_mul ssa > tailc cfg, ssa > copyrename cfg, ssa > uncprop cfg, ssa > local-pure-const cplxlower0 cfg gimple_lcx ehcleanup gimple_lcf resx gimple_lcf nrv cfg, ssa mudflap2 gimple_leh, cfg, ssa optimized cfg \*warn_function_noreturn cfg expand gimple_leh, cfg, ssa, gimple_lcx rtl gimple_any, gimple_lcf, gimple_leh, ssa, gimple_lomp \*rest_of_compilation rtl > \*init_function > sibling > rtl eh > initvals > unshare > vregs > into_cfglayout cfglayout > jump > subreg1 > dfinit > cse1 > fwprop1 > cprop cfglayout > rtl pre cfglayout > hoist cfglayout > cprop cfglayout > store_motion cfglayout > cse_local > ce1 > reginfo > loop2 > > loop2_init > > loop2_invariant > > loop2_unswitch > > loop2_unroll > > loop2_doloop > > loop2_done > web > cprop cfglayout > cse2 > dse1 > fwprop2 > auto_inc_dec > init-regs > ud dce > combine cfglayout > ce2 > bbpart cfglayout > regmove > outof_cfglayout cfglayout > split1 > subreg2 > no-opt dfinit > \*stack_ptr_mod > mode_sw > asmcons > sms > sched1 > ira > \*all-postreload rtl > > postreload > > gcse2 > > split2 > > zee > > cmpelim > > btl1 > > pro_and_epilogue > > dse2 > > csa > > peephole2 > > ce3 > > rnreg > > cprop_hardreg > > rtl dce > > bbro > > btl2 > > \*leaf_regs > > split4 > > sched2 > > \*stack_regs > > > split3 > > > stack > > alignments > > compgotos > > vartrack > > \*free_cfg cfg > > mach > > barriers > > dbr > > split5 > > eh_ranges > > shorten > > nothrow > > final > dfinish \*clean_state rtl ============================== ======================================= =================== ==================================================== gcc-python-plugin-0.17/docs/tree.rst000066400000000000000000001364441342215241600174450ustar00rootroot00000000000000.. Copyright 2011, 2012 David Malcolm Copyright 2011, 2012 Red Hat, Inc. This 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 . gcc.Tree and its subclasses =========================== The various language front-ends for GCC emit "tree" structures (which I believe are actually graphs), used throughout the rest of the internal representation of the code passing through GCC. .. py:class:: gcc.Tree A ``gcc.Tree`` is a wrapper around GCC's `tree` type .. py:method:: debug() Dump the tree to stderr, using GCC's own diagnostic routines .. py:attribute:: type Instance of :py:class:`gcc.Tree` giving the type of the node .. py:attribute:: addr (long) The address of the underlying GCC object in memory The __str__ method is implemented using GCC's own pretty-printer for trees, so e.g.:: str(t) might return:: 'int (int, char * *)' for a `gcc.FunctionDecl` .. py:attribute:: str_no_uid A string representation of this object, like str(), but without including any internal UIDs. This is intended for use in selftests that compare output against some expected value, to avoid embedding values that change into the expected output. For example, given the type declaration above, where `str(t)` might return:: 'int (int, char * *)' where the UID "531" is liable to change from compile to compile, whereas `t.str_no_uid` has value:: 'int (int, char * *)' which won't arbitrarily change each time. There are numerous subclasses of :py:class:`gcc.Tree`, some with numerous subclasses of their own. Some important parts of the class hierarchy include: ================================== ======================================= Subclass Meaning ================================== ======================================= :py:class:`gcc.Binary` A binary arithmetic expression, with numerous subclasses :py:class:`gcc.Block` A symbol-binding block :py:class:`gcc.Comparison` A relational operators (with various subclasses) :py:class:`gcc.Constant` Subclasses for constants :py:class:`gcc.Constructor` An aggregate value (e.g. in C, a structure or array initializer) :py:class:`gcc.Declaration` Subclasses relating to declarations (variables, functions, etc) :py:class:`gcc.Expression` Subclasses relating to expressions :py:class:`gcc.IdentifierNode` A name :py:class:`gcc.Reference` Subclasses for relating to reference to storage (e.g. pointer values) :py:class:`gcc.SsaName` A variable reference for SSA analysis :py:class:`gcc.Statement` Subclasses for statement expressions, which have side-effects :py:class:`gcc.Type` Subclasses for describing the types of variables :py:class:`gcc.Unary` Subclasses for unary arithmetic expressions ================================== ======================================= .. note:: Each subclass of :py:class:`gcc.Tree` is typically named after either one of the `enum tree_code_class` or `enum tree_code` values, with the names converted to Camel Case: For example a :py:class:`gcc.Binary` is a wrapper around a `tree` of type `tcc_binary`, and a :py:class:`gcc.PlusExpr` is a wrapper around a `tree` of type `PLUS_EXPR`. As of this writing, only a small subset of the various fields of the different subclasses have been wrapped yet, but it's generally easy to add new ones. To add new fields, I've found it easiest to look at `gcc/tree.h` and `gcc/print-tree.c` within the GCC source tree and use the `print_node` function to figure out what the valid fields are. With that information, you should then look at `generate-tree-c.py`, which is the code that generates the Python wrapper classes (it's used when building the plugin to create `autogenerated-tree.c`). Ideally when exposing a field to Python you should also add it to the API documentation, and add a test case. .. py:function:: gccutils.pformat(tree) This function attempts to generate a debug dump of a :py:class:`gcc.Tree` and all of its "interesting" attributes, recursively. It's loosely modelled on Python's `pprint` module and GCC's own `debug_tree` diagnostic routine using indentation to try to show the structure. It returns a string. It differs from :py:meth:`gcc.Tree.debug()` in that it shows the Python wrapper objects, rather than the underlying GCC data structures themselves. For example, it can't show attributes that haven't been wrapped yet. Objects that have already been reported within this call are abbreviated to "..." to try to keep the output readable. Example output:: , ) .function = gcc.Function('main') .location = /home/david/coding/gcc-python/test.c:15 .name = 'main' .type = str() = 'int (int, char * *)' superclasses = (, ) .name = None .type = str() = 'int' superclasses = (, ) .const = False .name = , ) .location = None .name = 'int' .pointer = str() = ' *' superclasses = (, ) .dereference = ... ("gcc.TypeDecl('int')") .name = None .type = ... ("gcc.TypeDecl('int')") > .type = ... ('') > .precision = 32 .restrict = False .type = None .unsigned = False .volatile = False > > > .. py:function:: gccutils.pprint(tree) Similar to :py:meth:`gccutils.pformat()`, but prints the output to stdout. (should this be stderr instead? probably should take a stream as an arg, but what should the default be?) Blocks ------ .. py:class:: gcc.Block A symbol binding block, such as the global symbols within a compilation unit. .. py:attribute:: vars The list of :py:class:`gcc.Tree` for the declarations and labels in this block Declarations ------------ .. py:class:: gcc.Declaration A subclass of :py:class:`gcc.Tree` indicating a declaration Corresponds to the `tcc_declaration` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: name (string) the name of this declaration .. py:attribute:: location The :py:class:`gcc.Location` for this declaration .. py:attribute:: is_artificial (bool) Is this declaration a compiler-generated entity, rather than one provided by the user? An example of such an "artificial" declaration occurs within the arguments of C++ methods: the initial `this` argument is a compiler-generated :py:class:`gcc.ParmDecl`. .. py:attribute:: is_builtin (bool) Is this declaration a compiler-builtin? .. py:class:: gcc.FieldDecl A subclass of :py:class:`gcc.Declaration` indicating the declaration of a field within a structure. .. py:attribute:: name (string) The name of this field .. py:class:: gcc.FunctionDecl A subclass of :py:class:`gcc.Declaration` indicating the declaration of a function. Internally, this wraps a `(struct tree_function_decl *)` .. py:attribute:: function The :py:class:`gcc.Function` for this declaration .. py:attribute:: arguments List of :py:class:`gcc.ParmDecl` representing the arguments of this function .. py:attribute:: result The :py:class:`gcc.ResultDecl` representing the return value of this function .. py:attribute:: fullname .. note:: This attribute is only usable with C++ code. Attempting to use it from another language will lead to a `RuntimeError` exception. (string) The "full name" of this function, including the scope, return type and default arguments. For example, given this code: .. code-block:: c++ namespace Example { struct Coord { int x; int y; }; class Widget { public: void set_location(const struct Coord& coord); }; }; `set_location`'s fullname is:: 'void Example::Widget::set_location(const Example::Coord&)' .. py:attribute:: callgraph_node The :py:class:`gcc.CallgraphNode` for this function declaration, or `None` .. py:attribute:: is_public (bool) For C++: is this declaration "public" .. py:attribute:: is_private (bool) For C++: is this declaration "private" .. py:attribute:: is_protected (bool) For C++: is this declaration "protected" .. py:attribute:: is_static (bool) For C++: is this declaration "static" .. py:class:: gcc.ParmDecl A subclass of :py:class:`gcc.Declaration` indicating the declaration of a parameter to a function or method. .. py:class:: gcc.ResultDecl A subclass of :py:class:`gcc.Declaration` declararing a dummy variable that will hold the return value from a function. .. py:class:: gcc.VarDecl A subclass of :py:class:`gcc.Declaration` indicating the declaration of a variable (e.g. a global or a local). .. py:attribute:: initial The initial value for this variable as a :py:class:`gcc.Constructor`, or None .. py:attribute:: static (boolean) Is this variable to be allocated with static storage? .. py:class:: gcc.NamespaceDecl A subclass of :py:class:`gcc.Declaration` representing a C++ namespace .. py:attribute:: alias_of The :py:class:`gcc.NamespaceDecl` which this namespace is an alias of or None if this namespace is not an alias. .. py:attribute:: declarations .. note:: This attribute is only usable with non-alias namespaces. Accessing it on an alias will lead to a RuntimeError exception. List of :py:class:`gcc.Declaration` objects in this namespace. This attribute is only valid for non-aliases .. py:attribute:: namespaces .. note:: This attribute is only usable with non-alias namespaces. Accessing it on an alias will lead to a RuntimeError exception. List of :py:class:`gcc.NamespaceDecl` objects nested in this namespace. This attribute is only valid for non-aliases .. py:method:: lookup(name) Locate the given name within the namespace, returning a :py:class:`gcc.Tree` or `None` .. py:method:: unalias() Always returns a :py:class:`gcc.NamespaceDecl` object which is not an alias. Returns self if this namespace is not an alias. .. Declaration .. ClassMethodDecl .. ConstDecl .. DebugExprDecl .. FieldDecl .. FunctionDecl .. ImportedDecl .. InstanceMethodDecl .. KeywordDecl .. LabelDecl .. NamespaceDecl .. ParmDecl .. PropertyDecl .. ResultDecl .. TemplateDecl .. TranslationUnitDecl .. TypeDecl .. UsingDecl .. VarDecl Types ----- .. py:class:: gcc.Type A subclass of `gcc.Tree` indicating a type Corresponds to the `tcc_type` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: name The :py:class:`gcc.IdentifierNode` for the name of the type, or `None`. .. py:attribute:: pointer The :py:class:`gcc.PointerType` representing the `(this_type *)` type .. py:attribute:: attributes The user-defined attributes on this type (using GCC's `__attribute` syntax), as a dictionary (mapping from attribute names to list of values). Typically this will be the empty dictionary. .. py:attribute:: sizeof `sizeof()` this type, as an `int`, or raising `TypeError` for those types which don't have a well-defined size .. note:: This attribute is not usable from within `lto1`; attempting to use it there will lead to a `RuntimeError` exception. Additional attributes for various :py:class:`gcc.Type` subclasses: .. py:attribute:: const (Boolean) Does this type have the `const` modifier? .. py:attribute:: const_equivalent The :py:class:`gcc.Type` for the `const` version of this type .. py:attribute:: volatile (Boolean) Does this type have the `volatile` modifier? .. py:attribute:: volatile_equivalent The :py:class:`gcc.Type` for the `volatile` version of this type .. py:attribute:: restrict (Boolean) Does this type have the `restrict` modifier? .. py:attribute:: restrict_equivalent The :py:class:`gcc.Type` for the `restrict` version of this type .. py:attribute:: unqualified_equivalent The :py:class:`gcc.Type` for the version of this type that does not have any qualifiers. The standard C types are accessible via class methods of :py:class:`gcc.Type`. They are only created by GCC after plugins are loaded, and so they're only visible during callbacks, not during the initial run of the code. (yes, having them as class methods is slightly clumsy). Each of the following returns a :py:class:`gcc.Type` instance representing the given type (or None at startup before any passes, when the types don't yet exist) ============================= ===================== Class method C Type ============================= ===================== gcc.Type.void() `void` gcc.Type.size_t() `size_t` gcc.Type.char() `char` gcc.Type.signed_char() `signed char` gcc.Type.unsigned_char() `unsigned char` gcc.Type.double() `double` gcc.Type.float() `float` gcc.Type.short() `short` gcc.Type.unsigned_short() `unsigned short` gcc.Type.int() `int` gcc.Type.unsigned_int() `unsigned int` gcc.Type.long() `long` gcc.Type.unsigned_long() `unsigned long` gcc.Type.long_double() `long double` gcc.Type.long_long() `long long` gcc.Type.unsigned_long_long() `unsigned long long` gcc.Type.int128() `int128` gcc.Type.unsigned_int128() `unsigned int128` gcc.Type.uint32() `uint32` gcc.Type.uint64() `uint64` ============================= ===================== .. py:class:: gcc.IntegerType Subclass of :py:class:`gcc.Type`, adding a few properties: .. py:attribute:: unsigned (Boolean) True for 'unsigned', False for 'signed' .. py:attribute:: precision (int) The precision of this type in bits, as an int (e.g. 32) .. py:attribute:: signed_equivalent The gcc.IntegerType for the signed version of this type .. note:: This attribute is not usable from within `lto1`; attempting to use it there will lead to a `RuntimeError` exception. .. py:attribute:: unsigned_equivalent The gcc.IntegerType for the unsigned version of this type .. note:: This attribute is not usable from within `lto1`; attempting to use it there will lead to a `RuntimeError` exception. .. py:attribute:: max_value The maximum possible value for this type, as a :py:class:`gcc.IntegerCst` .. py:attribute:: min_value The minimum possible value for this type, as a :py:class:`gcc.IntegerCst` .. py:class:: gcc.FloatType Subclass of :py:class:`gcc.Type` representing C's `float` and `double` types .. py:attribute:: precision (int) The precision of this type in bits (32 for `float`; 64 for `double`) .. py:class:: gcc.PointerType Subclass of :py:class:`gcc.Type` representing a pointer type, such as an `int *` .. py:attribute:: dereference The :py:class:`gcc.Type` that this type points to. In the above example (`int *`), this would be the `int` type. .. py:class:: gcc.EnumeralType Subclass of :py:class:`gcc.Type` representing an enumeral type. .. py:attribute:: values A list of tuple representing the constants defined in this enumeration. Each tuple consists of two elements; the first being the name of the constant, a :py:class:`gcc.IdentifierNode`; and the second being the value, a :py:class:`gcc.Constant`. .. py:class:: gcc.ArrayType Subclass of :py:class:`gcc.Type` representing an array type. For example, in a C declaration such as:: char buf[16] we have a :py:class:`gcc.VarDecl` for `buf`, and its type is an instance of :py:class:`gcc.ArrayType`, representing `char [16]`. .. py:attribute:: dereference The :py:class:`gcc.Type` that this type points to. In the above example, this would be the `char` type. .. py:attribute:: range The :py:class:`gcc.Type` that represents the range of the array's indices. If the array has a known range, then this will ordinarily be an :py:class:`gcc.IntegerType` whose `min_value` and `max_value` are the (inclusive) bounds of the array. If the array does not have a known range, then this attribute will be `None`. That is, in the example above, `range.min_val` is `0`, and `range.max_val` is `15`. But, for a C declaration like:: extern char array[]; the type's `range` would be `None`. .. py:class:: gcc.VectorType .. py:attribute:: dereference The :py:class:`gcc.Type` that this type points to .. py:class:: gcc.FunctionType Subclass of :py:class:`gcc.Type` representing the type of a given function (or or a typedef to a function type, e.g. for callbacks). See also :py:class:`gcc.FunctionType` The `type` attribute holds the return type. .. py:attribute:: is_variadic True if this type represents a variadic function. Note that for a variadic function, the final `...` argument is not explicitly represented in `argument_types`. .. py:attribute:: argument_types A tuple of :py:class:`gcc.Type` instances, representing the function's argument types .. py:function:: gccutils.get_nonnull_arguments(funtype) This is a utility function for working with the `"nonnull"` custom attribute on function types: http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html Return a `frozenset` of 0-based integers, giving the arguments for which we can assume "nonnull-ness", handling the various cases of: * the attribute isn't present (returning the empty frozenset) * the attribute is present, without args (all pointer args are non-NULL) * the attribute is present, with a list of 1-based argument indices (Note that the result is still 0-based) .. py:class:: gcc.MethodType Subclass of :py:class:`gcc.Type` representing the type of a given method. Similar to :py:class:`gcc.FunctionType` The `type` attribute holds the return type. .. py:attribute:: argument_types A tuple of :py:class:`gcc.Type` instances, representing the function's argument types .. py:class:: gcc.RecordType A compound type, such as a C `struct` .. py:attribute:: fields The fields of this type, as a list of :py:class:`gcc.FieldDecl` instances .. py:attribute:: methods The methods of this type, as a list of :py:class:`gcc.MethodType` instances You can look up C structures by looking within the top-level :py:class:`gcc.Block` within the current translation unit. For example, given this sample C code: .. literalinclude:: ../tests/examples/c/struct/input.c :lines: 20-30 :language: c then the following Python code: .. literalinclude:: ../tests/examples/c/struct/script.py :lines: 21-40 will generate this output: .. literalinclude:: ../tests/examples/c/struct/stdout.txt Constants --------- .. py:class:: gcc.Constant Subclass of :py:class:`gcc.Tree` indicating a constant value. Corresponds to the `tcc_constant` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: constant The actual value of this constant, as the appropriate Python type: ============================== =============== Subclass Python type ============================== =============== .. py:class:: ComplexCst .. py:class:: FixedCst .. py:class:: IntegerCst `int` or `long` .. py:class:: PtrmemCst .. py:class:: RealCst `float` .. py:class:: StringCst `str` .. py:class:: VectorCst ============================== =============== Binary Expressions ------------------ .. py:class:: gcc.Binary Subclass of :py:class:`gcc.Tree` indicating a binary expression. Corresponds to the `tcc_binary` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: location The :py:class:`gcc.Location` for this binary expression .. py:classmethod:: get_symbol() Get the symbol used in debug dumps for this :py:class:`gcc.Binary` subclass, if any, as a `str`. A table showing these strings can be seen :ref:`here `. Has subclasses for the various kinds of binary expression. These include: .. These tables correspond to GCC's "tree.def" Simple arithmetic: ============================ ====================== ============== Subclass C/C++ operators enum tree_code ============================ ====================== ============== .. py:class:: gcc.PlusExpr `+` PLUS_EXPR .. py:class:: gcc.MinusExpr `-` MINUS_EXPR .. py:class:: gcc.MultExpr `*` MULT_EXPR ============================ ====================== ============== Pointer addition: ================================= ================= ================= Subclass C/C++ operators enum tree_code ================================= ================= ================= .. py:class:: gcc.PointerPlusExpr POINTER_PLUS_EXPR ================================= ================= ================= Various division operations: ============================== =============== Subclass C/C++ operators ============================== =============== .. py:class:: gcc.TruncDivExr .. py:class:: gcc.CeilDivExpr .. py:class:: gcc.FloorDivExpr .. py:class:: gcc.RoundDivExpr ============================== =============== The remainder counterparts of the above division operators: ============================== =============== Subclass C/C++ operators ============================== =============== .. py:class:: gcc.TruncModExpr .. py:class:: gcc.CeilModExpr .. py:class:: gcc.FloorModExpr .. py:class:: gcc.RoundModExpr ============================== =============== Division for reals: =================================== ====================== Subclass C/C++ operators =================================== ====================== .. py:class:: gcc.RdivExpr =================================== ====================== Division that does not need rounding (e.g. for pointer subtraction in C): =================================== ====================== Subclass C/C++ operators =================================== ====================== .. py:class:: gcc.ExactDivExpr =================================== ====================== Max and min: =================================== ====================== Subclass C/C++ operators =================================== ====================== .. py:class:: gcc.MaxExpr .. py:class:: gcc.MinExpr =================================== ====================== Shift and rotate operations: =================================== ====================== Subclass C/C++ operators =================================== ====================== .. py:class:: gcc.LrotateExpr .. py:class:: gcc.LshiftExpr `<<`, `<<=` .. py:class:: gcc.RrotateExpr .. py:class:: gcc.RshiftExpr `>>`, `>>=` =================================== ====================== Bitwise binary expressions: =================================== ========================= Subclass C/C++ operators =================================== ========================= .. py:class:: gcc.BitAndExpr `&`, `&=` (bitwise "and") .. py:class:: gcc.BitIorExpr `|`, `|=` (bitwise "or") .. py:class:: gcc.BitXorExpr `^`, `^=` (bitwise "xor") =================================== ========================= Other gcc.Binary subclasses: ======================================== ================================== Subclass Usage ======================================== ================================== .. py:class:: gcc.CompareExpr .. py:class:: gcc.CompareGExpr .. py:class:: gcc.CompareLExpr .. py:class:: gcc.ComplexExpr .. py:class:: gcc.MinusNomodExpr .. py:class:: gcc.PlusNomodExpr .. py:class:: gcc.RangeExpr .. py:class:: gcc.UrshiftExpr .. py:class:: gcc.VecExtractevenExpr .. py:class:: gcc.VecExtractoddExpr .. py:class:: gcc.VecInterleavehighExpr .. py:class:: gcc.VecInterleavelowExpr .. py:class:: gcc.VecLshiftExpr .. py:class:: gcc.VecPackFixTruncExpr .. py:class:: gcc.VecPackSatExpr .. py:class:: gcc.VecPackTruncExpr .. py:class:: gcc.VecRshiftExpr .. py:class:: gcc.WidenMultExpr .. py:class:: gcc.WidenMultHiExpr .. py:class:: gcc.WidenMultLoExpr .. py:class:: gcc.WidenSumExpr ======================================== ================================== Unary Expressions ----------------- .. py:class:: gcc.Unary Subclass of :py:class:`gcc.Tree` indicating a unary expression (i.e. taking a single argument). Corresponds to the `tcc_unary` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: operand The operand of this operator, as a :py:class:`gcc.Tree`. .. py:attribute:: location The :py:class:`gcc.Location` for this unary expression .. py:classmethod:: get_symbol() Get the symbol used in debug dumps for this :py:class:`gcc.Unary` subclass, if any, as a `str`. A table showing these strings can be seen :ref:`here `. Subclasses include: ====================================== ================================================== Subclass Meaning; C/C++ operators ====================================== ================================================== .. py:class:: gcc.AbsExpr Absolute value .. py:class:: gcc.AddrSpaceConvertExpr Conversion of pointers between address spaces .. py:class:: gcc.BitNotExpr `~` (bitwise "not") .. py:class:: gcc.CastExpr .. py:class:: gcc.ConjExpr For complex types: complex conjugate .. py:class:: gcc.ConstCastExpr .. py:class:: gcc.ConvertExpr .. py:class:: gcc.DynamicCastExpr .. py:class:: gcc.FixTruncExpr Convert real to fixed-point, via truncation .. py:class:: gcc.FixedConvertExpr .. py:class:: gcc.FloatExpr Convert integer to real .. py:class:: gcc.NegateExpr Unary negation .. py:class:: gcc.NoexceptExpr .. py:class:: gcc.NonLvalueExpr .. py:class:: gcc.NopExpr .. py:class:: gcc.ParenExpr .. py:class:: gcc.ReducMaxExpr .. py:class:: gcc.ReducMinExpr .. py:class:: gcc.ReducPlusExpr .. py:class:: gcc.ReinterpretCastExpr .. py:class:: gcc.StaticCastExpr .. py:class:: gcc.UnaryPlusExpr ====================================== ================================================== Comparisons ------------ .. py:class:: gcc.Comparison Subclass of :py:class:`gcc.Tree` for comparison expressions Corresponds to the `tcc_comparison` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: location The :py:class:`gcc.Location` for this comparison .. py:classmethod:: get_symbol() Get the symbol used in debug dumps for this :py:class:`gcc.Comparison` subclass, if any, as a `str`. A table showing these strings can be seen :ref:`here `. Subclasses include: ===================================== ====================== Subclass C/C++ operators ===================================== ====================== .. py:class:: EqExpr `==` .. py:class:: GeExpr `>=` .. py:class:: GtExpr `>` .. py:class:: LeExpr `<=` .. py:class:: LtExpr `<` .. py:class:: LtgtExpr .. py:class:: NeExpr `!=` .. py:class:: OrderedExpr .. py:class:: UneqExpr .. py:class:: UngeExpr .. py:class:: UngtExpr .. py:class:: UnleExpr .. py:class:: UnltExpr .. py:class:: UnorderedExpr ===================================== ====================== References to storage --------------------- .. py:class:: gcc.Reference Subclass of :py:class:`gcc.Tree` for expressions involving a reference to storage. Corresponds to the `tcc_reference` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: location The :py:class:`gcc.Location` for this storage reference .. py:classmethod:: get_symbol() Get the symbol used in debug dumps for this :py:class:`gcc.Reference` subclass, if any, as a `str`. A table showing these strings can be seen :ref:`here `. .. py:class:: gcc.ArrayRef A subclass of :py:class:`gcc.Reference` for expressions involving an array reference: .. code-block:: c unsigned char buffer[4096]; ... /* The left-hand side of this gcc.GimpleAssign is a gcc.ArrayRef: */ buffer[42] = 0xff; .. py:attribute:: array The :py:class:`gcc.Tree` for the array within the reference (`gcc.VarDecl('buffer')` in the example above) .. py:attribute:: index The :py:class:`gcc.Tree` for the index within the reference (`gcc.IntegerCst(42)` in the example above) .. py:class:: gcc.ComponentRef A subclass of :py:class:`gcc.Reference` for expressions involving a field lookup. This can mean either a direct field lookup, as in: .. code-block:: c struct mystruct s; ... s.idx = 42; or dereferenced field lookup: .. code-block:: c struct mystruct *p; ... p->idx = 42; .. py:attribute:: target The :py:class:`gcc.Tree` for the container of the field (either `s` or `*p` in the examples above) .. py:attribute:: field The :py:class:`gcc.FieldDecl` for the field within the target. .. py:class:: gcc.MemRef A subclass of :py:class:`gcc.Reference` for expressions involving dereferencing a pointer: .. code-block:: c int p, *q; ... p = *q; .. py:attribute:: operand The :py:class:`gcc.Tree` for the expression describing the target of the pointer Other subclasses of :py:class:`gcc.Reference` include: ===================================== ====================== Subclass C/C++ operators ===================================== ====================== .. py:class:: ArrayRangeRef .. py:class:: AttrAddrExpr .. py:class:: BitFieldRef .. py:class:: ImagpartExpr .. py:class:: IndirectRef .. py:class:: MemberRef .. py:class:: OffsetRef .. py:class:: RealpartExpr .. py:class:: ScopeRef .. py:class:: TargetMemRef .. py:class:: UnconstrainedArrayRef .. py:class:: ViewConvertExpr ===================================== ====================== Other expression subclasses --------------------------- .. py:class:: gcc.Expression Subclass of :py:class:`gcc.Tree` indicating an expression that doesn't fit into the other categories. Corresponds to the `tcc_expression` value of `enum tree_code_class` within GCC's own C sources. .. py:attribute:: location The :py:class:`gcc.Location` for this expression .. py:classmethod:: get_symbol() Get the symbol used in debug dumps for this :py:class:`gcc.Expression` subclass, if any, as a `str`. A table showing these strings can be seen :ref:`here `. Subclasses include: ===================================== ====================== Subclass C/C++ operators ===================================== ====================== .. py:class:: gcc.AddrExpr .. py:class:: gcc.AlignofExpr .. py:class:: gcc.ArrowExpr .. py:class:: gcc.AssertExpr .. py:class:: gcc.AtEncodeExpr .. py:class:: gcc.BindExpr .. py:class:: gcc.CMaybeConstExpr .. py:class:: gcc.ClassReferenceExpr .. py:class:: gcc.CleanupPointExpr .. py:class:: gcc.CompoundExpr .. py:class:: gcc.CompoundLiteralExpr .. py:class:: gcc.CondExpr .. py:class:: gcc.CtorInitializer .. py:class:: gcc.DlExpr .. py:class:: gcc.DotProdExpr .. py:class:: gcc.DotstarExpr .. py:class:: gcc.EmptyClassExpr .. py:class:: gcc.ExcessPrecisionExpr .. py:class:: gcc.ExprPackExpansion .. py:class:: gcc.ExprStmt .. py:class:: gcc.FdescExpr .. py:class:: gcc.FmaExpr .. py:class:: gcc.InitExpr .. py:class:: gcc.MessageSendExpr .. py:class:: gcc.ModifyExpr .. py:class:: gcc.ModopExpr .. py:class:: gcc.MustNotThrowExpr .. py:class:: gcc.NonDependentExpr .. py:class:: gcc.NontypeArgumentPack .. py:class:: gcc.NullExpr .. py:class:: gcc.NwExpr .. py:class:: gcc.ObjTypeRef .. py:class:: gcc.OffsetofExpr .. py:class:: gcc.PolynomialChrec .. py:class:: gcc.PostdecrementExpr .. py:class:: gcc.PostincrementExpr .. py:class:: gcc.PredecrementExpr .. py:class:: gcc.PredictExpr .. py:class:: gcc.PreincrementExpr .. py:class:: gcc.PropertyRef .. py:class:: gcc.PseudoDtorExpr .. py:class:: gcc.RealignLoad .. py:class:: gcc.SaveExpr .. py:class:: gcc.ScevKnown .. py:class:: gcc.ScevNotKnown .. py:class:: gcc.SizeofExpr .. py:class:: gcc.StmtExpr .. py:class:: gcc.TagDefn .. py:class:: gcc.TargetExpr .. py:class:: gcc.TemplateIdExpr .. py:class:: gcc.ThrowExpr .. py:class:: gcc.TruthAndExpr .. py:class:: gcc.TruthAndifExpr .. py:class:: gcc.TruthNotExpr .. py:class:: gcc.TruthOrExpr .. py:class:: gcc.TruthOrifExpr .. py:class:: gcc.TruthXorExpr .. py:class:: gcc.TypeExpr .. py:class:: gcc.TypeidExpr .. py:class:: gcc.VaArgExpr .. py:class:: gcc.VecCondExpr .. py:class:: gcc.VecDlExpr .. py:class:: gcc.VecInitExpr .. py:class:: gcc.VecNwExpr .. py:class:: gcc.WidenMultMinusExpr .. py:class:: gcc.WidenMultPlusExpr .. py:class:: gcc.WithCleanupExpr .. py:class:: gcc.WithSizeExpr ===================================== ====================== TODO Statements ---------- .. py:class:: gcc.Statement A subclass of :py:class:`gcc.Tree` for statements Corresponds to the `tcc_statement` value of `enum tree_code_class` within GCC's own C sources. .. py:class:: gcc.CaseLabelExpr A subclass of :py:class:`gcc.Statement` for the `case` and `default` labels within a `switch` statement. .. py:attribute:: low * for single-valued case labels, the value, as a :py:class:`gcc.Tree` * for range-valued case labels, the lower bound, as a :py:class:`gcc.Tree` * `None` for the default label .. py:attribute:: high For range-valued case labels, the upper bound, as a :py:class:`gcc.Tree`. `None` for single-valued case labels, and for the default label .. py:attribute:: target The target of the case label, as a :py:class:`gcc.LabelDecl` SSA Names --------- .. py:class:: gcc.SsaName A subclass of :py:class:`gcc.Tree` representing a variable references during SSA analysis. New SSA names are created every time a variable is assigned a new value. .. py:attribute:: var The variable being referenced, as a :py:class:`gcc.VarDecl` or :py:class:`gcc.ParmDecl` .. py:attribute:: def_stmt The :py:class:`gcc.Gimple` statement which defines this SSA name .. py:attribute:: version An `int` value giving the version number of this SSA name .. Here's a dump of the class hierarchy, from help(gcc): .. Tree .. ArgumentPackSelect .. Baselink .. Binary .. BitAndExpr .. BitIorExpr .. BitXorExpr .. CeilDivExpr .. CeilModExpr .. CompareExpr .. CompareGExpr .. CompareLExpr .. ComplexExpr .. ExactDivExpr .. FloorDivExpr .. FloorModExpr .. LrotateExpr .. LshiftExpr .. MaxExpr .. MinExpr .. MinusExpr .. MinusNomodExpr .. MultExpr .. PlusExpr .. PlusNomodExpr .. PointerPlusExpr .. RangeExpr .. RdivExpr .. RoundDivExpr .. RoundModExpr .. RrotateExpr .. RshiftExpr .. TruncDivExpr .. TruncModExpr .. UrshiftExpr .. VecExtractevenExpr .. VecExtractoddExpr .. VecInterleavehighExpr .. VecInterleavelowExpr .. VecLshiftExpr .. VecPackFixTruncExpr .. VecPackSatExpr .. VecPackTruncExpr .. VecRshiftExpr .. WidenMultExpr .. WidenMultHiExpr .. WidenMultLoExpr .. WidenSumExpr .. Block .. Comparison .. EqExpr .. GeExpr .. GtExpr .. LeExpr .. LtExpr .. LtgtExpr .. NeExpr .. OrderedExpr .. UneqExpr .. UngeExpr .. UngtExpr .. UnleExpr .. UnltExpr .. UnorderedExpr .. Constant .. ComplexCst .. FixedCst .. IntegerCst .. PtrmemCst .. RealCst .. StringCst .. VectorCst .. Constructor .. Declaration .. ClassMethodDecl .. ConstDecl .. DebugExprDecl .. FieldDecl .. FunctionDecl .. ImportedDecl .. InstanceMethodDecl .. KeywordDecl .. LabelDecl .. NamespaceDecl .. ParmDecl .. PropertyDecl .. ResultDecl .. TemplateDecl .. TranslationUnitDecl .. TypeDecl .. UsingDecl .. VarDecl .. DefaultArg .. ErrorMark .. Expression .. AddrExpr .. AlignofExpr .. ArrowExpr .. AssertExpr .. AtEncodeExpr .. BindExpr .. CMaybeConstExpr .. ClassReferenceExpr .. CleanupPointExpr .. CompoundExpr .. CompoundLiteralExpr .. CondExpr .. CtorInitializer .. DlExpr .. DotProdExpr .. DotstarExpr .. EmptyClassExpr .. ExcessPrecisionExpr .. ExprPackExpansion .. ExprStmt .. FdescExpr .. FmaExpr .. InitExpr .. MessageSendExpr .. ModifyExpr .. ModopExpr .. MustNotThrowExpr .. NonDependentExpr .. NontypeArgumentPack .. NullExpr .. NwExpr .. ObjTypeRef .. OffsetofExpr .. PolynomialChrec .. PostdecrementExpr .. PostincrementExpr .. PredecrementExpr .. PredictExpr .. PreincrementExpr .. PropertyRef .. PseudoDtorExpr .. RealignLoad .. SaveExpr .. ScevKnown .. ScevNotKnown .. SizeofExpr .. StmtExpr .. TagDefn .. TargetExpr .. TemplateIdExpr .. ThrowExpr .. TruthAndExpr .. TruthAndifExpr .. TruthNotExpr .. TruthOrExpr .. TruthOrifExpr .. TruthXorExpr .. TypeExpr .. TypeidExpr .. VaArgExpr .. VecCondExpr .. VecDlExpr .. VecInitExpr .. VecNwExpr .. WidenMultMinusExpr .. WidenMultPlusExpr .. WithCleanupExpr .. WithSizeExpr .. IdentifierNode .. LambdaExpr .. OmpClause .. OptimizationNode .. Overload .. PlaceholderExpr .. Reference .. ArrayRangeRef .. ArrayRef .. AttrAddrExpr .. BitFieldRef .. ComponentRef .. ImagpartExpr .. IndirectRef .. MemRef .. MemberRef .. OffsetRef .. RealpartExpr .. ScopeRef .. TargetMemRef .. UnconstrainedArrayRef .. ViewConvertExpr .. SsaName .. Statement .. AsmExpr .. BreakStmt .. CaseLabelExpr .. CatchExpr .. CleanupStmt .. ContinueStmt .. DeclExpr .. DoStmt .. EhFilterExpr .. EhSpecBlock .. ExitExpr .. ExitStmt .. ForStmt .. GotoExpr .. Handler .. IfStmt .. LabelExpr .. LoopExpr .. LoopStmt .. OmpAtomic .. OmpCritical .. OmpFor .. OmpMaster .. OmpOrdered .. OmpParallel .. OmpSection .. OmpSections .. OmpSingle .. OmpTask .. RangeForStmt .. ReturnExpr .. StmtStmt .. SwitchExpr .. SwitchStmt .. TryBlock .. TryCatchExpr .. TryFinally .. UsingDirective .. WhileStmt .. StatementList .. StaticAssert .. TargetOptionNode .. TemplateInfo .. TemplateParmIndex .. TraitExpr .. TreeBinfo .. TreeList .. TreeVec .. Type .. ArrayType .. BooleanType .. BoundTemplateTemplateParm .. CategoryImplementationType .. CategoryInterfaceType .. ClassImplementationType .. ClassInterfaceType .. ComplexType .. DecltypeType .. EnumeralType .. FixedPointType .. FunctionType .. IntegerType .. LangType .. MethodType .. NullptrType .. OffsetType .. PointerType .. ProtocolInterfaceType .. QualUnionType .. RealType .. RecordType .. ReferenceType .. TemplateTemplateParm .. TemplateTypeParm .. TypeArgumentPack .. TypePackExpansion .. TypenameType .. TypeofType .. UnboundClassTemplate .. UnconstrainedArrayType .. UnionType .. VectorType .. VoidType .. Unary .. AbsExpr .. AddrSpaceConvertExpr .. BitNotExpr .. CastExpr .. ConjExpr .. ConstCastExpr .. ConvertExpr .. DynamicCastExpr .. FixTruncExpr .. FixedConvertExpr .. FloatExpr .. NegateExpr .. NoexceptExpr .. NonLvalueExpr .. NopExpr .. ParenExpr .. ReducMaxExpr .. ReducMinExpr .. ReducPlusExpr .. ReinterpretCastExpr .. StaticCastExpr .. UnaryPlusExpr .. VecUnpackFloatHiExpr .. VecUnpackFloatLoExpr .. VecUnpackHiExpr .. VecUnpackLoExpr .. VlExp .. AggrInitExpr .. CallExpr gcc-python-plugin-0.17/docs/versions.rst000066400000000000000000000060121342215241600203410ustar00rootroot00000000000000.. Copyright 2011, 2013 David Malcolm Copyright 2011, 2013 Red Hat, Inc. This 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 . Version handling ================ .. py:function:: gcc.get_gcc_version() Get the gcc.Version for this version of GCC .. py:function:: gcc.get_plugin_gcc_version() Get the gcc.Version that this plugin was compiled with Typically the above will be equal (the plugin-loading mechanism currently checks for this, and won't load the plugin otherwise). On my machine, running this currently gives:: gcc.Version(basever='4.6.0', datestamp='20110321', devphase='Red Hat 4.6.0-0.15', revision='', ...) .. py:class:: gcc.Version Information on the version of GCC being run. The various fields are accessible by name and by index. .. py:attribute:: basever (string) On my machine, this has value:: '4.6.0' .. py:attribute:: datestamp (string) On my machine, this has value:: '20110321' .. py:attribute:: devphase (string) On my machine, this has value:: 'Red Hat 4.6.0-0.15' .. py:attribute:: revision (string) On my machine, this is the empty string .. py:attribute:: configuration_arguments (string) On my machine, this has value:: '../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux' Internally, this is a wrapper around a `struct plugin_gcc_version` .. py:data:: gcc.GCC_VERSION (int) This corresponds to the value of GCC_VERSION within GCC's internal code: (MAJOR * 1000) + MINOR: =========== ======================== GCC version Value of gcc.GCC_VERSION =========== ======================== 4.6 4006 4.7 4007 4.8 4008 4.9 4009 =========== ======================== gcc-python-plugin-0.17/docs/working-with-c.rst000066400000000000000000000054311342215241600213460ustar00rootroot00000000000000.. Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . Working with C code =================== "Hello world" ------------- Here's a simple "hello world" C program: .. literalinclude:: ../tests/examples/hello-world/input.c :lines: 19-26 :language: c Here's a Python script that locates the function at one pass within the compile and prints various interesting things about it: .. literalinclude:: ../tests/examples/hello-world/script.py :lines: 19- :language: python We can run the script during the compile like this: .. code-block:: bash ./gcc-with-python script.py test.c Here's the expected output: .. literalinclude:: ../tests/examples/hello-world/stdout.txt Notice how the call to `printf` has already been optimized into a call to `__builtin_puts`. Spell-checking string constants within source code -------------------------------------------------- This example add a spell-checker pass to GCC: all string constants are run through the "enchant" spelling-checker: .. code-block:: bash $ ./gcc-with-python tests/examples/spelling-checker/script.py input.c The Python code for this is: .. literalinclude:: ../tests/examples/spelling-checker/script.py :lines: 18- :language: python Given this sample C source file: .. literalinclude:: ../tests/examples/spelling-checker/input.c :lines: 19-29 :language: c these warnings are emitted on stderr: .. literalinclude:: ../tests/examples/spelling-checker/stderr.txt Finding global variables ------------------------ This example adds a pass that warns about uses of global variables: .. code-block:: bash $ ./gcc-with-python \ tests/examples/find-global-state/script.py \ -c \ tests/examples/find-global-state/input.c The Python code for this is: .. literalinclude:: ../tests/examples/find-global-state/script.py :lines: 18- :language: python Given this sample C source file: .. literalinclude:: ../tests/examples/find-global-state/input.c :lines: 19- :language: c these warnings are emitted on stderr: .. literalinclude:: ../tests/examples/find-global-state/stderr.txt gcc-python-plugin-0.17/examples/000077500000000000000000000000001342215241600166265ustar00rootroot00000000000000gcc-python-plugin-0.17/examples/show-callgraph.py000066400000000000000000000034661342215241600221240ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Sample python script, to be run by our gcc plugin # Show the call graph (interprocedural analysis), using GraphViz import gcc from gccutils import callgraph_to_dot, invoke_dot # In theory we could have done this with a custom gcc.Pass registered # directly after "*build_cgraph_edges". However, we can only register # relative to passes of the same kind, and that pass is a # gcc.GimplePass, which is called per-function, and we want a one-time # pass instead. # # So we instead register a callback on the one-time pass that follows it def on_pass_execution(p, fn): if p.name == '*free_lang_data': # The '*free_lang_data' pass is called once, rather than per-function, # and occurs immediately after "*build_cgraph_edges", which is the # pass that initially builds the callgraph # # So at this point we're likely to get a good view of the callgraph # before further optimization passes manipulate it dot = callgraph_to_dot() invoke_dot(dot) gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION, on_pass_execution) gcc-python-plugin-0.17/examples/show-docs.py000066400000000000000000000013721342215241600211110ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . import gcc help(gcc) gcc-python-plugin-0.17/examples/show-gimple.py000066400000000000000000000026141342215241600214360ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Sample python script, to be run by our gcc plugin # Show the GIMPLE form of each function, using GraphViz import gcc from gccutils import get_src_for_loc, cfg_to_dot, invoke_dot # We'll implement this as a custom pass, to be called directly after the # builtin "cfg" pass, which generates the CFG: class ShowGimple(gcc.GimplePass): def execute(self, fun): # (the CFG should be set up by this point, and the GIMPLE is not yet # in SSA form) if fun and fun.cfg: dot = cfg_to_dot(fun.cfg, fun.decl.name) # print dot invoke_dot(dot, name=fun.decl.name) ps = ShowGimple(name='show-gimple') ps.register_after('cfg') gcc-python-plugin-0.17/examples/show-lto-supergraph.py000066400000000000000000000026611342215241600231370ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . # Sample python script, to be run by our gcc plugin # Show the "supergraph": the CFG of all functions, linked by # interproceduraledges: import gcc from gccutils.graph.supergraph import Supergraph from gccutils import invoke_dot # We'll implement this as a custom pass, to be called directly before # 'whole-program' class ShowSupergraph(gcc.IpaPass): def execute(self): # (the callgraph should be set up by this point) if gcc.is_lto(): sg = Supergraph(split_phi_nodes=False, add_fake_entry_node=False) dot = sg.to_dot('supergraph') invoke_dot(dot) ps = ShowSupergraph(name='show-supergraph') ps.register_before('whole-program') gcc-python-plugin-0.17/examples/show-passes.py000066400000000000000000000020141342215241600214510ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Sample python script, to be run by our gcc plugin # Show all the passes that get executed import gcc def my_pass_execution_callback(*args, **kwargs): (optpass, fun) = args print(args) gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION, my_pass_execution_callback) gcc-python-plugin-0.17/examples/show-ssa.py000066400000000000000000000026101342215241600207430ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Sample python script, to be run by our gcc plugin # Show the SSA form of each function, using GraphViz import gcc from gccutils import get_src_for_loc, cfg_to_dot, invoke_dot # A custom GCC pass, to be called directly after the builtin "ssa" pass, which # generates the Static Single Assignment form of the GIMPLE within the CFG: class ShowSsa(gcc.GimplePass): def execute(self, fun): # (the SSA form of each function should have just been set up) if fun and fun.cfg: dot = cfg_to_dot(fun.cfg, fun.decl.name) # print(dot) invoke_dot(dot, name=fun.decl.name) ps = ShowSsa(name='show-ssa') ps.register_after('ssa') gcc-python-plugin-0.17/gcc-c-api/000077500000000000000000000000001342215241600165335ustar00rootroot00000000000000gcc-python-plugin-0.17/gcc-c-api/Makefile000066400000000000000000000062211342215241600201740ustar00rootroot00000000000000# Copyright 2011, 2012, 2013 David Malcolm # Copyright 2011, 2012, 2013 Red Hat, Inc. # # This 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 # . ifneq ($(srcdir),) VPATH = $(srcdir) endif xmldir = $(srcdir)./ .PHONY: all clean check-api HANDWRITTEN_C_FILES = \ gcc-callgraph.c \ gcc-cfg.c \ gcc-constant.c \ gcc-declaration.c \ gcc-diagnostics.c \ gcc-function.c \ gcc-gimple.c \ gcc-location.c \ gcc-option.c \ gcc-rtl.c \ gcc-tree.c \ gcc-type.c \ gcc-variable.c GENERATED_C_FILES = \ autogenerated-casts.c OBJECT_FILES_FROM_HANDWRITTEN_C_FILES:= $(patsubst %.c,%.o,$(HANDWRITTEN_C_FILES)) OBJECT_FILES_FROM_GENERATED_C_FILES:= $(patsubst %.c,%.o,$(GENERATED_C_FILES)) OBJECT_FILES:= $(OBJECT_FILES_FROM_HANDWRITTEN_C_FILES) $(OBJECT_FILES_FROM_GENERATED_C_FILES) SOURCE_XML:= \ callgraph.xml \ cfg.xml \ constant.xml \ declaration.xml \ diagnostics.xml \ function.xml \ gimple.xml \ location.xml \ option.xml \ rtl.xml \ tree.xml \ type.xml \ variable.xml GENERATED_HEADERS:= \ gcc-public-types.h \ gcc-semiprivate-types.h \ gcc-callgraph.h \ gcc-cfg.h \ gcc-constant.h \ gcc-declaration.h \ gcc-function.h \ gcc-gimple.h \ gcc-location.h \ gcc-option.h \ gcc-rtl.h \ gcc-tree.h \ gcc-type.h \ gcc-variable.h GCCPLUGINS_DIR:= $(shell $(CC) --print-file-name=plugin) LIBGCC_C_API_SO := libgcc-c-api.so CPPFLAGS+= -I$(GCCPLUGINS_DIR)/include -I$(GCCPLUGINS_DIR)/include/c-family -I. # Allow user to pick optimization, choose whether warnings are fatal, # and choose debugging information level. CFLAGS?=-O2 -Werror -g # Force these settings CFLAGS+= -fPIC -fno-strict-aliasing -Wall all: $(LIBGCC_C_API_SO) $(LIBGCC_C_API_SO): $(OBJECT_FILES) $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared $^ -o $@ $(LIBS) # This is the standard .c->.o recipe, but it needs to be stated # explicitly to support the case that $(srcdir) is not blank. $(OBJECT_FILES): %.o: %.c $(GENERATED_HEADERS) ../autogenerated-EXTRA_CFLAGS.txt $(COMPILE.c) $(shell cat ../autogenerated-EXTRA_CFLAGS.txt) $(OUTPUT_OPTION) -I$(srcdir)./ $< autogenerated-casts.c: $(SOURCE_XML) xmltypes.py generate-casts-c.py $(PYTHON) $(srcdir)./generate-casts-c.py $@ $(xmldir) clean: $(RM) *.so *.o $(GENERATED_HEADERS) install: $(LIBGCC_C_API_SO) mkdir -p $(DESTDIR)$(GCCPLUGINS_DIR) cp libgcc-c-api.so $(DESTDIR)$(GCCPLUGINS_DIR) # The python interpreter to use: PYTHON=python $(GENERATED_HEADERS): $(SOURCE_XML) xml-to-h.py xmltypes.py $(PYTHON) $(srcdir)./xml-to-h.py $(xmldir) check-api: xmllint --noout --relaxng api.rng *.xml gcc-python-plugin-0.17/gcc-c-api/api.rng000066400000000000000000000070471342215241600200240ustar00rootroot00000000000000 rw r gcc-python-plugin-0.17/gcc-c-api/callgraph.xml000066400000000000000000000036131342215241600212150ustar00rootroot00000000000000 Callgraphs The function declaration for this node within the callgraph The function calls made by this function The places that calls this function An edge within the callgraph The function that makes this call The function that is called here The gimple call statement representing the function call All callgraph nodes, for the passes where these exist gcc-python-plugin-0.17/gcc-c-api/cfg.xml000066400000000000000000000037571342215241600200300ustar00rootroot00000000000000 Control Flow Graphs A control flow graph Entrypoint for this CFG The final block within this CFG gcc-python-plugin-0.17/gcc-c-api/constant.xml000066400000000000000000000025531342215241600211130ustar00rootroot00000000000000 Constant Expressions gcc-python-plugin-0.17/gcc-c-api/declaration.xml000066400000000000000000000042651342215241600215510ustar00rootroot00000000000000 Declarations gcc-python-plugin-0.17/gcc-c-api/design.rst000066400000000000000000000055731342215241600205500ustar00rootroot00000000000000Proposed GCC plugin C API ------------------------- This is an API for GCC plugins written in C. All public functions are declared with GCC_PUBLIC_API, any private helper functions are declared with GCC_PRIVATE_API Different plugins are likely to want different lifetime-management policies for the wrapper objects: some plugins will want to garbage-collect, others will want to reference-count, etc. Hence all types are "really" just pointers, but are hidden somewhat to emphasize that you must collaborate with the GCC garbage collector. Naming convention: a gcc_something is an interface to a GCC "something" e.g. gcc_gimple_phi is an interface to a GCC gimple phi node. All types also have a standard varname (e.g. "edge" for a gcc_cfg_edge). All functions have a prefix relating to what they act on, e.g.: GCC_PUBLIC_API (int) gcc_cfg_block_get_index (gcc_cfg_block block); All such interface types have a "mark_in_use" function, e.g.:: GCC_PUBLIC_API (void) gcc_cfg_block_mark_in_use (gcc_cfg_block block); If you're holding a pointer to one of these types, you *must* call this when GCC's garbage collector runs. Getters are named "TYPEPREFIX_get_ATTR" e.g. "gcc_cfg_block_get_index" Iterators follow a common pattern. Here's one that iterates over all basic blocks within a control flow graph:: GCC_PUBLIC_API (bool) gcc_cfg_for_each_block (gcc_cfg cfg, bool (*cb) (gcc_cfg_block block, void *user_data), void *user_data); The iteration terminates if the callback ever returns truth (allowing it also to be used for a linear search). The overall return value is truth if any callback returned truth (implying there was an early exit), or false if every callback returned falsehood (implying every element was visited). C-based class hierarchy ^^^^^^^^^^^^^^^^^^^^^^^ There is a "class hierarchy" to the types, but it is expressed in C. There are explicit casting functions for every pair of types. For example, this function allows you to upcast a gcc_ssa_name to its parent class gcc_tree:: GCC_PUBLIC_API (gcc_tree) gcc_ssa_name_as_gcc_tree (gcc_ssa_name node); and this function does the reverse, downcasting a gcc_tree to the more refined class gcc_ssa_name:: GCC_PUBLIC_API (gcc_ssa_name) gcc_tree_as_gcc_ssa_name (gcc_tree node); XML hooks ^^^^^^^^^ The API is described in XML form, expressing the class hierarchy, along with with the attributes that each class has. This makes it easy to programatically manipulate the API: every important platform can already handle XML. The header files for the API are generated from the XML description. (This was useful for generating all of the casting functions). There is a RELAX-NG schema for the XML format. (I also tried JSON, but XML ended up being clearer). TODO: how to arrange for your code to be called when the GC runs? TODO: getting errors? gcc-python-plugin-0.17/gcc-c-api/diagnostics.xml000066400000000000000000000027401342215241600215670ustar00rootroot00000000000000 Diagnostics gcc-python-plugin-0.17/gcc-c-api/function.xml000066400000000000000000000024071342215241600211050ustar00rootroot00000000000000 Functions The control flow graph for this function (can be NULL in early passes) gcc-python-plugin-0.17/gcc-c-api/gcc-callgraph.c000066400000000000000000000112421342215241600213660ustar00rootroot00000000000000/* Copyright 2012, 2013, 2015 David Malcolm Copyright 2012, 2013, 2015 Red Hat, Inc. This 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 . */ #include "gcc-callgraph.h" #include "tree.h" #include "cgraph.h" #include "ggc.h" #include "tree-ssa-alias.h" #include "basic-block.h" #if (GCC_VERSION >= 4009) #include "tree-ssa-alias.h" /* needed by gimple.h in 4.9 */ #include "internal-fn.h" /* needed by gimple.h in 4.9 */ #include "is-a.h" /* needed by gimple.h in 4.9 */ #include "predict.h" /* needed by gimple.h in 4.9 */ #include "gimple-expr.h" /* needed by gimple.h in 4.9 */ #endif #include "gimple.h" /*********************************************************** gcc_cgraph_node ************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_cgraph_node) gcc_private_make_cgraph_node (struct cgraph_node *inner) { struct gcc_cgraph_node result; result.inner = inner; return result; } GCC_PUBLIC_API (void) gcc_cgraph_node_mark_in_use (gcc_cgraph_node node) { /* As of gcc 4.9, a cgraph_node inherits from symtab node and uses that struct's marking routine. */ #if (GCC_VERSION >= 4009) gt_ggc_mx_symtab_node (node.inner); #else gt_ggc_mx_cgraph_node (node.inner); #endif } GCC_PUBLIC_API (gcc_function_decl) gcc_cgraph_node_get_decl (gcc_cgraph_node node) { /* gcc 4.8 eliminated the tree decl; field of cgraph_node in favor of struct symtab_node_base symbol; gcc 4.9 made cgraph_node inherit from symtab_node_base, renaming the latter to symtab_node. */ tree decl; #if (GCC_VERSION >= 4009) /* Access decl field of parent class, symtab_node */ decl = node.inner->decl; #else # if (GCC_VERSION >= 4008) decl = node.inner->symbol.decl; # else decl = node.inner->decl; # endif #endif return gcc_private_make_function_decl (decl); } GCC_PUBLIC_API (bool) gcc_cgraph_node_for_each_callee (gcc_cgraph_node node, bool (*cb) (gcc_cgraph_edge edge, void *user_data), void *user_data) { struct cgraph_edge *edge; for (edge = node.inner->callees; edge; edge = edge->next_callee) { if (cb (gcc_private_make_cgraph_edge (edge), user_data)) { return true; } } return false; } GCC_PUBLIC_API (bool) gcc_cgraph_node_for_each_caller (gcc_cgraph_node node, bool (*cb) (gcc_cgraph_edge edge, void *user_data), void *user_data) { struct cgraph_edge *edge; for (edge = node.inner->callers; edge; edge = edge->next_caller) { if (cb (gcc_private_make_cgraph_edge (edge), user_data)) { return true; } } return false; } /*********************************************************** gcc_cgraph_edge ************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_cgraph_edge) gcc_private_make_cgraph_edge (struct cgraph_edge *inner) { struct gcc_cgraph_edge result; result.inner = inner; return result; } GCC_PUBLIC_API (void) gcc_cgraph_edge_mark_in_use (gcc_cgraph_edge edge) { gt_ggc_mx_cgraph_edge (edge.inner); } GCC_PUBLIC_API (gcc_cgraph_node) gcc_cgraph_edge_get_caller (gcc_cgraph_edge edge) { return gcc_private_make_cgraph_node (edge.inner->caller); } GCC_PUBLIC_API (gcc_cgraph_node) gcc_cgraph_edge_get_callee (gcc_cgraph_edge edge) { return gcc_private_make_cgraph_node (edge.inner->callee); } GCC_PUBLIC_API (gcc_gimple_call) gcc_cgraph_edge_get_call_stmt (gcc_cgraph_edge edge) { return gcc_private_make_gimple_call (edge.inner->call_stmt); } GCC_PUBLIC_API (bool) gcc_for_each_cgraph_node (bool (*cb) (gcc_cgraph_node node, void *user_data), void *user_data) { struct cgraph_node *node; /* gcc 4.7 introduced FOR_EACH_DEFINED_FUNCTION gcc 4.8 eliminated: extern GTY(()) struct cgraph_node *cgraph_nodes; FIXME: does this only visit *defined* functions then? */ #if (GCC_VERSION >= 4008) FOR_EACH_DEFINED_FUNCTION(node) #else for (node = cgraph_nodes; node; node = node->next) #endif { if (cb (gcc_private_make_cgraph_node (node), user_data)) { return true; } } return false; } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-cfg.c000066400000000000000000000171201342215241600201710ustar00rootroot00000000000000/* Copyright 2012, 2013, 2015 David Malcolm Copyright 2012, 2013, 2015 Red Hat, Inc. This 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 . */ #include "gcc-cfg.h" #include "tree.h" #include "function.h" #include "basic-block.h" #if (GCC_VERSION >= 4009) #include "tree-ssa-alias.h" /* needed by gimple.h in 4.9 */ #include "internal-fn.h" /* needed by gimple.h in 4.9 */ #include "is-a.h" /* needed by gimple.h in 4.9 */ #include "predict.h" /* needed by gimple.h in 4.9 */ #include "gimple-expr.h" /* needed by gimple.h in 4.9 */ #endif #include "gimple.h" /* gcc 4.9 moved gimple_stmt_iterator into this header */ #if (GCC_VERSION >= 4009) #include "gimple-iterator.h" #endif #include "params.h" #include "tree.h" #include "diagnostic.h" #include "cgraph.h" #include "opts.h" #include "rtl.h" #include "gcc-private-compat.h" /*********************************************************** gcc_cfg ************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_cfg) gcc_private_make_cfg (struct control_flow_graph *inner) { struct gcc_cfg result; result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (void) gcc_cfg_mark_in_use (gcc_cfg cfg) { gt_ggc_mx_control_flow_graph (cfg.inner); } GCC_IMPLEMENT_PUBLIC_API (gcc_cfg_block) gcc_cfg_get_entry (gcc_cfg cfg) { return gcc_private_make_cfg_block (cfg.inner->x_entry_block_ptr); } GCC_IMPLEMENT_PUBLIC_API (gcc_cfg_block) gcc_cfg_get_exit (gcc_cfg cfg) { return gcc_private_make_cfg_block (cfg.inner->x_exit_block_ptr); } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_cfg_for_each_block (gcc_cfg cfg, bool (*cb) (gcc_cfg_block block, void *user_data), void *user_data) { int i; for (i = 0; i < cfg.inner->x_n_basic_blocks; i++) { basic_block bb = GCC_COMPAT_VEC_INDEX (basic_block, cfg.inner->x_basic_block_info, i); if (cb (gcc_private_make_cfg_block (bb), user_data)) { return true; } } return false; } /*********************************************************** gcc_cfg_block ************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_cfg_block) gcc_private_make_cfg_block (basic_block inner) { struct gcc_cfg_block result; result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (void) gcc_cfg_block_mark_in_use (gcc_cfg_block block) { gt_ggc_mx_basic_block_def (block.inner); } GCC_IMPLEMENT_PUBLIC_API (int) gcc_cfg_block_get_index (gcc_cfg_block block) { return block.inner->index; } static bool for_each_edge ( #if (GCC_VERSION >= 4008) vec *vec_edges, #else VEC (edge, gc) * vec_edges, #endif bool (*cb) (gcc_cfg_edge edge, void *user_data), void *user_data) { int i; edge e; GCC_COMPAT_FOR_EACH_VEC_ELT (edge, vec_edges, i, e) { if (cb (gcc_private_make_cfg_edge (e), user_data)) { return true; } } return false; } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_cfg_block_for_each_pred_edge (gcc_cfg_block block, bool (*cb) (gcc_cfg_edge edge, void *user_data), void *user_data) { return for_each_edge (block.inner->preds, cb, user_data); } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_cfg_block_for_each_succ_edge (gcc_cfg_block block, bool (*cb) (gcc_cfg_edge edge, void *user_data), void *user_data) { return for_each_edge (block.inner->succs, cb, user_data); } /* In GCC 4.7, struct basic_block_def had a struct gimple_bb_info * gimple; within its il union. In GCC 4.8, this became: struct gimple_bb_info gimple i.e. it is no longer dereferenced */ static struct gimple_bb_info * checked_get_gimple_info(gcc_cfg_block block) { if (block.inner->flags & BB_RTL) { return NULL; } #if (GCC_VERSION >= 4008) return &block.inner->il.gimple; #else return block.inner->il.gimple; #endif } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_cfg_block_for_each_gimple_phi (gcc_cfg_block block, bool (*cb) (gcc_gimple_phi phi, void *user_data), void *user_data) { gimple_stmt_iterator gsi; struct gimple_bb_info *info; info = checked_get_gimple_info(block); if (NULL == info) { return false; } for (gsi = gsi_start (info->phi_nodes); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple_stmt_ptr stmt = gsi_stmt (gsi); if (cb (gcc_private_make_gimple_phi (stmt), user_data)) { return true; } } return false; } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_cfg_block_for_each_gimple (gcc_cfg_block block, bool (*cb) (gcc_gimple stmt, void *user_data), void *user_data) { gimple_stmt_iterator gsi; struct gimple_bb_info *info; info = checked_get_gimple_info(block); if (NULL == info) { return false; } for (gsi = gsi_start (info->seq); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple_stmt_ptr stmt = gsi_stmt (gsi); if (cb (gcc_private_make_gimple (stmt), user_data)) { return true; } } return false; } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_cfg_block_for_each_rtl_insn (gcc_cfg_block block, bool (*cb) (gcc_rtl_insn insn, void *user_data), void *user_data) { #if (GCC_VERSION >= 5000) rtx_insn *insn; #else rtx insn; #endif if (!(block.inner->flags & BB_RTL)) { return false; } FOR_BB_INSNS (block.inner, insn) { if (cb (gcc_private_make_rtl_insn (insn), user_data)) { return true; } } return false; } /*********************************************************** gcc_cfg_edge ************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_cfg_edge) gcc_private_make_cfg_edge (edge inner) { struct gcc_cfg_edge result; result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (void) gcc_cfg_edge_mark_in_use (gcc_cfg_edge edge) { gt_ggc_mx_edge_def (edge.inner); } GCC_IMPLEMENT_PUBLIC_API (gcc_cfg_block) gcc_cfg_edge_get_src (gcc_cfg_edge edge) { return gcc_private_make_cfg_block (edge.inner->src); } GCC_IMPLEMENT_PUBLIC_API (gcc_cfg_block) gcc_cfg_edge_get_dest (gcc_cfg_edge edge) { return gcc_private_make_cfg_block (edge.inner->dest); } GCC_PUBLIC_API (bool) gcc_cfg_edge_is_true_value (gcc_cfg_edge edge) { return (edge.inner->flags & EDGE_TRUE_VALUE) == EDGE_TRUE_VALUE; } GCC_PUBLIC_API (bool) gcc_cfg_edge_is_false_value (gcc_cfg_edge edge) { return (edge.inner->flags & EDGE_FALSE_VALUE) == EDGE_FALSE_VALUE; } GCC_PUBLIC_API(bool) gcc_cfg_edge_is_loop_exit(gcc_cfg_edge edge) { return (edge.inner->flags & EDGE_LOOP_EXIT) == EDGE_LOOP_EXIT; } GCC_PUBLIC_API(bool) gcc_cfg_edge_get_can_fallthru(gcc_cfg_edge edge) { return (edge.inner->flags & EDGE_CAN_FALLTHRU) == EDGE_CAN_FALLTHRU; } GCC_PUBLIC_API(bool) gcc_cfg_edge_is_complex(gcc_cfg_edge edge) { return (edge.inner->flags & EDGE_COMPLEX); } GCC_PUBLIC_API(bool) gcc_cfg_edge_is_eh(gcc_cfg_edge edge) { return (edge.inner->flags & EDGE_EH) == EDGE_EH; } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-common.h000066400000000000000000000033601342215241600207300ustar00rootroot00000000000000/* Copyright 2012, 2015 David Malcolm Copyright 2012, 2015 Red Hat, Inc. This 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 . */ #include /* see design.rst for notes */ /* Compatibility macros */ /* For declarations: */ #define GCC_PUBLIC_API(RETURN_TYPE) extern RETURN_TYPE #define GCC_PRIVATE_API(RETURN_TYPE) extern RETURN_TYPE #if (GCC_VERSION >= 6000) typedef gimple *gimple_stmt_ptr; #else typedef gimple gimple_stmt_ptr; #endif #include "gcc-public-types.h" /* For internal use: */ #define GCC_IMPLEMENT_PUBLIC_API(RETURN_TYPE) RETURN_TYPE #define GCC_IMPLEMENT_PRIVATE_API(RETURN_TYPE) RETURN_TYPE #if (GCC_VERSION >= 5000) #define AS_A_GASM(STMT) (as_a (STMT)) #define AS_A_GCOND(STMT) (as_a (STMT)) #define AS_A_GLABEL(STMT) (as_a (STMT)) #define AS_A_GPHI(STMT) (as_a (STMT)) #define AS_A_GSWITCH(STMT) (as_a (STMT)) #define AS_A_GRETURN(STMT) (as_a (STMT)) #else #define AS_A_GASM(STMT) (STMT) #define AS_A_GCOND(STMT) (STMT) #define AS_A_GLABEL(STMT) (STMT) #define AS_A_GPHI(STMT) (STMT) #define AS_A_GSWITCH(STMT) (STMT) #define AS_A_GRETURN(STMT) (STMT) #endif gcc-python-plugin-0.17/gcc-c-api/gcc-constant.c000066400000000000000000000023561342215241600212700ustar00rootroot00000000000000/* Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . */ #include "gcc-common.h" #include "gcc-constant.h" #include "gcc-internal.h" #include "tree.h" /* gcc_constant */ /* gcc_integer_constant */ /*************************************************************************** gcc_string_constant **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(const char*) gcc_string_constant_get_char_ptr(gcc_string_constant node) { return TREE_STRING_POINTER (node.inner); } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-declaration.c000066400000000000000000000200221342215241600217120ustar00rootroot00000000000000/* Copyright 2012, 2013 David Malcolm Copyright 2012, 2013 Red Hat, Inc. This 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 . */ #include "gcc-common.h" #include "gcc-semiprivate-types.h" #include "gcc-declaration.h" #include "tree.h" #include "gcc-internal.h" #include "gcc-tree.h" #include "gcc-private-compat.h" /*************************************************************************** gcc_decl **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_decl_get_location (gcc_decl decl) { return gcc_private_make_location (DECL_SOURCE_LOCATION (decl.inner)); } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_decl_is_artificial (gcc_decl decl) { return DECL_ARTIFICIAL (decl.inner); } GCC_IMPLEMENT_PUBLIC_API(bool) gcc_decl_is_builtin(gcc_decl decl) { return DECL_IS_BUILTIN (decl.inner); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_decl_as_gcc_tree (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_class_method_decl) gcc_decl_as_gcc_class_method_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_const_decl) gcc_decl_as_gcc_const_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_debug_expr_decl) gcc_decl_as_gcc_debug_expr_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_field_decl) gcc_decl_as_gcc_field_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_function_decl) gcc_decl_as_gcc_function_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_imported_decl) gcc_decl_as_gcc_imported_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_instance_method_decl) gcc_decl_as_gcc_instance_method_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_keyword_decl) gcc_decl_as_gcc_keyword_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_label_decl) gcc_decl_as_gcc_label_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_namespace_decl) gcc_decl_as_gcc_namespace_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_parm_decl) gcc_decl_as_gcc_parm_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_property_decl) gcc_decl_as_gcc_property_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_result_decl) gcc_decl_as_gcc_result_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_template_decl) gcc_decl_as_gcc_template_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_translation_unit_decl) gcc_decl_as_gcc_translation_unit_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_type_decl) gcc_decl_as_gcc_type_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_using_decl) gcc_decl_as_gcc_using_decl (gcc_decl node); GCC_IMPLEMENT_PUBLIC_API (gcc_var_decl) gcc_decl_as_gcc_var_decl (gcc_decl node); GCC_IMPLEMENT_PRIVATE_API (struct gcc_function_decl) gcc_private_make_function_decl (tree inner) { struct gcc_function_decl result; result.inner = FUNCTION_DECL_CHECK (inner); return result; } GCC_IMPLEMENT_PRIVATE_API (struct gcc_translation_unit_decl) gcc_private_make_translation_unit_decl (tree inner) { struct gcc_translation_unit_decl result; result.inner = TRANSLATION_UNIT_DECL_CHECK (inner); return result; } /*************************************************************************** gcc_class_method_decl **************************************************************************/ /*************************************************************************** gcc_const_decl **************************************************************************/ /*************************************************************************** gcc_debug_expr_decl **************************************************************************/ /*************************************************************************** gcc_field_decl **************************************************************************/ /*************************************************************************** gcc_function_decl **************************************************************************/ /*************************************************************************** gcc_imported_decl **************************************************************************/ /*************************************************************************** gcc_instance_method_decl **************************************************************************/ /*************************************************************************** gcc_keyword_decl **************************************************************************/ /*************************************************************************** gcc_label_decl **************************************************************************/ /*************************************************************************** gcc_namespace_decl **************************************************************************/ /*************************************************************************** gcc_parm_decl **************************************************************************/ /*************************************************************************** gcc_property_decl **************************************************************************/ /*************************************************************************** gcc_result_decl **************************************************************************/ /*************************************************************************** gcc_template_decl **************************************************************************/ /*************************************************************************** gcc_translation_unit_decl **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_block) gcc_translation_unit_decl_get_block (gcc_translation_unit_decl node) { return gcc_private_make_block (DECL_INITIAL (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (const char *) gcc_translation_unit_decl_get_language (gcc_translation_unit_decl node) { return TRANSLATION_UNIT_LANGUAGE (node.inner); } /*************************************************************************** gcc_type_decl **************************************************************************/ /*************************************************************************** gcc_using_decl **************************************************************************/ /*************************************************************************** gcc_var_decl **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_constructor) gcc_var_decl_get_initial(gcc_var_decl node) { return gcc_tree_as_gcc_constructor (gcc_private_make_tree (DECL_INITIAL (node.inner))); } GCC_IMPLEMENT_PUBLIC_API(bool) gcc_var_decl_is_static(gcc_var_decl node) { return TREE_STATIC(node.inner); } /*************************************************************************** Other things: **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (bool) gcc_for_each_translation_unit_decl (bool (*cb) (gcc_translation_unit_decl node, void *user_data), void *user_data) { int i; tree t; /* all_translation_units was made globally visible in gcc revision 164331: http://gcc.gnu.org/ml/gcc-cvs/2010-09/msg00625.html http://gcc.gnu.org/viewcvs?view=revision&revision=164331 */ GCC_COMPAT_FOR_EACH_VEC_ELT (tree, all_translation_units, i, t) { if (cb (gcc_private_make_translation_unit_decl (t), user_data)) { return true; } } return false; } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-diagnostics.c000066400000000000000000000026561342215241600217510ustar00rootroot00000000000000/* Copyright 2013 David Malcolm Copyright 2013 Red Hat, Inc. This 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 . */ #include "gcc-diagnostics.h" #include "diagnostic.h" GCC_IMPLEMENT_PUBLIC_API(void) gcc_inform(gcc_location location, const char* message) { return inform(location.inner, "%s", message); } GCC_IMPLEMENT_PUBLIC_API(void) gcc_error_at(gcc_location location, const char* message) { return error_at(location.inner, "%s", message); } #if 0 GCC_IMPLEMENT_PUBLIC_API(bool) gcc_warning_at(gcc_location location, const char* message, gcc_option option) { /* TODO (what about the no-option case?) */ } #endif GCC_IMPLEMENT_PUBLIC_API(bool) gcc_permerror(gcc_location location, const char* message) { return permerror(location.inner, "%s", message); } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-function.c000066400000000000000000000043231342215241600212600ustar00rootroot00000000000000/* Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . */ #include "gcc-common.h" #include "gcc-function.h" /* TODO: rationalize these headers */ #include "tree.h" #include "params.h" #include "tree.h" #include "function.h" #include "diagnostic.h" #include "cgraph.h" #include "opts.h" #include "basic-block.h" #include "rtl.h" #include "ggc.h" /* Declarations: functions */ /* gcc_function */ GCC_IMPLEMENT_PUBLIC_API (void) gcc_function_mark_in_use (gcc_function func) { gt_ggc_mx_function (func.inner); } GCC_IMPLEMENT_PRIVATE_API (struct gcc_function) gcc_private_make_function (struct function *inner) { struct gcc_function result; result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (gcc_cfg) gcc_function_get_cfg (gcc_function func) { return gcc_private_make_cfg (func.inner->cfg); } GCC_IMPLEMENT_PUBLIC_API (gcc_function_decl) gcc_function_get_decl (gcc_function func) { return gcc_private_make_function_decl (func.inner->decl); } GCC_IMPLEMENT_PUBLIC_API (int) gcc_function_get_index (gcc_function func) { return func.inner->funcdef_no; } GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_function_get_start (gcc_function func) { return gcc_private_make_location (func.inner->function_start_locus); } GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_function_get_end (gcc_function func) { return gcc_private_make_location (func.inner->function_end_locus); } GCC_IMPLEMENT_PUBLIC_API (gcc_function) gcc_get_current_function (void) { return gcc_private_make_function (cfun); } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-gimple.c000066400000000000000000000210371342215241600207110ustar00rootroot00000000000000/* Copyright 2012, 2013, 2015 David Malcolm Copyright 2012, 2013, 2015 Red Hat, Inc. This 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 . */ #include "gcc-gimple.h" #include "gcc-tree.h" #include "gcc-internal.h" #include "tree.h" #include "function.h" #include "basic-block.h" #if (GCC_VERSION >= 4009) //#include "alias.h" /* needed by tree-ssa-alias.h in 4.9 */ #include "tree-ssa-alias.h" /* needed by gimple.h in 4.9 */ #include "internal-fn.h" /* needed by gimple.h in 4.9 */ #include "is-a.h" /* needed by gimple.h in 4.9 */ #include "predict.h" /* needed by gimple.h in 4.9 */ #include "gimple-expr.h" /* needed by gimple.h in 4.9 */ #endif #include "gimple.h" #if 0 #include "params.h" #include "cp/name-lookup.h" /* for global_namespace */ #include "tree.h" #include "diagnostic.h" #include "cgraph.h" #include "opts.h" #include "c-family/c-pragma.h" /* for parse_in */ #include "rtl.h" #endif #include GCC_IMPLEMENT_PUBLIC_API (void) gcc_gimple_mark_in_use (gcc_gimple stmt) { /* Mark the underlying object (recursing into its fields): */ /* GCC 4.9 converted gimple to a class hierarchy */ #if (GCC_VERSION >= 4009) /* GCC 6 converted the base class from "gimple_statement_base" to "gimple". */ # if (GCC_VERSION >= 6000) gt_ggc_mx_gimple (stmt.inner); # else gt_ggc_mx_gimple_statement_base (stmt.inner); # endif #else /* #if (GCC_VERSION >= 4009) */ gt_ggc_mx_gimple_statement_d (stmt.inner); #endif /* #if (GCC_VERSION >= 4009) */ } GCC_IMPLEMENT_PRIVATE_API (struct gcc_gimple_phi) gcc_private_make_gimple_phi (gimple_stmt_ptr inner) { struct gcc_gimple_phi result; GIMPLE_CHECK (inner, GIMPLE_PHI); result.inner = inner; return result; } GCC_IMPLEMENT_PRIVATE_API (struct gcc_gimple_call) gcc_private_make_gimple_call (gimple_stmt_ptr inner) { struct gcc_gimple_call result; GIMPLE_CHECK (inner, GIMPLE_CALL); result.inner = inner; return result; } GCC_IMPLEMENT_PRIVATE_API (struct gcc_gimple) gcc_private_make_gimple (gimple_stmt_ptr inner) { struct gcc_gimple result; result.inner = inner; return result; } /*************************************************************************** gcc_gimple **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_gimple_get_location (gcc_gimple stmt) { return gcc_private_make_location (gimple_location (stmt.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_get_block (gcc_gimple stmt) { return gcc_private_make_tree (gimple_block (stmt.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_get_expr_type (gcc_gimple stmt) { return gcc_private_make_tree (gimple_expr_type (stmt.inner)); } /*************************************************************************** gcc_gimple_asm **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (const char *) gcc_gimple_asm_get_string (gcc_gimple_asm stmt) { return gimple_asm_string (AS_A_GASM (stmt.inner)); } /*************************************************************************** gcc_gimple_assign **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_assign_get_lhs (gcc_gimple_assign stmt) { return gcc_private_make_tree (gimple_assign_lhs (stmt.inner)); } /*************************************************************************** gcc_gimple_call **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_call_get_lhs (gcc_gimple_call stmt) { return gcc_private_make_tree (gimple_call_lhs (stmt.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_call_get_fn (gcc_gimple_call stmt) { return gcc_private_make_tree (gimple_call_fn (stmt.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_call_get_fndecl (gcc_gimple_call stmt) { return gcc_private_make_tree (gimple_call_fndecl (stmt.inner)); } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_gimple_call_is_noreturn (gcc_gimple_call stmt) { return gimple_call_noreturn_p (stmt.inner); } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_gimple_call_for_each_arg (gcc_gimple_call stmt, bool (*cb) (gcc_tree node, void *user_data), void *user_data) { int num_args = gimple_call_num_args (stmt.inner); int i; for (i = 0; i < num_args; i++) { if (cb (gcc_private_make_tree (gimple_call_arg (stmt.inner, i)), user_data)) { return true; } } return false; } /*************************************************************************** gcc_gimple_return **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_return_get_retval (gcc_gimple_return stmt) { return gcc_private_make_tree (gimple_return_retval (AS_A_GRETURN (stmt.inner))); } /*************************************************************************** gcc_gimple_cond **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_cond_get_lhs (gcc_gimple_cond stmt) { return gcc_private_make_tree (gimple_cond_lhs (stmt.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_cond_get_rhs (gcc_gimple_cond stmt) { return gcc_private_make_tree (gimple_cond_rhs (stmt.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_cond_get_true_label (gcc_gimple_cond stmt) { return gcc_private_make_tree (gimple_cond_true_label (AS_A_GCOND (stmt.inner))); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_cond_get_false_label (gcc_gimple_cond stmt) { return gcc_private_make_tree (gimple_cond_false_label (AS_A_GCOND (stmt.inner))); } /*************************************************************************** gcc_gimple_phi **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_phi_get_lhs (gcc_gimple_phi phi) { return gcc_private_make_tree (gimple_phi_result (phi.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_phi_get_result (gcc_gimple_phi phi) { return gcc_private_make_tree (gimple_phi_result (phi.inner)); } /* Iterator; terminates if the callback returns truth (for linear search) */ GCC_IMPLEMENT_PUBLIC_API (bool) gcc_gimple_phi_for_each_exprs (gcc_gimple_phi phi, bool (*cb) (gcc_tree node, void *user_data), void *user_data); /* Iterator; terminates if the callback returns truth (for linear search) */ GCC_IMPLEMENT_PUBLIC_API (bool) gcc_gimple_phi_for_each_edges (gcc_gimple_phi phi, bool (*cb) (gcc_cfg_edge edge, void *user_data), void *user_data); /*************************************************************************** gcc_gimple_switch **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_gimple_switch_get_indexvar (gcc_gimple_switch stmt) { return gcc_private_make_tree (gimple_switch_index (AS_A_GSWITCH (stmt.inner))); } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_gimple_switch_for_each_label (gcc_gimple_switch stmt, bool (*cb) (gcc_case_label_expr node, void *user_data), void *user_data) { unsigned num_labels = gimple_switch_num_labels (AS_A_GSWITCH (stmt.inner)); unsigned i; for (i = 0; i < num_labels; i++) { if (cb (gcc_private_make_case_label_expr (gimple_switch_label (AS_A_GSWITCH (stmt.inner), i)), user_data)) { return true; } } return false; } /*************************************************************************** gcc_gimple_label **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_label_decl) gcc_gimple_label_get_label(gcc_gimple_label stmt) { return gcc_tree_as_gcc_label_decl (gcc_private_make_tree (gimple_label_label (AS_A_GLABEL (stmt.inner)))); } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-internal.h000066400000000000000000000020571342215241600212560ustar00rootroot00000000000000/* Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . */ #define IMPLEMENT_CAST(T_INPUT, T_OUTPUT) \ GCC_IMPLEMENT_PUBLIC_API(T_OUTPUT) \ T_INPUT ## _as_ ## T_OUTPUT(T_INPUT input) \ { \ T_OUTPUT output; \ output.inner = input.inner; \ return output; \ } gcc-python-plugin-0.17/gcc-c-api/gcc-location.c000066400000000000000000000064221342215241600212450ustar00rootroot00000000000000/* Copyright 2012, 2017 David Malcolm Copyright 2012, 2017 Red Hat, Inc. This 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 . */ #include "gcc-location.h" /*********************************************************** gcc_location ************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_location) gcc_private_make_location (location_t inner) { struct gcc_location result; result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (void) gcc_location_mark_in_use (gcc_location loc) { /* empty */ } GCC_IMPLEMENT_PUBLIC_API (const char *) gcc_location_get_filename (gcc_location loc) { return LOCATION_FILE (loc.inner); } GCC_IMPLEMENT_PUBLIC_API (int) gcc_location_get_line (gcc_location loc) { return LOCATION_LINE (loc.inner); } GCC_IMPLEMENT_PUBLIC_API (int) gcc_location_get_column (gcc_location loc) { expanded_location exploc = expand_location (loc.inner); return exploc.column; } GCC_PUBLIC_API (bool) gcc_location_is_unknown (gcc_location loc) { return UNKNOWN_LOCATION == loc.inner; } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_location_get_in_system_header (gcc_location loc) { return in_system_header_at (loc.inner); } /* get_pure_location and get_finish were added to gcc's input.h in gcc's r238792 (aka f17776ffcba650feb512137e3e22a04f3f433c84). get_start was added to gcc's input.h in gcc's r239831 (aka aca2a315073c72fb7c9ab1be779c290cc91f564c). I believe that makes them available in gcc 7 onwards. */ #if (GCC_VERSION >= 7000) GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_location_get_caret (gcc_location loc) { return gcc_private_make_location (get_pure_location (loc.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_location_get_start (gcc_location loc) { return gcc_private_make_location (get_start (loc.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_location_get_finish (gcc_location loc) { return gcc_private_make_location (get_finish (loc.inner)); } #endif /* linemap_position_for_loc_and_offset was added in gcc's r217383 (aka 766928aa6ac2c846c2d098ef4ef9e220feb4dcab). It's present in gcc 5.1. */ #if (GCC_VERSION >= 5000) GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_location_offset_column (gcc_location loc, int offset) { return gcc_private_make_location (linemap_position_for_loc_and_offset (line_table, loc.inner, offset)); } #endif GCC_IMPLEMENT_PUBLIC_API (void) gcc_set_input_location (gcc_location loc) { input_location = loc.inner; } GCC_IMPLEMENT_PUBLIC_API (gcc_location) gcc_get_input_location (void) { return gcc_private_make_location (input_location); } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-option.c000066400000000000000000000031441342215241600207430ustar00rootroot00000000000000/* Copyright 2012, 2013 David Malcolm Copyright 2012, 2013 Red Hat, Inc. This 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 . */ #include "gcc-option.h" #include "opts.h" /* Command-line options */ /* gcc_option */ GCC_IMPLEMENT_PRIVATE_API (struct gcc_option) gcc_private_make_option (enum opt_code inner) { struct gcc_option result; result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (void) gcc_option_mark_in_use (gcc_option opt) { /* empty */ } GCC_IMPLEMENT_PUBLIC_API (const char*) gcc_option_get_text (gcc_option opt) { return cl_options[opt.inner].opt_text; } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_for_each_option (bool (*cb)(gcc_option opt, void *user_data), void *user_data) { unsigned int i; for (i = 0; i < cl_options_count; i++) { gcc_option opt = gcc_private_make_option ((enum opt_code)i); if (cb (opt, user_data) ) { return true; } } return false; } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-private-compat.h000066400000000000000000000036121342215241600223730ustar00rootroot00000000000000/* Copyright 2013 David Malcolm Copyright 2013 Red Hat, Inc. This 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 . */ /* Private header for the API, for handling compatibility between GCC versions */ /************************************************************************* Vectors *************************************************************************/ /* Getting the length of a vector, returning 0 if it is NULL: */ #if (GCC_VERSION >= 4008) #define GCC_COMPAT_VEC_LENGTH(KIND, V) \ ( (V) ? ( (V)->length() ) : 0 ) #else #define GCC_COMPAT_VEC_LENGTH(KIND, V) \ ( VEC_length(KIND, (V)) ) #endif /* Looking up an element by index: */ #if (GCC_VERSION >= 4008) #define GCC_COMPAT_VEC_INDEX(KIND, V, IDX) \ ( (*(V))[IDX] ) #else #define GCC_COMPAT_VEC_INDEX(KIND, V, IDX) \ ( VEC_index(KIND, (V), (IDX) ) ) #endif /* Iterating over every element in a vector, or not at all if it is NULL: */ #if (GCC_VERSION >= 4008) #define GCC_COMPAT_FOR_EACH_VEC_ELT(KIND, V, IDX_VAR, ITEM_VAR) \ if ( (V) != NULL ) \ FOR_EACH_VEC_ELT ( (*V), (IDX_VAR), (ITEM_VAR) ) #else #define GCC_COMPAT_FOR_EACH_VEC_ELT(KIND, V, IDX_VAR, ITEM_VAR) \ FOR_EACH_VEC_ELT(KIND, (V), (IDX_VAR), (ITEM_VAR) ) #endif /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-rtl.c000066400000000000000000000022641342215241600202360ustar00rootroot00000000000000/* Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . */ #include "gcc-rtl.h" #include #include "tm.h" #include "tree.h" #include "rtl.h" #include "ggc.h" GCC_IMPLEMENT_PUBLIC_API (void) gcc_rtl_insn_mark_in_use (gcc_rtl_insn insn) { gt_ggc_mx_rtx_def (insn.inner); } GCC_IMPLEMENT_PRIVATE_API (struct gcc_rtl_insn) gcc_private_make_rtl_insn (struct rtx_def *inner) { struct gcc_rtl_insn result; result.inner = inner; return result; } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-tree.c000066400000000000000000000170251342215241600203750ustar00rootroot00000000000000/* Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . */ #include "gcc-common.h" #include "gcc-tree.h" #include "ggc.h" #include "gcc-internal.h" #include #include "tree.h" /* Trees */ /* gcc_tree */ GCC_IMPLEMENT_PUBLIC_API (void) gcc_tree_mark_in_use (gcc_tree node) { /* Mark the underlying object (recursing into its fields): */ gt_ggc_mx_tree_node (node.inner); } GCC_IMPLEMENT_PRIVATE_API (struct gcc_tree) gcc_private_make_tree (tree inner) { struct gcc_tree result; /* FIXME: type-checking */ result.inner = inner; return result; } GCC_IMPLEMENT_PRIVATE_API (struct gcc_block) gcc_private_make_block (tree inner) { struct gcc_block result; result.inner = BLOCK_CHECK (inner); return result; } /* gcc_binary */ GCC_IMPLEMENT_PUBLIC_API (void) gcc_binary_mark_in_use (gcc_binary node); GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_binary_get_location(gcc_binary node) { return gcc_private_make_location (EXPR_LOCATION (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_bitwise_and_expr) gcc_binary_as_gcc_bitwise_and_expr (gcc_binary node); GCC_IMPLEMENT_PUBLIC_API (gcc_bitwise_ior_expr) gcc_binary_as_gcc_bitwise_ior_expr (gcc_binary node); GCC_IMPLEMENT_PUBLIC_API (gcc_bitwise_xor_expr) gcc_binary_as_gcc_bitwise_xor_expr (gcc_binary node); /* gcc_bitwise_and_expr */ GCC_IMPLEMENT_PUBLIC_API (void) gcc_bitwise_and_expr_mark_in_use (gcc_bitwise_and_expr node); /* gcc_bitwise_ior_expr */ GCC_IMPLEMENT_PUBLIC_API (void) gcc_bitwise_ior_expr_mark_in_use (gcc_bitwise_ior_expr node); /* gcc_bitwise_xor_expr */ GCC_IMPLEMENT_PUBLIC_API (void) gcc_bitwise_xor_expr_mark_in_use (gcc_bitwise_xor_expr node); /* gcc_block */ GCC_IMPLEMENT_PUBLIC_API (void) gcc_block_mark_in_use (gcc_block node); /*************************************************************************** gcc_comparison **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_comparison_get_location(gcc_comparison node) { return gcc_private_make_location (EXPR_LOCATION (node.inner)); } /*************************************************************************** gcc_expression **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_expression_get_location(gcc_expression node) { return gcc_private_make_location (EXPR_LOCATION (node.inner)); } /*************************************************************************** gcc_addr_expr **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_tree) gcc_addr_expr_get_operand(gcc_addr_expr node) { return gcc_private_make_tree (TREE_OPERAND (node.inner, 0)); } /*************************************************************************** gcc_reference **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_reference_get_location(gcc_reference node) { return gcc_private_make_location (EXPR_LOCATION (node.inner)); } /*************************************************************************** gcc_array_ref **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_tree) gcc_array_ref_get_array(gcc_array_ref node) { return gcc_private_make_tree(TREE_OPERAND(node.inner, 0)); } GCC_IMPLEMENT_PUBLIC_API(gcc_tree) gcc_array_ref_get_index(gcc_array_ref node) { return gcc_private_make_tree(TREE_OPERAND(node.inner, 1)); } /*************************************************************************** gcc_component_ref **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_tree) gcc_component_ref_get_target(gcc_component_ref node) { return gcc_private_make_tree (TREE_OPERAND (node.inner, 0)); } GCC_IMPLEMENT_PUBLIC_API(gcc_tree) gcc_component_ref_get_field(gcc_component_ref node) { return gcc_private_make_tree (TREE_OPERAND (node.inner, 1)); } /*************************************************************************** gcc_mem_ref **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_tree) gcc_mem_ref_get_operand(gcc_mem_ref node) { return gcc_private_make_tree (TREE_OPERAND (node.inner, 0)); } /*************************************************************************** gcc_ssa_name **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_ssa_name_get_var (gcc_ssa_name node) { return gcc_private_make_tree (SSA_NAME_VAR (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_gimple) gcc_ssa_name_get_def_stmt (gcc_ssa_name node) { return gcc_private_make_gimple (SSA_NAME_DEF_STMT (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (int) gcc_ssa_name_get_version (gcc_ssa_name node) { return SSA_NAME_VERSION (node.inner); } /*************************************************************************** gcc_statement **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (void) gcc_statement_mark_in_use (gcc_statement node); GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_statement_get_location(gcc_statement node) { return gcc_private_make_location (EXPR_LOCATION (node.inner)); } /*************************************************************************** gcc_case_label_expr **************************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_case_label_expr) gcc_private_make_case_label_expr (tree inner) { struct gcc_case_label_expr result; /* FIXME: type-checking */ result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_case_label_expr_get_low (gcc_case_label_expr node) { return gcc_private_make_tree (CASE_LOW (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_case_label_expr_get_high (gcc_case_label_expr node) { return gcc_private_make_tree (CASE_HIGH (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_label_decl) gcc_case_label_expr_get_target (gcc_case_label_expr node) { return gcc_tree_as_gcc_label_decl (gcc_private_make_tree (CASE_LABEL (node.inner))); } /*************************************************************************** gcc_unary **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_unary_get_location(gcc_unary node) { return gcc_private_make_location (EXPR_LOCATION (node.inner)); } GCC_IMPLEMENT_PUBLIC_API(gcc_tree) gcc_unary_get_operand(gcc_unary node) { return gcc_private_make_tree (TREE_OPERAND (node.inner, 0)); } /*************************************************************************** gcc_vlexp **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_location) gcc_vlexp_get_location(gcc_vlexp node) { return gcc_private_make_location (EXPR_LOCATION (node.inner)); } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-type.c000066400000000000000000000220371342215241600204160ustar00rootroot00000000000000/* Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . */ #include "gcc-common.h" #include "gcc-type.h" #include "gcc-constant.h" #include "gcc-tree.h" #include "gcc-internal.h" #include "tree.h" /* Types */ GCC_IMPLEMENT_PRIVATE_API (gcc_type) gcc_private_make_type (tree inner) { struct gcc_type result; result.inner = inner; return result; } GCC_IMPLEMENT_PRIVATE_API (gcc_pointer_type) gcc_private_make_pointer_type (tree inner) { struct gcc_pointer_type result; result.inner = inner; return result; } /*************************************************************************** gcc_type **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_type_get_name (gcc_type node) { return gcc_private_make_tree (TYPE_NAME (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_pointer_type) gcc_type_get_pointer (gcc_type node) { return gcc_private_make_pointer_type (build_pointer_type (node.inner)); } /*************************************************************************** gcc_array_type **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_type) gcc_array_type_get_dereference(gcc_array_type node) { return gcc_private_make_type (TREE_TYPE (node.inner)); } /*************************************************************************** gcc_boolean_type **************************************************************************/ /*************************************************************************** gcc_bound_template_template_parm **************************************************************************/ /*************************************************************************** gcc_category_implementation_type **************************************************************************/ /*************************************************************************** gcc_category_interface_type **************************************************************************/ /*************************************************************************** gcc_class_implementation_type **************************************************************************/ /*************************************************************************** gcc_class_interface_type **************************************************************************/ /*************************************************************************** gcc_complex_type **************************************************************************/ /*************************************************************************** gcc_decl_type_type **************************************************************************/ /*************************************************************************** gcc_enumeral_type **************************************************************************/ /*************************************************************************** gcc_fixed_point_type **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (int) gcc_fixed_point_type_get_precision (gcc_fixed_point_type node) { return TYPE_PRECISION (node.inner); } /*************************************************************************** gcc_function_type **************************************************************************/ /*************************************************************************** gcc_integer_type **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (gcc_integer_constant) gcc_integer_type_get_max_value (gcc_integer_type node) { return gcc_private_make_integer_constant (TYPE_MAX_VALUE (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (gcc_integer_constant) gcc_integer_type_get_min_value (gcc_integer_type node) { return gcc_private_make_integer_constant (TYPE_MIN_VALUE (node.inner)); } GCC_IMPLEMENT_PUBLIC_API (int) gcc_integer_type_get_precision (gcc_integer_type node) { return TYPE_PRECISION (node.inner); } GCC_IMPLEMENT_PUBLIC_API(bool) gcc_integer_type_is_unsigned(gcc_integer_type node) { return TYPE_UNSIGNED (node.inner); } /* gcc_integer_type */ GCC_IMPLEMENT_PRIVATE_API (gcc_integer_constant) gcc_private_make_integer_constant (tree inner) { gcc_integer_constant result; result.inner = INTEGER_CST_CHECK (inner); return result; } /*************************************************************************** gcc_lang_type **************************************************************************/ /*************************************************************************** gcc_method_type **************************************************************************/ /*************************************************************************** gcc_null_ptr_type **************************************************************************/ /*************************************************************************** gcc_offset_type **************************************************************************/ /*************************************************************************** gcc_pointer_type **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_type) gcc_pointer_type_get_dereference(gcc_pointer_type node) { return gcc_private_make_type (TREE_TYPE (node.inner)); } /*************************************************************************** gcc_protocol_interface_type **************************************************************************/ /*************************************************************************** gcc_qual_union_type **************************************************************************/ /*************************************************************************** gcc_real_type **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API (int) gcc_real_type_get_precision (gcc_real_type node) { return TYPE_PRECISION (node.inner); } /*************************************************************************** gcc_record_type **************************************************************************/ /*************************************************************************** gcc_reference_type **************************************************************************/ /*************************************************************************** gcc_template_template_parm **************************************************************************/ /*************************************************************************** gcc_template_type_parm **************************************************************************/ /*************************************************************************** gcc_type_argument_pack **************************************************************************/ /*************************************************************************** gcc_type_pack_expansion **************************************************************************/ /*************************************************************************** gcc_typename_type **************************************************************************/ /*************************************************************************** gcc_typeof_type **************************************************************************/ /*************************************************************************** gcc_unbound_class_template **************************************************************************/ /*************************************************************************** gcc_unconstrained_array_type **************************************************************************/ /*************************************************************************** gcc_union_type **************************************************************************/ /*************************************************************************** gcc_vector_type **************************************************************************/ GCC_IMPLEMENT_PUBLIC_API(gcc_type) gcc_vector_type_get_dereference(gcc_vector_type node) { return gcc_private_make_type (TREE_TYPE (node.inner)); } /*************************************************************************** gcc_void_type **************************************************************************/ /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/gcc-variable.c000066400000000000000000000052051342215241600212200ustar00rootroot00000000000000/* Copyright 2012, 2013 David Malcolm Copyright 2012, 2013 Red Hat, Inc. This 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 . */ #include "gcc-variable.h" #include "ggc.h" #include "tree.h" #include "cgraph.h" /* for varpool_nodes */ /*********************************************************** gcc_variable ************************************************************/ GCC_IMPLEMENT_PRIVATE_API (struct gcc_variable) gcc_private_make_variable (struct varpool_node *inner) { struct gcc_variable result; result.inner = inner; return result; } GCC_IMPLEMENT_PUBLIC_API (void) gcc_variable_mark_in_use (gcc_variable var) { /* Mark the underlying object (recursing into its fields): */ /* In GCC 4.8, struct varpool_node became part of union symtab_node_def, and In GCC 4.9, union symtab_node_def became class symtab_node. */ #if (GCC_VERSION >= 4009) gt_ggc_mx_symtab_node (var.inner); #else # if (GCC_VERSION >= 4008) gt_ggc_mx_symtab_node_def (var.inner); # else gt_ggc_mx_varpool_node (var.inner); # endif #endif } GCC_IMPLEMENT_PUBLIC_API (gcc_tree) gcc_variable_get_decl (gcc_variable var) { /* gcc 4.8 eliminated the tree decl; field of varpool_node in favor of struct symtab_node_base symbol; gcc 4.9 made varpool_node be a derived class of symtab_node_base, renaming it to symtab_node. */ tree decl; #if (GCC_VERSION >= 4009) /* Get from base class */ decl = var.inner->decl; #else # if (GCC_VERSION >= 4008) decl = var.inner->symbol.decl; # else decl = var.inner->decl; # endif #endif return gcc_private_make_tree (decl); } GCC_IMPLEMENT_PUBLIC_API (bool) gcc_for_each_variable (bool (*cb) (gcc_variable var, void *user_data), void *user_data) { struct varpool_node *n; #ifdef FOR_EACH_VARIABLE /* added in gcc 4.8 */ FOR_EACH_VARIABLE(n) #else for (n = varpool_nodes; n; n = n->next) #endif { if (cb (gcc_private_make_variable (n), user_data)) { return true; } } return false; } /* Local variables: c-basic-offset: 2 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-c-api/generate-casts-c.py000066400000000000000000000060241342215241600222340ustar00rootroot00000000000000# Copyright 2013 David Malcolm # Copyright 2013 Red Hat, Inc. # # This 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 # . import glob import sys from xmltypes import ApiRegistry, Api COPYRIGHT_HEADER = ''' Copyright 2013 David Malcolm Copyright 2013 Red Hat, Inc. This 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 . ''' def write_header(out): out.write('/* This file is autogenerated: do not edit */\n') out.write('/*%s*/\n' % COPYRIGHT_HEADER) out.write('\n') def write_footer(out): out.write(''' /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ ''') def main(c_filename, xmldir): registry = ApiRegistry() for xmlfile in sorted(glob.glob(xmldir + '*.xml')): api = Api(registry, xmlfile) with open(c_filename, 'w') as c_out: write_header(c_out) c_out.write('#include "gcc-common.h"\n') c_out.write('#include "gcc-tree.h"\n') c_out.write('#include "ggc.h"\n') c_out.write('#include "gcc-internal.h"\n') c_out.write('#include \n') c_out.write('#include "tree.h"\n') # Ensure that we use the headers, so that the generated # casts pick up on the 'extern "C"' and don't get name-mangled: for api in registry.apis: c_out.write('#include "%s"\n' % api.get_header_filename()) for type_ in registry.iter_types(): for subclass in type_.get_subclasses(recursive=True): c_out.write('IMPLEMENT_CAST(%s, %s)\n' % (type_.get_c_name(), subclass.get_c_name())) c_out.write('IMPLEMENT_CAST(%s, %s)\n' % (subclass.get_c_name(), type_.get_c_name())) write_footer(c_out) main(c_filename=sys.argv[1], xmldir=sys.argv[2]) gcc-python-plugin-0.17/gcc-c-api/gimple.xml000066400000000000000000000073271342215241600205430ustar00rootroot00000000000000 GIMPLE statements Source code location of this statement The lexical block holding this statement, as a gcc_tree The type of the main expression computed by this statement, as a gcc_tree (which might be void_type. The inline assembler as a string The left-hand-side of the assignment The left-hand-side of the call The function being called The declaration of the function being called (if any) Has this call been marked as never returning? The arguments for the call The return value The left-hand-side of the call The possible input expressions to the phi node The possible edges leading to the phi node The index variable used by the switch statement The labels of the switch statement. The initial label in the list is always the default. The underlying label_decl for this statement gcc-python-plugin-0.17/gcc-c-api/location.xml000066400000000000000000000031231342215241600210640ustar00rootroot00000000000000 Source code locations gcc-python-plugin-0.17/gcc-c-api/option.xml000066400000000000000000000021221342215241600205620ustar00rootroot00000000000000 Command-line options The text used at the command-line to affect this option e.g. -Werror. gcc-python-plugin-0.17/gcc-c-api/rtl.xml000066400000000000000000000016221342215241600200570ustar00rootroot00000000000000 Register Transfer Language gcc-python-plugin-0.17/gcc-c-api/tree.xml000066400000000000000000000214171342215241600202210ustar00rootroot00000000000000 Trees gcc-python-plugin-0.17/gcc-c-api/type.xml000066400000000000000000000072141342215241600202420ustar00rootroot00000000000000 Types The type that this type points to The precision of this type in bits (e.g. 32) The precision of this type in bits (e.g. 32) The type that this type points to The precision of this type in bits (e.g. 32) The type that this type points to gcc-python-plugin-0.17/gcc-c-api/variable.xml000066400000000000000000000021461342215241600210450ustar00rootroot00000000000000 Variables the declaration of this variable gcc-python-plugin-0.17/gcc-c-api/xml-to-h.py000066400000000000000000000255031342215241600205570ustar00rootroot00000000000000import glob import sys from xmltypes import ApiRegistry, Api COPYRIGHT_HEADER = ''' Copyright 2013 David Malcolm Copyright 2013 Red Hat, Inc. This 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 . ''' def write_header(out): out.write('/* This file is autogenerated: do not edit */\n') out.write('/*%s*/\n' % COPYRIGHT_HEADER) out.write('\n') def write_footer(out): out.write(''' /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ ''') class SourceWriter: def __init__(self, out): self.out = out self._indent = 0 def indent(self): self._indent += 1 def outdent(self): self._indent -= 1 def writeln(self, line=None): if line: self.out.write('%s%s\n' % (' ' * self._indent, line)) else: self.out.write('\n') def write_doc_comment(self, doc): self.write_comment(doc.as_text()) def write_comment(self, doc): self.writeln('/*') self.indent() for line in doc.splitlines(): self.writeln(line) self.outdent() self.writeln('*/') def write_begin_extern_c(self): self.writeln('#ifdef __cplusplus') self.writeln('extern "C" {') self.writeln('#endif') def write_end_extern_c(self): self.writeln('#ifdef __cplusplus') self.writeln('}') self.writeln('#endif') def write_api(api, out): writer = SourceWriter(out) write_header(out) if api.get_xml_name() == 'rtl': out.write(''' /* FIXME: rationalize these headers */ #include "gcc-common.h" ''') else: out.write('#include "gcc-common.h"\n') writer.writeln() writer.write_begin_extern_c() doc = api.get_doc() if doc: writer.write_doc_comment(doc) writer.writeln() for type_ in api.iter_types(): writer.writeln('/* %s */\n' % type_.get_c_name()) doc = type_.get_doc() if doc: writer.write_doc_comment(doc) # mark_in_use: writer.writeln('GCC_PUBLIC_API(void)') writer.writeln('%s_mark_in_use(%s %s);' % (type_.get_c_prefix(), type_.get_c_name(), type_.get_varname())) writer.writeln() # add getters for attributes: for attr in type_.iter_attrs(): doc = attr.get_doc() if doc: writer.write_doc_comment(doc) if attr.get_c_name().startswith('is_'): # "gcc_foo_is_some_boolean", rather than # "gcc_foo_get_is_some_boolean": writer.writeln('GCC_PUBLIC_API(%s)' % attr.get_c_type()) writer.writeln('%s_%s(%s %s);' % (type_.get_c_prefix(), attr.get_c_name(), type_.get_c_name(), type_.get_varname())) else: writer.writeln('GCC_PUBLIC_API(%s)' % attr.get_c_type()) writer.writeln('%s_get_%s(%s %s);' % (type_.get_c_prefix(), attr.get_c_name(), type_.get_c_name(), type_.get_varname())) writer.writeln() # add iterators for iter_ in type_.iter_iters(): itertype = iter_.get_type() writer.write_comment('Iterator; terminates if the callback returns truth\n' '(for linear search)') writer.writeln('GCC_PUBLIC_API(bool)') writer.writeln('%s_for_each_%s(%s %s,' % (type_.get_c_prefix(), iter_.get_c_name(), type_.get_c_name(), type_.get_varname())) writer.writeln(' bool (*cb)(%s %s, void *user_data),' % (itertype.get_c_name(), itertype.get_varname())) writer.writeln(' void *user_data);') writer.writeln() # add functions: for fun in type_.iter_functions(): doc = fun.get_doc() if doc: writer.write_doc_comment(doc) writer.writeln('GCC_PUBLIC_API(%s)' % fun.get_c_return_type()) paramstrs = ['%s %s' % (type_.get_c_name(), type_.get_varname())] for param in fun.iter_params(): paramstrs.append('%s %s' % (param.get_c_type(), param.get_xml_name())) writer.writeln('%s_%s(%s);' % (type_.get_c_prefix(), fun.get_c_name(), ', '.join(paramstrs))) writer.writeln() # add upcasts for base in type_.get_bases(): writer.writeln('GCC_PUBLIC_API(%s)' % base.get_c_name()) writer.writeln('%s_as_%s(%s %s);' % (type_.get_c_prefix(), base.get_c_name(), type_.get_c_name(), type_.get_varname())) writer.writeln() # add downcasts for subclass in type_.get_subclasses(recursive=True): writer.writeln('GCC_PUBLIC_API(%s)' % subclass.get_c_name()) writer.writeln('%s_as_%s(%s %s);' % (type_.get_c_prefix(), subclass.get_c_name(), type_.get_c_name(), type_.get_varname())) writer.writeln() # add getters for attributes: for attr in api.iter_attrs(): doc = attr.get_doc() if doc: writer.write_doc_comment(doc, out) if attr.is_readable(): writer.writeln('GCC_PUBLIC_API(%s)' % attr.get_c_type()) writer.writeln('gcc_get_%s(void);' % attr.get_c_name()) if attr.is_writable(): writer.writeln('GCC_PUBLIC_API(void)') writer.writeln('gcc_set_%s(%s %s);' % (attr.get_c_name(), attr.get_c_type(), attr.get_varname())) writer.writeln() # add iterators for iter_ in api.iter_iters(): itertype = iter_.get_type() writer.write_comment(' Iterator; terminates if the callback returns truth\n' ' (for linear search).') writer.writeln('GCC_PUBLIC_API(bool)') writer.writeln('gcc_for_each_%s(bool (*cb)(%s %s, void *user_data),' % (iter_.get_c_name(), itertype.get_c_name(), itertype.get_varname())) writer.writeln(' void *user_data);') writer.writeln() # add functions for fun in api.iter_functions(): writer.writeln('GCC_PUBLIC_API(%s)' % fun.get_c_return_type()) paramstrs = [] for param in fun.iter_params(): paramstrs.append('%s %s' % (param.get_c_type(), param.get_xml_name())) writer.writeln('gcc_%s(%s);' % (fun.get_c_name(), ', '.join(paramstrs))) writer.write_end_extern_c() write_footer(out) def write_public_types(registry, out): writer = SourceWriter(out) write_header(out) out.write('#ifndef INCLUDED__GCC_PUBLIC_TYPES_H\n') out.write('#define INCLUDED__GCC_PUBLIC_TYPES_H\n') out.write('\n') out.write('#include "gcc-semiprivate-types.h"\n') out.write('\n') writer.write_begin_extern_c() for api in registry.apis: out.write('/* Opaque types: %s */\n' % api.get_doc().as_text()) for type_ in api.iter_types(): out.write('typedef struct %s %s;\n' % (type_.get_c_name(), type_.get_c_name())) out.write('\n') writer.write_end_extern_c() out.write('#endif /* INCLUDED__GCC_PUBLIC_TYPES_H */\n') write_footer(out) def write_semiprivate_types(registry, out): writer = SourceWriter(out) write_header(out) out.write('#ifndef INCLUDED__GCC_SEMIPRIVATE_TYPES_H\n') out.write('#define INCLUDED__GCC_SEMIPRIVATE_TYPES_H\n') out.write('\n') out.write('#include "input.h" /* for location_t */\n') out.write('#include "options.h" /* for enum opt_code */\n') out.write('\n') writer.write_begin_extern_c() out.write('/*\n') out.write(' These "interface types" should be treated like pointers, only that\n') out.write(' users are required to collaborate with the garbage-collector.\n') out.write('\n') out.write(' The internal details are exposed here so that the debugger is able to\n') out.write(' identify the real types. Plugin developers should *not* rely on the\n') out.write(' internal implementation details.\n') out.write('\n') out.write(' By being structs, the compiler will be able to complain if plugin code\n') out.write(' directly pokes at a pointer.\n') out.write('*/\n') for api in registry.apis: out.write('/* Semiprivate types: %s */\n' % api.get_doc().as_text()) for type_ in api.iter_types(): out.write('struct %s {\n' % type_.get_c_name()) out.write(' %s inner;\n' % type_.get_inner_type()) out.write('};\n') out.write('\n') out.write('GCC_PRIVATE_API(struct %s)\n' % type_.get_c_name()) out.write('gcc_private_make_%s(%s inner);\n' % (type_.get_xml_name(), type_.get_inner_type())) out.write('\n') writer.write_end_extern_c() out.write('#endif /* INCLUDED__GCC_SEMIPRIVATE_TYPES_H */\n') write_footer(out) registry = ApiRegistry() xmldir=sys.argv[1] for xmlfile in sorted(glob.glob(xmldir + '*.xml')): api = Api(registry, xmlfile) for api in registry.apis: with open(api.get_header_filename(), 'w') as f: # write(api, sys.stdout) write_api(api, f) with open('gcc-public-types.h', 'w') as f: write_public_types(registry, f) with open('gcc-semiprivate-types.h', 'w') as f: write_semiprivate_types(registry, f) gcc-python-plugin-0.17/gcc-c-api/xmltypes.py000066400000000000000000000205441342215241600207770ustar00rootroot00000000000000# Copyright 2012, 2013 David Malcolm # Copyright 2012, 2013 Red Hat, Inc. # # This 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 # . # # Autogenerate a header file from a .h description import sys import unittest import xml.etree.ElementTree as ET def get_c_type(xml_kind): if xml_kind in ('int', 'bool'): return xml_kind if xml_kind == 'string': return 'const char*' if xml_kind == 'void': return 'void' return 'gcc_%s' % xml_kind class TypeNotFound(Exception): def __init__(self, xmlname): self.xmlname = xmlname def __str__(self): return 'type named %r not found' % self.xmlname class ApiRegistry: def __init__(self): self.apis = [] def load(self, filename): api = Api(self, filename) def iter_types(self): for api in self.apis: for type_ in api.iter_types(): yield type_ def lookup_type(self, xmlname): for api in self.apis: type_ = api.lookup_type(xmlname) if type_: return type_ raise TypeNotFound(xmlname) class XmlWrapper: def __init__(self, api, node): self.api = api self.node = node def __eq__(self, other): if not isinstance(other, XmlWrapper): return False return self.node == other.node class HasDocsMixin: def get_doc(self): xml_doc = self.node.find('doc') if xml_doc is not None: return Doc(self.api, xml_doc) else: return None class Type(XmlWrapper, HasDocsMixin): def get_xml_name(self): return self.node.get('name') def get_c_name(self): return 'gcc_%s' % self.get_xml_name() def get_c_prefix(self): return 'gcc_%s' % self.get_xml_name() def get_base(self): basename = self.node.get('base') if basename: return self.api.registry.lookup_type(basename) def get_bases(self): basename = self.node.get('base') if basename: base = self.api.registry.lookup_type(basename) yield base for base in base.get_bases(): yield base def get_subclasses(self, recursive=False): # brute force linear search for now: for type_ in self.api.registry.iter_types(): base = type_.get_base() if base == self: yield type_ if recursive: for type_ in type_.get_subclasses(recursive): yield type_ def get_varname(self): varname = self.node.get('varname') if varname: return varname base = self.get_base() return base.get_varname() def get_inner_type(self): inner = self.node.get('inner') if inner: return inner base = self.get_base() if base: return base.get_inner_type() else: class NoInnerType(Exception): def __init__(self, type_): self.type_ = type_ def __str__(self): return ('%s has no inheritable "inner" attribute' % self.type_.get_xml_name()) raise NoInnerType(self) def iter_attrs(self): for node in self.node.iter('attribute'): yield Attribute(self.api, node) def iter_iters(self): for node in self.node.iter('iterator'): yield Iterator(self.api, node) def iter_functions(self): for node in self.node.findall('function'): yield Function(self, node) class Attribute(XmlWrapper, HasDocsMixin): def get_xml_name(self): return self.node.get('name') def get_c_name(self): return self.get_xml_name() def get_xml_kind(self): return self.node.get('kind') def get_c_type(self): xml_kind = self.get_xml_kind() return get_c_type(xml_kind) def get_varname(self): xml_kind = self.get_xml_kind() if xml_kind == 'int': return 'i' if xml_kind == 'bool': return 'flag' if xml_kind == 'string': return 'str' return self.api.registry.lookup_type(xml_kind).get_varname() def get_access(self): access = self.node.get('access') if access: return access else: return 'r' # default to readonly def is_writable(self): access = self.get_access() return 'w' in access def is_readable(self): access = self.get_access() return 'r' in access class Iterator(XmlWrapper, HasDocsMixin): def get_xml_name(self): return self.node.get('name') def get_c_name(self): return self.get_xml_name() def get_type(self): xmlkind = self.node.get('kind') return self.api.registry.lookup_type(xmlkind) class Function(XmlWrapper, HasDocsMixin): def get_xml_name(self): return self.node.get('name') def get_c_name(self): return self.get_xml_name() def get_return_type(self): xmlkind = self.node.get('returntype') return xmlkind def get_c_return_type(self): xml_kind = self.get_return_type() return get_c_type(xml_kind); def iter_params(self): for node in self.node.iter('parameter'): yield Parameter(self.api, node) class Parameter(XmlWrapper, HasDocsMixin): def get_xml_name(self): return self.node.get('name') def get_xml_type(self): return self.node.get('type') def get_c_type(self): xmltype = self.get_xml_type() return get_c_type(xmltype) class Doc(XmlWrapper): def as_text(self): return self.node.text class Api: def __init__(self, registry, filename): self.registry = registry self.filename = filename tree = ET.parse(filename) self.registry.apis.append(self) self.api = tree.getroot() def get_xml_name(self): return self.api.get('name') def get_header_filename(self): return 'gcc-%s.h' % self.get_xml_name() def get_doc(self): xml_doc = self.api.find('doc') if xml_doc is not None: return Doc(self, xml_doc) else: return None def iter_types(self): for node in self.api.iter('type'): yield Type(self, node) def lookup_type(self, xmlname): for type_ in self.iter_types(): if xmlname == type_.get_xml_name(): return type_ def iter_attrs(self): for node in self.api.findall('attribute'): yield Attribute(self, node) def iter_iters(self): for node in self.api.findall('iterator'): yield Iterator(self, node) def iter_functions(self): for node in self.api.findall('function'): yield Function(self, node) class Tests(unittest.TestCase): def test_loading_all(self): r = ApiRegistry() for filename in ('cfg.xml', 'gimple.xml', 'rtl.xml'): gimpleapi = r.load(filename) def test_types(self): r = ApiRegistry() gimpleapi = r.load('gimple.xml') gimple = r.lookup_type('gimple') self.assertEqual(gimple.get_xml_name(), 'gimple') self.assertEqual(gimple.get_c_name(), 'gcc_gimple') self.assertEqual(gimple.get_varname(), 'stmt') def test_subclassing(self): r = ApiRegistry() gimpleapi = r.load('gimple.xml') gimple = r.lookup_type('gimple') gimple_phi = r.lookup_type('gimple_phi') self.assertEqual(gimple_phi.get_xml_name(), 'gimple_phi') self.assertEqual(gimple.get_base(), None) self.assertEqual(gimple_phi.get_base(), gimple) self.assertIn(gimple_phi, gimple.get_subclasses()) # python 2.7 if __name__ == '__main__': unittest.main() gcc-python-plugin-0.17/gcc-python-attribute.c000066400000000000000000000167371342215241600212460ustar00rootroot00000000000000/* Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "tree.h" #include "diagnostic.h" #include "plugin.h" #if (GCC_VERSION >= 4009) /* GCC 4.9 moved debug_tree here: */ #include "print-tree.h" #endif /* Attribute handling */ /* Dictionary mapping string attribute names to callables */ static PyObject *attribute_dict; /* Helper function when a custom attribute is encountered, for generating the arguments to the Python callback. The args to the function call will be the node, plus the args of the attribute: (node, arg0, arg1, ...) */ PyObject * make_args_for_attribute_callback(tree node, tree args) { PyObject *list_args = NULL; PyObject *py_args = NULL; PyObject *py_node = NULL; Py_ssize_t i; /* Walk "args" (a tree_list), converting to a python list of wrappers */ list_args = PyGcc_TreeMakeListFromTreeList(args); if (!list_args) { goto error; } py_args = PyTuple_New(1 + PyList_Size(list_args)); if (!py_args) { goto error; } py_node = PyGccTree_New(gcc_private_make_tree(node)); if (!py_node) { goto error; } PyTuple_SET_ITEM(py_args, 0, py_node); for (i = 0; i < PyList_Size(list_args); i++) { PyObject *arg = PyList_GetItem(list_args, i); Py_INCREF(arg); PyTuple_SET_ITEM(py_args, i + 1, arg); } Py_DECREF(list_args); return py_args; error: Py_XDECREF(list_args); Py_XDECREF(py_args); Py_XDECREF(py_node); return NULL; } static tree handle_python_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs) { PyObject *callable; /* Debug code: */ if (0) { printf("handle_python_attribute called\n"); fprintf(stderr, "node: "); debug_tree(*node); /* the site of the attribute e.g. a var_decl */ fprintf(stderr, "name: "); debug_tree(name); /* an identifier_node e.g. "custom_attribute_without_args" */ fprintf(stderr, "args: "); debug_tree(args); /* if present, a tree_list, e.g. of constants */ fprintf(stderr, "flags: %i\n", flags); fprintf(stderr, "and here!\n"); } /* How do we get to the attribute? This code: const struct attribute_spec *spec = lookup_attribute_spec (name); suggests that attributes must have unique names, so keep a dict mapping strings to callables */ assert(IDENTIFIER_NODE == TREE_CODE(name)); callable = PyDict_GetItemString(attribute_dict, IDENTIFIER_POINTER(name)); assert(callable); { PyGILState_STATE gstate; PyObject *py_args = NULL; PyObject *result = NULL; gstate = PyGILState_Ensure(); /* The args to the function call will be the node, plus the args of the attribute: */ py_args = make_args_for_attribute_callback(*node, args); if (!py_args) { goto cleanup; } result = PyObject_Call(callable, py_args, NULL); if (!result) { /* Treat an unhandled Python error as a compilation error: */ error("Unhandled Python exception raised within %s attribute handler", IDENTIFIER_POINTER(name)); PyErr_PrintEx(1); } /* (the result is ignored) */ cleanup: Py_XDECREF(py_args); Py_XDECREF(result); PyGILState_Release(gstate); } return NULL; // FIXME } PyObject* PyGcc_RegisterAttribute(PyObject *self, PyObject *args, PyObject *kwargs) { const char *name; int min_length; int max_length; int decl_required; int type_required; int function_type_required; PyObject *callable; struct attribute_spec *attr; const char *keywords[] = {"name", "min_length", "max_length", "decl_required", "type_required", "function_type_required", "callable", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siiiiiO:register_attribute", (char**)keywords, &name, &min_length, &max_length, &decl_required, &type_required, &function_type_required, &callable)) { return NULL; } /* "struct attribute_spec" is declared in gcc/tree.h register_attribute() is called by GCC for various attrs stored in tables of global data e.g.: const struct attribute_spec lto_attribute_table[] Hence we must malloc the data, so that it persists for the rest of the lifetime of the process We get a "handler" callback, it gets passed the name of the attribute, so maybe we can map names to callables. */ attr = PyMem_New(struct attribute_spec, 1); if (!attr) { return PyErr_NoMemory(); } /* Clear it first, for safety: */ memset(attr, 0, sizeof(struct attribute_spec)); /* Populate "attr" Annoyingly, all of the fields are marked as "const" within struct attribute_spec, so we have to cast away the constness, leading to the following deeply ugly code: */ *(char**)&attr->name = PyGcc_strdup(name); if (!attr->name) { PyMem_Free(attr); return PyErr_NoMemory(); } *(int*)&attr->min_length = min_length; *(int*)&attr->max_length = max_length; *(bool*)&attr->decl_required = decl_required; *(bool*)&attr->type_required = type_required; *(bool*)&attr->function_type_required = function_type_required; *(tree (**) (tree *node, tree name, tree args, int flags, bool *no_add_attrs))&attr->handler = handle_python_attribute; /* Associate the user-supplied callable with the given name, so that handle_python_attribute knows which one to call: */ if (!attribute_dict) { attribute_dict = PyDict_New(); if (!attribute_dict) { PyMem_Free((char*)attr->name); PyMem_Free(attr); return NULL; } } assert(attribute_dict); if (-1 == PyDict_SetItemString(attribute_dict, name, callable)) { PyMem_Free((char*)attr->name); PyMem_Free(attr); return NULL; } /* OK, call into GCC to register the attribute. (register_attribute doesn't have a return value; failures appear to be fatal) */ register_attribute (attr); Py_RETURN_NONE; } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-callbacks.c000066400000000000000000000251531342215241600211520ustar00rootroot00000000000000/* Copyright 2011, 2012 David Malcolm Copyright 2011, 2012 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-closure.h" #include "gcc-python-wrappers.h" #include "gcc-c-api/gcc-location.h" /* Notes on the passes As of 2011-03-30, http://gcc.gnu.org/onlinedocs/gccint/Plugins.html doesn't seem to document the type of the gcc_data passed to each callback. For reference, with gcc-4.6.0-0.15.fc15.x86_64 the types seem to be as follows: PLUGIN_ATTRIBUTES: gcc_data=0x0 Called from: init_attributes () at ../../gcc/attribs.c:187 However, it seems at this point to have initialized these: static const struct attribute_spec *attribute_tables[4]; static htab_t attribute_hash; PLUGIN_PRAGMAS: gcc_data=0x0 Called from: c_common_init () at ../../gcc/c-family/c-opts.c:1052 PLUGIN_START_UNIT: gcc_data=0x0 Called from: compile_file () at ../../gcc/toplev.c:573 PLUGIN_PRE_GENERICIZE gcc_data is: tree fndecl; Called from: finish_function () at ../../gcc/c-decl.c:8323 PLUGIN_OVERRIDE_GATE gcc_data: &gate_status bool gate_status; Called from : execute_one_pass (pass=0x1011340) at ../../gcc/passes.c:1520 PLUGIN_PASS_EXECUTION gcc_data: struct opt_pass *pass Called from: execute_one_pass (pass=0x1011340) at ../../gcc/passes.c:1530 PLUGIN_ALL_IPA_PASSES_START gcc_data=0x0 Called from: ipa_passes () at ../../gcc/cgraphunit.c:1779 PLUGIN_EARLY_GIMPLE_PASSES_START gcc_data=0x0 Called from: execute_ipa_pass_list (pass=0x1011fa0) at ../../gcc/passes.c:1927 PLUGIN_EARLY_GIMPLE_PASSES_END gcc_data=0x0 Called from: execute_ipa_pass_list (pass=0x1011fa0) at ../../gcc/passes.c:1930 PLUGIN_ALL_IPA_PASSES_END gcc_data=0x0 Called from: ipa_passes () at ../../gcc/cgraphunit.c:1821 PLUGIN_ALL_PASSES_START gcc_data=0x0 Called from: tree_rest_of_compilation (fndecl=0x7ffff16b1f00) at ../../gcc/tree-optimize.c:420 PLUGIN_ALL_PASSES_END gcc_data=0x0 Called from: tree_rest_of_compilation (fndecl=0x7ffff16b1f00) at ../../gcc/tree-optimize.c:425 PLUGIN_FINISH_UNIT gcc_data=0x0 Called from: compile_file () at ../../gcc/toplev.c:668 PLUGIN_FINISH_TYPE gcc_data=tree Called from c_parser_declspecs (parser=0x7fffef559730, specs=0x15296d0, scspec_ok=1 '\001', typespec_ok=1 '\001', start_attr_ok=, la=cla_nonabstract_decl) at ../../gcc/c-parser.c:2111 PLUGIN_PRAGMA gcc_data=0x0 Called from: init_pragma at ../../gcc/c-family/c-pragma.c:1321 to "Allow plugins to register their own pragmas." */ static enum plugin_event current_event = (enum plugin_event)GCC_PYTHON_PLUGIN_BAD_EVENT; int PyGcc_IsWithinEvent(enum plugin_event *out_event) { if (current_event != GCC_PYTHON_PLUGIN_BAD_EVENT) { if (out_event) { *out_event = current_event; } return 1; } else { return 0; } } static void PyGcc_FinishInvokingCallback(PyGILState_STATE gstate, int expect_wrapped_data, PyObject *wrapped_gcc_data, void *user_data) CPYCHECKER_STEALS_REFERENCE_TO_ARG(3) /* wrapped_gcc_data */ ; static void PyGcc_FinishInvokingCallback(PyGILState_STATE gstate, int expect_wrapped_data, PyObject *wrapped_gcc_data, void *user_data) { struct callback_closure *closure = (struct callback_closure *)user_data; PyObject *args = NULL; PyObject *result = NULL; gcc_location saved_loc = gcc_get_input_location(); enum plugin_event saved_event; assert(closure); /* We take ownership of wrapped_gcc_data. For some callbacks types it will always be NULL; for others, it's only NULL if an error has occurred: */ if (expect_wrapped_data && !wrapped_gcc_data) { goto cleanup; } if (cfun) { /* Temporarily override input_location to the top of the function: */ gcc_set_input_location(gcc_private_make_location(cfun->function_start_locus)); } args = PyGcc_Closure_MakeArgs(closure, 1, wrapped_gcc_data); if (!args) { goto cleanup; } saved_event = current_event; current_event = closure->event; result = PyObject_Call(closure->callback, args, closure->kwargs); current_event = saved_event; if (!result) { /* Treat an unhandled Python error as a compilation error: */ PyGcc_PrintException("Unhandled Python exception raised within callback"); } // FIXME: the result is ignored cleanup: Py_XDECREF(wrapped_gcc_data); Py_XDECREF(args); Py_XDECREF(result); /* We never cleanup "closure"; we don't know if we'll be called again */ PyGILState_Release(gstate); gcc_set_input_location(saved_loc); } /* C-level callbacks for each event ID follow, thunking into the registered Python callable. There's some repetition here, but it can be easier to debug if you have separate breakpoint locations for each event ID. */ static void PyGcc_CallbackFor_tree(void *gcc_data, void *user_data) { PyGILState_STATE gstate; tree t = (tree)gcc_data; gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 1, PyGccTree_New(gcc_private_make_tree(t)), user_data); } static void PyGcc_CallbackFor_PLUGIN_ATTRIBUTES(void *gcc_data, void *user_data) { PyGILState_STATE gstate; //printf("%s:%i:(%p, %p)\n", __FILE__, __LINE__, gcc_data, user_data); gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 0, NULL, user_data); } static void PyGcc_CallbackFor_PLUGIN_PASS_EXECUTION(void *gcc_data, void *user_data) { PyGILState_STATE gstate; struct opt_pass *pass = (struct opt_pass *)gcc_data; //printf("%s:%i:(%p, %p)\n", __FILE__, __LINE__, gcc_data, user_data); assert(pass); gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 1, PyGccPass_New(pass), user_data); } static void PyGcc_CallbackFor_FINISH(void *gcc_data, void *user_data) { PyGILState_STATE gstate; /* PLUGIN_FINISH: gcc_data=0x0 called from: toplev_main at ../../gcc/toplev.c:1970 */ gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 0, NULL, user_data); } static void PyGcc_CallbackFor_FINISH_UNIT(void *gcc_data, void *user_data) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 0, NULL, user_data); } static void PyGcc_CallbackFor_GGC_START(void *gcc_data, void *user_data) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 0, NULL, user_data); } static void PyGcc_CallbackFor_GGC_MARKING(void *gcc_data, void *user_data) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 0, NULL, user_data); } static void PyGcc_CallbackFor_GGC_END(void *gcc_data, void *user_data) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyGcc_FinishInvokingCallback(gstate, 0, NULL, user_data); } PyObject* PyGcc_RegisterCallback(PyObject *self, PyObject *args, PyObject *kwargs) { int event; PyObject *callback = NULL; PyObject *extraargs = NULL; struct callback_closure *closure; if (!PyArg_ParseTuple(args, "iO|O:register_callback", &event, &callback, &extraargs)) { return NULL; } //printf("%s:%i:PyGcc_RegisterCallback\n", __FILE__, __LINE__); closure = PyGcc_Closure_NewForPluginEvent(callback, extraargs, kwargs, (enum plugin_event)event); if (!closure) { return PyErr_NoMemory(); } switch ((enum plugin_event)event) { case PLUGIN_ATTRIBUTES: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_PLUGIN_ATTRIBUTES, closure); break; case PLUGIN_PRE_GENERICIZE: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_tree, closure); break; case PLUGIN_PASS_EXECUTION: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_PLUGIN_PASS_EXECUTION, closure); break; case PLUGIN_FINISH: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_FINISH, closure); break; case PLUGIN_FINISH_UNIT: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_FINISH_UNIT, closure); break; case PLUGIN_FINISH_TYPE: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_tree, closure); break; case PLUGIN_GGC_START: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_GGC_START, closure); break; case PLUGIN_GGC_MARKING: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_GGC_MARKING, closure); break; case PLUGIN_GGC_END: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_GGC_END, closure); break; /* PLUGIN_FINISH_DECL was added in gcc 4.7 onwards: */ #ifdef GCC_PYTHON_PLUGIN_CONFIG_has_PLUGIN_FINISH_DECL case PLUGIN_FINISH_DECL: register_callback("python", // FIXME (enum plugin_event)event, PyGcc_CallbackFor_tree, closure); break; #endif /* GCC_PYTHON_PLUGIN_CONFIG_has_PLUGIN_FINISH_DECL */ default: PyErr_Format(PyExc_ValueError, "event type %i invalid (or not wired up yet)", event); return NULL; } Py_RETURN_NONE; } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-callgraph.c000066400000000000000000000111531342215241600211630ustar00rootroot00000000000000/* Copyright 2011, 2012, 2015 David Malcolm Copyright 2011, 2012, 2015 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-c-api/gcc-callgraph.h" /* Wrapper for various types in gcc/cgraph.h struct cgraph_edge * struct cgraph_node * */ PyObject * PyGccCallgraphEdge_repr(struct PyGccCallgraphEdge * self) { return PyGccString_FromFormat("%s()", Py_TYPE(self)->tp_name); } PyObject * PyGccCallgraphEdge_str(struct PyGccCallgraphEdge * self) { return PyGccString_FromFormat("%s()", Py_TYPE(self)->tp_name); } PyObject * PyGccCallgraphNode_repr(struct PyGccCallgraphNode * self) { return PyGccString_FromFormat("%s()", Py_TYPE(self)->tp_name); } PyObject * PyGccCallgraphNode_str(struct PyGccCallgraphNode * self) { return PyGccString_FromFormat("%s()", Py_TYPE(self)->tp_name); } IMPL_APPENDER(add_cgraph_edge_to_list, gcc_cgraph_edge, PyGccCallgraphEdge_New) PyObject * PyGccCallgraphNode_get_callees(struct PyGccCallgraphNode * self) { IMPL_LIST_MAKER(gcc_cgraph_node_for_each_callee, self->node, add_cgraph_edge_to_list) } PyObject * PyGccCallgraphNode_get_callers(struct PyGccCallgraphNode * self) { IMPL_LIST_MAKER(gcc_cgraph_node_for_each_caller, self->node, add_cgraph_edge_to_list) } union gcc_cgraph_edge_as_ptr { gcc_cgraph_edge edge; void *ptr; }; PyObject * real_make_cgraph_edge_wrapper(void *ptr) { struct PyGccCallgraphEdge *obj = NULL; union gcc_cgraph_edge_as_ptr u; u.ptr = ptr; obj = PyGccWrapper_New(struct PyGccCallgraphEdge, &PyGccCallgraphEdge_TypeObj); if (!obj) { goto error; } obj->edge = u.edge; return (PyObject*)obj; error: return NULL; } static PyObject *cgraph_edge_wrapper_cache = NULL; PyObject * PyGccCallgraphEdge_New(gcc_cgraph_edge edge) { union gcc_cgraph_edge_as_ptr u; u.edge = edge; return PyGcc_LazilyCreateWrapper(&cgraph_edge_wrapper_cache, u.ptr, real_make_cgraph_edge_wrapper); } void PyGcc_WrtpMarkForPyGccCallgraphEdge(PyGccCallgraphEdge *wrapper) { gcc_cgraph_edge_mark_in_use(wrapper->edge); } union gcc_cgraph_node_as_ptr { gcc_cgraph_node node; void *ptr; }; PyObject * real_make_cgraph_node_wrapper(void *ptr) { struct PyGccCallgraphNode *obj = NULL; union gcc_cgraph_node_as_ptr u; u.ptr = ptr; obj = PyGccWrapper_New(struct PyGccCallgraphNode, &PyGccCallgraphNode_TypeObj); if (!obj) { goto error; } obj->node = u.node; return (PyObject*)obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccCallgraphNode(PyGccCallgraphNode *wrapper) { gcc_cgraph_node_mark_in_use(wrapper->node); } static PyObject *cgraph_node_wrapper_cache = NULL; PyObject * PyGccCallgraphNode_New(gcc_cgraph_node node) { union gcc_cgraph_node_as_ptr u; u.node = node; return PyGcc_LazilyCreateWrapper(&cgraph_node_wrapper_cache, u.ptr, real_make_cgraph_node_wrapper); } IMPL_APPENDER(add_cgraph_node_to_list, gcc_cgraph_node, PyGccCallgraphNode_New) PyObject * PyGcc_get_callgraph_nodes(PyObject *self, PyObject *args) { /* For debugging, see GCC's dump of things: */ if (0) { fprintf(stderr, "----------------BEGIN----------------\n"); #if (GCC_VERSION >= 5000) cgraph_node::dump_cgraph (stderr); #else dump_cgraph (stderr); #endif fprintf(stderr, "---------------- END ----------------\n"); } IMPL_GLOBAL_LIST_MAKER(gcc_for_each_cgraph_node, add_cgraph_node_to_list) } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-cfg.c000066400000000000000000000264761342215241600200030ustar00rootroot00000000000000/* Copyright 2011, 2013 David Malcolm Copyright 2011, 2013 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-c-api/gcc-cfg.h" #include "gcc-c-api/gcc-gimple.h" #include "gcc-c-api/gcc-private-compat.h" /* for now */ #if 1 /* Ideally we wouldn't have these includes here: */ #include "basic-block.h" #include "rtl.h" #endif /* "struct edge_def" is declared in basic-block.h, c.f: struct GTY(()) edge_def { ... snip ... } and there are these typedefs to pointers defined in coretypes.h: typedef struct edge_def *edge; typedef const struct edge_def *const_edge; */ union cfg_edge_or_ptr { gcc_cfg_edge edge; void *ptr; }; PyObject * real_make_edge(void * ptr) { union cfg_edge_or_ptr u; u.ptr = ptr; struct PyGccEdge *obj; if (!u.edge.inner) { Py_RETURN_NONE; } obj = PyGccWrapper_New(struct PyGccEdge, &PyGccEdge_TypeObj); if (!obj) { goto error; } obj->e = u.edge; return (PyObject*)obj; error: return NULL; } static PyObject *edge_wrapper_cache = NULL; PyObject * PyGccEdge_New(gcc_cfg_edge e) { union cfg_edge_or_ptr u; u.edge = e; return PyGcc_LazilyCreateWrapper(&edge_wrapper_cache, u.ptr, real_make_edge); } void PyGcc_WrtpMarkForPyGccEdge(PyGccEdge *wrapper) { /* Mark the underlying object (recursing into its fields): */ gcc_cfg_edge_mark_in_use(wrapper->e); } IMPL_APPENDER(add_edge_to_list, gcc_cfg_edge, PyGccEdge_New) PyObject * PyGccBasicBlock_repr(struct PyGccBasicBlock * self) { return PyGccString_FromFormat("%s(index=%i)", Py_TYPE(self)->tp_name, gcc_cfg_block_get_index(self->bb)); } PyObject * PyGccBasicBlock_get_preds(PyGccBasicBlock *self, void *closure) { IMPL_LIST_MAKER(gcc_cfg_block_for_each_pred_edge, self->bb, add_edge_to_list) } PyObject * PyGccBasicBlock_get_succs(PyGccBasicBlock *self, void *closure) { IMPL_LIST_MAKER(gcc_cfg_block_for_each_succ_edge, self->bb, add_edge_to_list) } IMPL_APPENDER(append_gimple_to_list, gcc_gimple, PyGccGimple_New) PyObject * PyGccBasicBlock_get_gimple(PyGccBasicBlock *self, void *closure) { assert(self); assert(self->bb.inner); IMPL_LIST_MAKER(gcc_cfg_block_for_each_gimple, self->bb, append_gimple_to_list) } static PyObject* PyGccGimple_New_phi(gcc_gimple_phi phi) { return PyGccGimple_New(gcc_gimple_phi_as_gcc_gimple(phi)); } IMPL_APPENDER(append_gimple_phi_to_list, gcc_gimple_phi, PyGccGimple_New_phi) PyObject * PyGccBasicBlock_get_phi_nodes(PyGccBasicBlock *self, void *closure) { assert(self); assert(self->bb.inner); IMPL_LIST_MAKER(gcc_cfg_block_for_each_gimple_phi, self->bb, append_gimple_phi_to_list) } IMPL_APPENDER(append_rtl_to_list, gcc_rtl_insn, PyGccRtl_New) PyObject * PyGccBasicBlock_get_rtl(PyGccBasicBlock *self, void *closure) { assert(self); assert(self->bb.inner); IMPL_LIST_MAKER(gcc_cfg_block_for_each_rtl_insn, self->bb, append_rtl_to_list) } /* Force a 1-1 mapping between pointer values and wrapper objects */ PyObject * PyGcc_LazilyCreateWrapper(PyObject **cache, void *ptr, PyObject *(*ctor)(void *ptr)) { PyObject *key = NULL; PyObject *oldobj = NULL; PyObject *newobj = NULL; /* printf("PyGcc_LazilyCreateWrapper(&%p, %p, %p)\n", *cache, ptr, ctor); */ assert(cache); /* ptr is allowed to be NULL */ assert(ctor); /* The cache is lazily created: */ if (!*cache) { *cache = PyDict_New(); if (!*cache) { return NULL; } } key = PyLong_FromVoidPtr(ptr); if (!key) { return NULL; } oldobj = PyDict_GetItem(*cache, key); if (oldobj) { /* The cache already contains an object wrapping "ptr": reuse t */ /* printf("reusing %p for %p\n", oldobj, ptr); */ Py_INCREF(oldobj); /* it was a borrowed ref */ Py_DECREF(key); return oldobj; } /* Not in the cache: we don't yet have a wrapper object for this pointer */ assert(NULL != key); /* we own a ref */ assert(NULL == oldobj); assert(NULL == newobj); /* Construct a wrapper : */ newobj = (*ctor)(ptr); if (!newobj) { Py_DECREF(key); return NULL; } /* printf("created %p for %p\n", newobj, ptr); */ if (PyDict_SetItem(*cache, key, newobj)) { Py_DECREF(newobj); Py_DECREF(key); return NULL; } Py_DECREF(key); return newobj; } int PyGcc_insert_new_wrapper_into_cache(PyObject **cache, void *ptr, PyObject *obj) { PyObject *key; assert(cache); assert(ptr); assert(obj); /* The cache is lazily created: */ if (!*cache) { *cache = PyDict_New(); if (!*cache) { return -1; } } key = PyLong_FromVoidPtr(ptr); if (!key) { return -1; } if (PyDict_SetItem(*cache, key, obj)) { Py_DECREF(key); return -1; } Py_DECREF(key); return 0; } union cfg_block_or_ptr { gcc_cfg_block block; void *ptr; }; static PyObject * real_make_basic_block_wrapper(void *ptr) { union cfg_block_or_ptr u; struct PyGccBasicBlock *obj; u.ptr = ptr; if (!u.block.inner) { Py_RETURN_NONE; } obj = PyGccWrapper_New(struct PyGccBasicBlock, &PyGccBasicBlock_TypeObj); if (!obj) { goto error; } #if 0 printf("bb: %p\n", bb); printf("bb->flags: 0x%x\n", bb->flags); printf("bb->flags & BB_RTL: %i\n", bb->flags & BB_RTL); if (bb->flags & BB_RTL) { printf("bb->il.rtl: %p\n", bb->il.rtl); } else { printf("bb->il.gimple: %p\n", bb->il.gimple); if (bb->il.gimple) { /* See http://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html and also gimple-pretty-print.c coretypes.h has: struct gimple_seq_d; typedef struct gimple_seq_d *gimple_seq; and gimple.h has: "A double-linked sequence of gimple statements." struct GTY ((chain_next ("%h.next_free"))) gimple_seq_d { ... snip ... } and: struct gimple_seq_node_d; typedef struct gimple_seq_node_d *gimple_seq_node; and: struct GTY((chain_next ("%h.next"), chain_prev ("%h.prev"))) gimple_seq_node_d { gimple stmt; struct gimple_seq_node_d *prev; struct gimple_seq_node_d *next; }; and coretypes.h has: union gimple_statement_d; typedef union gimple_statement_d *gimple; and gimple.h has the "union gimple_statement_d", and another set of codes for this */ printf("bb->il.gimple->seq: %p\n", bb->il.gimple->seq); printf("bb->il.gimple->phi_nodes: %p\n", bb->il.gimple->phi_nodes); { gimple_stmt_iterator i; for (i = gsi_start (bb->il.gimple->seq); !gsi_end_p (i); gsi_next (&i)) { gimple stmt = gsi_stmt(i); printf(" gimple: %p code: %s (%i) %s:%i num_ops=%i\n", stmt, gimple_code_name[gimple_code(stmt)], gimple_code(stmt), gimple_filename(stmt), gimple_lineno(stmt), gimple_num_ops(stmt)); //print_generic_stmt (stderr, stmt, 0); } } } } #endif obj->bb = u.block; return (PyObject*)obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccBasicBlock(PyGccBasicBlock *wrapper) { /* Mark the underlying object (recursing into its fields): */ gcc_cfg_block_mark_in_use(wrapper->bb); } static PyObject *basic_block_wrapper_cache = NULL; PyObject * PyGccBasicBlock_New(gcc_cfg_block bb) { return PyGcc_LazilyCreateWrapper(&basic_block_wrapper_cache, bb.inner, real_make_basic_block_wrapper); } static bool add_block_to_list(gcc_cfg_block bb, void *user_data) { PyObject *result = (PyObject*)user_data; PyObject *obj_var; obj_var = PyGccBasicBlock_New(bb); if (!obj_var) { return true; } /* It appears that with optimization there can be occasional NULLs, which get turned into None. Skip them: */ if (obj_var != Py_None) { if (-1 == PyList_Append(result, obj_var)) { Py_DECREF(obj_var); return true; } /* Success: */ } Py_DECREF(obj_var); return false; } PyObject * PyGccCfg_get_basic_blocks(PyGccCfg *self, void *closure) { IMPL_LIST_MAKER(gcc_cfg_for_each_block, self->cfg, add_block_to_list) } extern PyTypeObject PyGccLabelDecl_TypeObj; PyObject * PyGccCfg_get_block_for_label(PyObject *s, PyObject *args) { struct PyGccCfg *self = (struct PyGccCfg *)s; struct PyGccTree *label_decl; int uid; basic_block bb; if (!PyArg_ParseTuple(args, "O!:get_block_for_label", &PyGccLabelDecl_TypeObj, &label_decl)) { return NULL; } /* See also gcc/tree-cfg.c: label_to_block_fn */ uid = LABEL_DECL_UID(label_decl->t.inner); if (uid < 0 || ( ( #if (GCC_VERSION >= 4008) vec_safe_length(self->cfg.inner->x_label_to_block_map) #else VEC_length (basic_block, self->cfg.inner->x_label_to_block_map) #endif ) <= (unsigned int) uid) ) { return PyErr_Format(PyExc_ValueError, "uid %i not found", uid); } bb = GCC_COMPAT_VEC_INDEX(basic_block, self->cfg.inner->x_label_to_block_map, uid); return PyGccBasicBlock_New(gcc_private_make_cfg_block(bb)); } union gcc_cfg_as_ptr { gcc_cfg cfg; void *ptr; }; PyObject * real_make_cfg_wrapper(void *ptr) { struct PyGccCfg *obj; union gcc_cfg_as_ptr u; u.ptr = ptr; if (!u.cfg.inner) { Py_RETURN_NONE; } obj = PyGccWrapper_New(struct PyGccCfg, &PyGccCfg_TypeObj); if (!obj) { goto error; } obj->cfg = u.cfg; return (PyObject*)obj; error: return NULL; } static PyObject *cfg_wrapper_cache = NULL; PyObject * PyGccCfg_New(gcc_cfg cfg) { union gcc_cfg_as_ptr u; u.cfg = cfg; return PyGcc_LazilyCreateWrapper(&cfg_wrapper_cache, u.ptr, real_make_cfg_wrapper); } void PyGcc_WrtpMarkForPyGccCfg(PyGccCfg *wrapper) { /* Mark the underlying object (recursing into its fields): */ gcc_cfg_mark_in_use(wrapper->cfg); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-closure.c000066400000000000000000000075261342215241600207130ustar00rootroot00000000000000/* Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . */ #include #include #include "gcc-python-closure.h" #include "gcc-python.h" #include "function.h" #include "gcc-c-api/gcc-function.h" struct callback_closure * PyGcc_closure_new_generic(PyObject *callback, PyObject *extraargs, PyObject *kwargs) { struct callback_closure *closure; assert(callback); /* extraargs can be NULL kwargs can also be NULL */ closure = PyMem_New(struct callback_closure, 1); if (!closure) { return NULL; } closure->callback = callback; Py_INCREF(callback); // FIXME: we may want to pass in the event enum as well as the user-supplied extraargs if (extraargs) { /* Hold a reference to the extraargs for when we register it with the callback: */ closure->extraargs = extraargs; Py_INCREF(extraargs); } else { closure->extraargs = PyTuple_New(0); if (!closure->extraargs) { return NULL; // singleton, so can't happen, really } } closure->kwargs = kwargs; if (kwargs) { Py_INCREF(kwargs); } closure->event = (enum plugin_event)GCC_PYTHON_PLUGIN_BAD_EVENT; return closure; } struct callback_closure * PyGcc_Closure_NewForPluginEvent(PyObject *callback, PyObject *extraargs, PyObject *kwargs, enum plugin_event event) { struct callback_closure *closure = PyGcc_closure_new_generic(callback, extraargs, kwargs); if (closure) { closure->event = event; } return closure; } PyObject * PyGcc_Closure_MakeArgs(struct callback_closure * closure, int add_cfun, PyObject *wrapped_gcc_data) { PyObject *args = NULL; PyObject *cfun_obj = NULL; int i; assert(closure); /* wrapped_gcc_data can be NULL if there isn't one for this kind of callback */ assert(closure->extraargs); assert(PyTuple_Check(closure->extraargs)); if (wrapped_gcc_data) { /* Equivalent to either: args = (gcc_data, cfun, ) + extraargs or: args = (gcc_data, ) + extraargs */ args = PyTuple_New((add_cfun ? 2 : 1) + PyTuple_Size(closure->extraargs)); if (!args) { goto error; } if (add_cfun) { cfun_obj = PyGccFunction_New(gcc_get_current_function()); if (!cfun_obj) { goto error; } } PyTuple_SetItem(args, 0, wrapped_gcc_data); if (add_cfun) { PyTuple_SetItem(args, 1, cfun_obj); } Py_INCREF(wrapped_gcc_data); for (i = 0; i < PyTuple_Size(closure->extraargs); i++) { PyObject *item = PyTuple_GetItem(closure->extraargs, i); PyTuple_SetItem(args, i + (add_cfun ? 2 : 1), item); Py_INCREF(item); } return args; } else { /* Just reuse closure's extraargs tuple */ Py_INCREF(closure->extraargs); return closure->extraargs; } error: Py_XDECREF(args); Py_XDECREF(cfun_obj); return NULL; } void PyGcc_closure_free(struct callback_closure *closure) { assert(closure); Py_XDECREF(closure->callback); Py_XDECREF(closure->extraargs); Py_XDECREF(closure->kwargs); PyMem_Free(closure); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-closure.h000066400000000000000000000033411342215241600207070ustar00rootroot00000000000000/* Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . */ #ifndef INCLUDED__GCC_PYTHON_CLOSURE_H #define INCLUDED__GCC_PYTHON_CLOSURE_H struct callback_closure { PyObject *callback; PyObject *extraargs; PyObject *kwargs; enum plugin_event event; /* or GCC_PYTHON_PLUGIN_BAD_EVENT if not an event */ }; struct callback_closure * PyGcc_closure_new_generic(PyObject *callback, PyObject *extraargs, PyObject *kwargs); struct callback_closure * PyGcc_Closure_NewForPluginEvent(PyObject *callback, PyObject *extraargs, PyObject *kwargs, enum plugin_event event); PyObject * PyGcc_Closure_MakeArgs(struct callback_closure * closure, int add_cfun, PyObject *wrapped_gcc_data); void PyGcc_closure_free(struct callback_closure *closure); /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ #endif /* INCLUDED__GCC_PYTHON_CLOSURE_H */ gcc-python-plugin-0.17/gcc-python-compat.h000066400000000000000000000044621342215241600205230ustar00rootroot00000000000000/* Copyright 2011, 2012, 2014 David Malcolm Copyright 2011, 2012, 2014 Red Hat, Inc. This 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 . */ #ifndef INCLUDED__GCC_PYTHON_COMPAT_H #define INCLUDED__GCC_PYTHON_COMPAT_H #include "gimple.h" #include "tree.h" #if GCC_VERSION < 8000 typedef int dump_flags_t; #endif /* There are a few GCC symbols that don't seem to be exposed in the plugin headers, but I wish were. We manually repeated the necessary declarations here. This is wrong, but at least it's all captured here in one place. Hopefully these will eventually become officially exposed to plugins, but for now its all here. */ /* This is declared in gcc/gimple-pretty-print.c, but not exposed in any of the plugin headers AFAIK: */ extern void dump_gimple_stmt (pretty_printer *buffer, gimple gs, int spc, dump_flags_t flags); /* This is declared in gcc/tree-pretty-print.c (around line 580); it was only exposed to plugin headers (in tree-pretty-print.h) in GCC commit r203113 (aka 0d9585ca35b919263b973afb371f0eda04857159, 2013-10-02), as part of GCC 4.9 The signature was changed by GCC 8 commit r248140 (aka 3f6e5ced7eb1cf5b3212b2391c5b70ec3dcaf136, 2017-05-17), which introduced dump_flags_t. */ #if GCC_VERSION < 4009 extern int dump_generic_node (pretty_printer *buffer, tree node, int spc, dump_flags_t flags, bool is_stmt); #endif /* Within gcc/gcc-internal.h, not exposed by plugin API */ extern bool ggc_force_collect; /* From c-family/c-common.h */ #if GCC_VERSION < 4008 extern tree c_sizeof_or_alignof_type (location_t, tree, bool, int); #endif /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ #endif /* INCLUDED__GCC_PYTHON_COMPAT_H */ gcc-python-plugin-0.17/gcc-python-diagnostics.c000066400000000000000000000136031342215241600215370ustar00rootroot00000000000000/* Copyright 2011-2013, 2017 David Malcolm Copyright 2011-2013, 2017 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "diagnostic.h" #include "gcc-c-api/gcc-diagnostics.h" /* I initially attempted to directly wrap gcc's: diagnostic_report_diagnostic() given that that seems to be the abstraction within gcc/diagnostic.h: all instances of (struct diagnostic_info) within the gcc source tree seem to be allocated on the stack, within functions exposed in gcc/diagnostic.h However, diagnostic_report_diagnostic() ultimately calls into the pretty-printing routines, trying to format varargs, which doesn't make much sense for us: we have first-class string objects and string formatting at the python level. Thus we instead just wrap "error_at" and its analogs */ PyObject* PyGcc_permerror(PyObject *self, PyObject *args) { PyGccLocation *loc_obj = NULL; const char *msg = NULL; PyObject *result_obj = NULL; bool result_b; if (!PyArg_ParseTuple(args, "O!" "s" ":permerror", &PyGccLocation_TypeObj, &loc_obj, &msg)) { return NULL; } /* Invoke the GCC function: */ result_b = gcc_permerror(loc_obj->loc, msg); result_obj = PyBool_FromLong(result_b); return result_obj; } PyObject * PyGcc_error(PyObject *self, PyObject *args, PyObject *kwargs) { PyGccLocation *loc_obj; const char *msg; const char *keywords[] = {"location", "message", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!s:error", (char**)keywords, &PyGccLocation_TypeObj, &loc_obj, &msg)) { return NULL; } gcc_error_at(loc_obj->loc, msg); Py_RETURN_NONE; } PyObject * PyGcc_warning(PyObject *self, PyObject *args, PyObject *kwargs) { PyGccLocation *loc_obj; const char *msg; PyObject *opt_obj = &_Py_NoneStruct; int opt_code; const char *keywords[] = {"location", "message", "option", NULL}; bool was_reported; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!s|O:warning", (char**)keywords, /* code "O!": */ &PyGccLocation_TypeObj, &loc_obj, /* code: "s": */ &msg, /* optional args: */ /* code: "O": */ &opt_obj)) { return NULL; } assert(opt_obj); /* If a gcc.Option was given, extract the code: */ if (Py_TYPE(opt_obj) == (PyTypeObject*)&PyGccOption_TypeObj) { opt_code = ((PyGccOption*)opt_obj)->opt.inner; /* Ugly workaround; see this function: */ if (0 == PyGcc_option_is_enabled((enum opt_code)opt_code)) { return PyBool_FromLong(0); } } else { if (opt_obj == &_Py_NoneStruct) { /* No gcc.Option given: an unconditionally enabled warning: */ opt_code = 0; } else { /* Some other object was given: */ return PyErr_Format(PyExc_TypeError, ("option must be either None," " or of type gcc.Option")); } } was_reported = warning_at(loc_obj->loc.inner, opt_code, "%s", msg); return PyBool_FromLong(was_reported); } PyObject * PyGcc_inform(PyObject *self, PyObject *args, PyObject *kwargs) { const char *msg; const char *keywords[] = {"location", "message", NULL}; #if (GCC_VERSION >= 6000) PyObject *obj; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os:inform", (char**)keywords, &obj, &msg)) { return NULL; } /* Check for location vs rich_location. */ if (Py_TYPE (obj) == (PyTypeObject *)&PyGccLocation_TypeObj) { PyGccLocation *loc_obj = (PyGccLocation *)obj; gcc_inform(loc_obj->loc, msg); Py_RETURN_NONE; } else if (Py_TYPE (obj) == (PyTypeObject *)&PyGccRichLocation_TypeObj) { PyGccRichLocation *richloc_obj = (PyGccRichLocation *)obj; #if (GCC_VERSION >= 8000) inform (&richloc_obj->richloc, "%s", msg); #else inform_at_rich_loc (&richloc_obj->richloc, "%s", msg); #endif Py_RETURN_NONE; } else { return PyErr_Format(PyExc_TypeError, ("type of location must be either gcc.Location" " or gcc.RichLocation")); } #else PyGccLocation *loc_obj; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!s:inform", (char**)keywords, &PyGccLocation_TypeObj, &loc_obj, &msg)) { return NULL; } gcc_inform(loc_obj->loc, msg); Py_RETURN_NONE; #endif } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-docs000077500000000000000000000015211342215241600177360ustar00rootroot00000000000000#!/bin/sh # Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Invoke "show-docs.py", with a dummy source file ./gcc-with-python examples/show-docs.py test.c gcc-python-plugin-0.17/gcc-python-function.c000066400000000000000000000121731342215241600210560ustar00rootroot00000000000000/* Copyright 2011, 2012 David Malcolm Copyright 2011, 2012 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" /* "struct function" is declared in function.h, c.f.: struct GTY(()) function { ... snip ... }; */ #include "function.h" #include "gcc-c-api/gcc-function.h" PyObject * PyGccFunction_repr(struct PyGccFunction * self) { const char *name = NULL; PyObject *result = NULL; tree decl; assert(self->fun.inner); decl = self->fun.inner->decl; if (DECL_NAME(decl)) { name = IDENTIFIER_POINTER (DECL_NAME(decl)); } else { name = "(unnamed)"; } if (!name) { goto error; } result = PyGccString_FromFormat("gcc.Function('%s')", name); return result; error: Py_XDECREF(result); return NULL; } long PyGccFunction_hash(struct PyGccFunction * self) { return (long)self->fun.inner; } PyObject * PyGccFunction_richcompare(PyObject *o1, PyObject *o2, int op) { struct PyGccFunction *functionobj1; struct PyGccFunction *functionobj2; int cond; PyObject *result_obj; if (!PyObject_TypeCheck(o1, (PyTypeObject*)&PyGccFunction_TypeObj)) { result_obj = Py_NotImplemented; goto out; } if (!PyObject_TypeCheck(o2, (PyTypeObject*)&PyGccFunction_TypeObj)) { result_obj = Py_NotImplemented; goto out; } functionobj1 = (struct PyGccFunction *)o1; functionobj2 = (struct PyGccFunction *)o2; switch (op) { case Py_EQ: cond = (functionobj1->fun.inner == functionobj2->fun.inner); break; case Py_NE: cond = (functionobj1->fun.inner != functionobj2->fun.inner); break; default: result_obj = Py_NotImplemented; goto out; } result_obj = cond ? Py_True : Py_False; out: Py_INCREF(result_obj); return result_obj; } PyObject * PyGccFunction_New(gcc_function func) { struct PyGccFunction *obj; if (!func.inner) { Py_RETURN_NONE; } #if 0 printf("PyGccFunction_New(%p)\n", fun); printf("struct eh_status *eh: %p\n", fun->eh); printf("struct control_flow_graph *cfg: %p\n", fun->cfg); printf("struct gimple_seq_d *gimple_body: %p\n", fun->gimple_body); printf("struct gimple_df *gimple_df: %p\n", fun->gimple_df); printf("struct loops *x_current_loops: %p\n", fun->x_current_loops); printf("struct stack_usage *su: %p\n", fun->su); #if 0 printf("htab_t GTY((skip)) value_histogram\n"); printf("tree decl: %p;\n", fun->decl); printf("tree static_chain_decl;\n"); printf("tree nonlocal_goto_save_area;\n"); printf("VEC(tree,gc) *local_decls: local_decls;\n"); printf("struct machine_function * GTY ((maybe_undef)) machine;\n"); printf("struct language_function * language;\n"); printf("htab_t GTY ((param_is (union tree_node))) used_types_hash;\n"); printf("int last_stmt_uid;\n"); printf("int funcdef_no;\n"); printf("location_t function_start_locus;\n"); printf("location_t function_end_locus;\n"); printf("unsigned int curr_properties;\n"); printf("unsigned int last_verified;\n"); printf("const char * GTY((skip)) cannot_be_copied_reason;\n"); printf("unsigned int va_list_gpr_size : 8;\n"); printf("unsigned int va_list_fpr_size : 8;\n"); printf("unsigned int calls_setjmp : 1;\n"); printf("unsigned int calls_alloca : 1;\n"); printf("unsigned int has_nonlocal_label : 1;\n"); printf("unsigned int cannot_be_copied_set : 1;\n"); printf("unsigned int stdarg : 1;\n"); printf("unsigned int dont_save_pending_sizes_p : 1;\n"); printf("unsigned int after_inlining : 1;\n"); printf("unsigned int always_inline_functions_inlined : 1;\n"); printf("unsigned int can_throw_non_call_exceptions : 1;\n"); printf("unsigned int returns_struct : 1;\n"); printf("unsigned int returns_pcc_struct : 1;\n"); printf("unsigned int after_tree_profile : 1;\n"); printf("unsigned int has_local_explicit_reg_vars : 1;\n"); printf("unsigned int is_thunk : 1;\n"); #endif #endif obj = PyGccWrapper_New(struct PyGccFunction, &PyGccFunction_TypeObj); if (!obj) { goto error; } obj->fun = func; return (PyObject*)obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccFunction(PyGccFunction *wrapper) { /* Mark the underlying object (recursing into its fields): */ gcc_function_mark_in_use(wrapper->fun); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-gimple.c000066400000000000000000000246001342215241600205040ustar00rootroot00000000000000/* Copyright 2011-2013, 2015 David Malcolm Copyright 2011-2013, 2015 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-python-compat.h" #include "gcc-python-closure.h" #include "gimple.h" /* gimple_phi_arg_def etc were in tree-flow-inline.h prior to 4.9, when they moved to gimple.h */ #if (GCC_VERSION < 4009) #include "tree-flow.h" #include "tree-flow-inline.h" #endif /* Needed for pp_gimple_stmt_1 for gcc 4.8+; this header didn't exist in gcc 4.6: */ #if (GCC_VERSION >= 4008) #include "gimple-pretty-print.h" #endif #include "gcc-c-api/gcc-gimple.h" /* GCC 4.9 moved struct walk_stmt_info into the new header gimple-walk.h, which in turn needs the new header gimple-iterator.h: */ #if (GCC_VERSION >= 4009) #include "gimple-iterator.h" #include "gimple-walk.h" #endif gcc_gimple_asm PyGccGimple_as_gcc_gimple_asm(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_asm(self->stmt); } gcc_gimple_assign PyGccGimple_as_gcc_gimple_assign(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_assign(self->stmt); } gcc_gimple_call PyGccGimple_as_gcc_gimple_call(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_call(self->stmt); } gcc_gimple_return PyGccGimple_as_gcc_gimple_return(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_return(self->stmt); } gcc_gimple_cond PyGccGimple_as_gcc_gimple_cond(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_cond(self->stmt); } gcc_gimple_phi PyGccGimple_as_gcc_gimple_phi(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_phi(self->stmt); } gcc_gimple_switch PyGccGimple_as_gcc_gimple_switch(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_switch(self->stmt); } gcc_gimple_label PyGccGimple_as_gcc_gimple_label(struct PyGccGimple *self) { return gcc_gimple_as_gcc_gimple_label(self->stmt); } static PyObject * do_pretty_print(struct PyGccGimple * self, int spc, dump_flags_t flags) { PyObject *ppobj = PyGccPrettyPrinter_New(); PyObject *result = NULL; if (!ppobj) { return NULL; } /* gcc 4.8 renamed "dump_gimple_stmt" to "pp_gimple_stmt_1" (in r191884). Declaration is in gimple-pretty-print.h */ #if (GCC_VERSION >= 4008) pp_gimple_stmt_1(PyGccPrettyPrinter_as_pp(ppobj), self->stmt.inner, spc, flags); #else dump_gimple_stmt(PyGccPrettyPrinter_as_pp(ppobj), self->stmt.inner, spc, flags); #endif result = PyGccPrettyPrinter_as_string(ppobj); if (!result) { goto error; } Py_XDECREF(ppobj); return result; error: Py_XDECREF(ppobj); return NULL; } PyObject * PyGccGimple_repr(struct PyGccGimple * self) { return PyGccString_FromFormat("%s()", Py_TYPE(self)->tp_name); } PyObject * PyGccGimple_str(struct PyGccGimple * self) { return do_pretty_print(self, 0, (dump_flags_t)0); } long PyGccGimple_hash(struct PyGccGimple * self) { return (long)self->stmt.inner; } PyObject * PyGccGimple_richcompare(PyObject *o1, PyObject *o2, int op) { struct PyGccGimple *gimpleobj1; struct PyGccGimple *gimpleobj2; int cond; PyObject *result_obj; if (!PyObject_TypeCheck(o1, (PyTypeObject*)&PyGccGimple_TypeObj)) { result_obj = Py_NotImplemented; goto out; } if (!PyObject_TypeCheck(o2, (PyTypeObject*)&PyGccGimple_TypeObj)) { result_obj = Py_NotImplemented; goto out; } gimpleobj1 = (struct PyGccGimple *)o1; gimpleobj2 = (struct PyGccGimple *)o2; switch (op) { case Py_EQ: cond = (gimpleobj1->stmt.inner == gimpleobj2->stmt.inner); break; case Py_NE: cond = (gimpleobj1->stmt.inner != gimpleobj2->stmt.inner); break; default: result_obj = Py_NotImplemented; goto out; } result_obj = cond ? Py_True : Py_False; out: Py_INCREF(result_obj); return result_obj; } static tree gimple_walk_tree_callback(tree *tree_ptr, int *walk_subtrees, void *data) { struct walk_stmt_info *wi = (struct walk_stmt_info*)data; struct callback_closure *closure = (struct callback_closure *)wi->info; PyObject *tree_obj = NULL; PyObject *args = NULL; PyObject *result = NULL; assert(closure); assert(*tree_ptr); tree_obj = PyGccTree_New(gcc_private_make_tree(*tree_ptr)); if (!tree_obj) { goto error; } args = PyGcc_Closure_MakeArgs(closure, 0, tree_obj); if (!args) { goto error; } /* Invoke the python callback: */ result = PyObject_Call(closure->callback, args, closure->kwargs); if (!result) { goto error; } Py_DECREF(tree_obj); Py_DECREF(args); if (PyObject_IsTrue(result)) { Py_DECREF(result); return *tree_ptr; } else { Py_DECREF(result); return NULL; } error: /* On an exception, terminate the traversal: */ *walk_subtrees = 0; Py_XDECREF(tree_obj); Py_XDECREF(args); Py_XDECREF(result); return NULL; } PyObject * PyGccGimple_walk_tree(struct PyGccGimple * self, PyObject *args, PyObject *kwargs) { PyObject *callback; PyObject *extraargs = NULL; struct callback_closure *closure; tree result; struct walk_stmt_info wi; callback = PyTuple_GetItem(args, 0); extraargs = PyTuple_GetSlice(args, 1, PyTuple_Size(args)); closure = PyGcc_closure_new_generic(callback, extraargs, kwargs); if (!closure) { Py_DECREF(callback); Py_DECREF(extraargs); return NULL; } memset(&wi, 0, sizeof(wi)); wi.info = closure; result = walk_gimple_op (self->stmt.inner, gimple_walk_tree_callback, &wi); PyGcc_closure_free(closure); /* Propagate exceptions: */ if (PyErr_Occurred()) { return NULL; } return PyGccTree_New(gcc_private_make_tree(result)); } PyObject * PyGccGimple_get_rhs(struct PyGccGimple *self, void *closure) { PyObject * result = NULL; unsigned int i; assert(gimple_has_ops(self->stmt.inner)); assert(gimple_num_ops(self->stmt.inner) > 0); result = PyList_New(gimple_num_ops (self->stmt.inner) - 1); if (!result) { goto error; } for (i = 1 ; i < gimple_num_ops(self->stmt.inner); i++) { tree t = gimple_op(self->stmt.inner, i); PyObject *obj = PyGccTree_New(gcc_private_make_tree(t)); if (!obj) { goto error; } PyList_SetItem(result, i-1, obj); } return result; error: Py_XDECREF(result); return NULL; } PyObject * PyGccGimple_get_str_no_uid(struct PyGccGimple *self, void *closure) { return do_pretty_print(self, 0, TDF_NOUID); } IMPL_APPENDER(add_tree_to_list, gcc_tree, PyGccTree_New) PyObject * PyGccGimpleCall_get_args(struct PyGccGimple *self, void *closure) { IMPL_LIST_MAKER(gcc_gimple_call_for_each_arg, PyGccGimple_as_gcc_gimple_call(self), add_tree_to_list) } PyObject * PyGccGimpleLabel_repr(PyObject *self) { PyObject *label_repr = NULL; PyObject *result = NULL; label_repr = PyGcc_GetReprOfAttribute(self, "label"); if (!label_repr) { goto cleanup; } result = PyGccString_FromFormat("%s(label=%s)", Py_TYPE(self)->tp_name, PyGccString_AsString(label_repr)); cleanup: Py_XDECREF(label_repr); return result; } PyObject * PyGccGimplePhi_get_args(struct PyGccGimple *self, void *closure) { /* See e.g. gimple-pretty-print.c:dump_gimple_phi */ PyObject * result = NULL; int num_args = gimple_phi_num_args (self->stmt.inner); int i; result = PyList_New(num_args); if (!result) { goto error; } for (i = 0 ; i < num_args; i++) { tree arg_def = gimple_phi_arg_def(self->stmt.inner, i); edge arg_edge = gimple_phi_arg_edge(AS_A_GPHI(self->stmt.inner), i); /* fwiw, there's also gimple_phi_arg_has_location and gimple_phi_arg_location */ PyObject *tuple_obj; tuple_obj = Py_BuildValue("O&O&", PyGccTree_New, arg_def, PyGccEdge_New, arg_edge); if (!tuple_obj) { goto error; } PyList_SET_ITEM(result, i, tuple_obj); } return result; error: Py_XDECREF(result); return NULL; } IMPL_APPENDER(add_case_label_expr_to_list, gcc_case_label_expr, PyGccCaseLabelExpr_New) PyObject * PyGccGimpleSwitch_get_labels(struct PyGccGimple *self, void *closure) { IMPL_LIST_MAKER(gcc_gimple_switch_for_each_label, PyGccGimple_as_gcc_gimple_switch(self), add_case_label_expr_to_list) } /* Ensure we have a unique PyGccGimple per gimple address (by maintaining a dict): */ static PyObject *gimple_wrapper_cache = NULL; union gcc_gimple_or_ptr { gcc_gimple stmt; void *ptr; }; static PyObject * real_make_gimple_wrapper(void *ptr) { union gcc_gimple_or_ptr u; u.ptr = ptr; struct PyGccGimple *gimple_obj = NULL; PyGccWrapperTypeObject* tp; tp = PyGcc_autogenerated_gimple_type_for_stmt(u.stmt); assert(tp); //printf("tp:%p\n", tp); gimple_obj = PyGccWrapper_New(struct PyGccGimple, tp); if (!gimple_obj) { goto error; } gimple_obj->stmt = u.stmt; return (PyObject*)gimple_obj; error: return NULL; } PyObject* PyGccGimple_New(gcc_gimple stmt) { union gcc_gimple_or_ptr u; u.stmt = stmt; return PyGcc_LazilyCreateWrapper(&gimple_wrapper_cache, u.ptr, real_make_gimple_wrapper); } void PyGcc_WrtpMarkForPyGccGimple(PyGccGimple *wrapper) { gcc_gimple_mark_in_use(wrapper->stmt); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-location.c000066400000000000000000000173201342215241600210400ustar00rootroot00000000000000/* Copyright 2011-2013, 2017 David Malcolm Copyright 2011-2013, 2017 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-c-api/gcc-location.h" /* Wrapper for GCC's "location_t" GCC's input.h has: typedef source_location location_t; GCC's line-map.h has: A logical line/column number, i.e. an "index" into a line_map: typedef unsigned int source_location; */ #if (GCC_VERSION >= 7000) int PyGccLocation_init(PyGccLocation *self, PyObject *args, PyObject *kwargs) { const char *keywords[] = {"caret", "start", "finish", NULL}; PyGccLocation *caret_obj; PyGccLocation *start_obj; PyGccLocation *finish_obj; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!O!", (char**)keywords, &PyGccLocation_TypeObj, &caret_obj, &PyGccLocation_TypeObj, &start_obj, &PyGccLocation_TypeObj, &finish_obj)) { return -1; } self->loc = gcc_private_make_location (make_location (caret_obj->loc.inner, start_obj->loc.inner, finish_obj->loc.inner)); return 0; } #endif PyObject * PyGccLocation_repr(struct PyGccLocation * self) { return PyGccString_FromFormat("gcc.Location(file='%s', line=%i)", gcc_location_get_filename(self->loc), gcc_location_get_line(self->loc)); } PyObject * PyGccLocation_str(struct PyGccLocation * self) { return PyGccString_FromFormat("%s:%i", gcc_location_get_filename(self->loc), gcc_location_get_line(self->loc)); } PyObject * PyGccLocation_richcompare(PyObject *o1, PyObject *o2, int op) { struct PyGccLocation *locobj1; struct PyGccLocation *locobj2; int cond; PyObject *result_obj; const char *file1; const char *file2; if (Py_TYPE(o1) != (PyTypeObject*)&PyGccLocation_TypeObj) { result_obj = Py_NotImplemented; goto out; } if (Py_TYPE(o2) != (PyTypeObject*)&PyGccLocation_TypeObj) { result_obj = Py_NotImplemented; goto out; } locobj1 = (struct PyGccLocation *)o1; locobj2 = (struct PyGccLocation *)o2; /* First compare by filename, then by line, then by column */ file1 = gcc_location_get_filename(locobj1->loc); file2 = gcc_location_get_filename(locobj2->loc); if (file1 != file2) { /* Compare by file: */ switch (op) { case Py_LT: case Py_LE: /* we merge the LT and LE cases since we've already established that the values are not equal */ cond = (strcmp(file1, file2) < 0); break; case Py_GT: case Py_GE: cond = (strcmp(file1, file2) > 0); break; case Py_EQ: cond = 0; break; case Py_NE: cond = 1; break; default: result_obj = Py_NotImplemented; goto out; } } else { /* File equality; compare by line: */ int line1 = gcc_location_get_line(locobj1->loc); int line2 = gcc_location_get_line(locobj2->loc); if (line1 != line2) { switch (op) { case Py_LT: case Py_LE: cond = (line1 < line2); break; case Py_GT: case Py_GE: cond = (line1 > line2); break; case Py_EQ: cond = 0; break; case Py_NE: cond = 1; break; default: result_obj = Py_NotImplemented; goto out; } } else { /* File and line equality; compare by column: */ int col1 = gcc_location_get_column(locobj1->loc); int col2 = gcc_location_get_column(locobj2->loc); switch (op) { case Py_LT: case Py_LE: cond = (col1 < col2); break; case Py_GT: case Py_GE: cond = (col1 > col2); break; case Py_EQ: cond = (col1 == col2); break; case Py_NE: cond = (col1 != col2); break; default: result_obj = Py_NotImplemented; goto out; } } } result_obj = cond ? Py_True : Py_False; out: Py_INCREF(result_obj); return result_obj; } long PyGccLocation_hash(struct PyGccLocation * self) { return self->loc.inner; } #if (GCC_VERSION >= 5000) PyObject * PyGccLocation_offset_column(PyGccLocation *self, PyObject *args) { int offset; if (!PyArg_ParseTuple(args, "i", &offset)) { return NULL; } return PyGccLocation_New(gcc_location_offset_column(self->loc, offset)); } #endif /* #if (GCC_VERSION >= 5000) */ PyObject * PyGccLocation_New(gcc_location loc) { struct PyGccLocation *location_obj = NULL; if (gcc_location_is_unknown(loc)) { Py_RETURN_NONE; } location_obj = PyGccWrapper_New(struct PyGccLocation, &PyGccLocation_TypeObj); if (!location_obj) { goto error; } location_obj->loc = loc; return (PyObject*)location_obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccLocation(PyGccLocation *wrapper) { /* empty */ } /* rich_location. */ #if (GCC_VERSION >= 6000) PyObject * PyGccRichLocation_add_fixit_replace(PyGccRichLocation *self, PyObject *args, PyObject *kwargs) { const char *keywords[] = {"new_content", NULL}; const char *new_content; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", (char**)keywords, &new_content)) { return NULL; } self->richloc.add_fixit_replace (get_range_from_loc (line_table, self->richloc.get_loc (0)), new_content); Py_RETURN_NONE; } int PyGccRichLocation_init(PyGccRichLocation *self, PyObject *args, PyObject *kwargs) { const char *keywords[] = {"location", NULL}; PyGccLocation *loc_obj; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", (char**)keywords, &PyGccLocation_TypeObj, &loc_obj)) { return -1; } // FIXME: also need a manual dtor call new (&self->richloc) rich_location (line_table, loc_obj->loc.inner); return 0; } void PyGcc_WrtpMarkForPyGccRichLocation(PyGccRichLocation *wrapper) { /* empty */ } #endif /* #if (GCC_VERSION >= 6000) */ /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-option.c000066400000000000000000000121651342215241600205420ustar00rootroot00000000000000/* Copyright 2011, 2012, 2013 David Malcolm Copyright 2011, 2012, 2013 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "c-common.h" /* for warn_format */ #include "diagnostic.h" /* Wrapper for GCC's opts.h opts.h declares: extern const struct cl_option cl_options[]; extern const unsigned int cl_options_count; which suggests that this table is fixed in place and thus not resizable. (The definition is in the autogenerated DIR/gcc/options.c) We specifically wrap: enum opt_code and use this to get at the associated "struct cl_option" within the "cl_options" table. */ int PyGccOption_init(PyGccOption * self, PyObject *args, PyObject *kwargs) { const char *text; static const char *kwlist[] = {"text", NULL}; unsigned int i; /* We need to call _track manually as we're not using PyGccWrapper_New(): */ PyGccWrapper_Track(&self->head); if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", (char**)kwlist, &text)) { return -1; } /* Search for text within cl_options */ for (i = 0; i < cl_options_count; i++) { if (0 == strcmp(cl_options[i].opt_text, text)) { self->opt = gcc_private_make_option((enum opt_code)i); return 0; /* success */ } } /* Not found: */ PyErr_Format(PyExc_ValueError, "Could not find command line argument with text '%s'", text); return -1; } PyObject * PyGccOption_repr(PyGccOption * self) { return PyGccString_FromFormat("gcc.Option('%s')", PyGcc_option_to_cl_option(self)->opt_text); } /* In GCC 4.6 and 4.7, "warn_format" is a global, declared in c-family/c-common.h In GCC 4.8, it became a macro in options.h to: #define warn_format global_options.x_warn_format */ #if (GCC_VERSION < 4008) /* Weakly import warn_format; it's not available in lto1 (during link-time optimization) */ __typeof__ (warn_format) warn_format __attribute__ ((weak)); #endif int PyGcc_option_is_enabled(enum opt_code opt_code) { /* Returns 1 if option OPT_IDX is enabled in OPTS, 0 if it is disabled, or -1 if it isn't a simple on-off switch. */ int i = option_enabled (opt_code, global_dc->option_state); if (i == 1) { return 1; } if (i == 0) { return 0; } /* -1: we don't know */ /* Ugly workaround to allow disabling warnings. For many options, it doesn't seem to be possible to disable them directly. Specifically a cl_option with o.flag_var_offset == -1 will return NULL from option_flag_var() For these options, option_enabled() will return -1 signifying that "it isn't a simple on-off switch". diagnostic_report_diagnostic() uses if (!option_enabled(...)) return false to suppress disabled warnings. However, -1 is true for the purpose of this test. For GCC's own uses of the options, they are typically guarded by an additional test. For example, "-Wformat" sets "warn_format", and this guards the formatting tests. */ switch (opt_code) { default: /* We don't know: */ return -1; #if (GCC_VERSION >= 4008) case OPT_Wformat_: #else case OPT_Wformat: #endif return warn_format; } } PyObject * PyGccOption_is_enabled(PyGccOption * self, void *closure) { int i = PyGcc_option_is_enabled(self->opt.inner); if (i == 1) { return PyBool_FromLong(1); } if (i == 0) { return PyBool_FromLong(0); } PyErr_Format(PyExc_NotImplementedError, "The plugin does not know how to determine if gcc.Format('%s') is implemented", PyGcc_option_to_cl_option(self)->opt_text); return NULL; } const struct cl_option* PyGcc_option_to_cl_option(PyGccOption * self) { assert(self); assert(self->opt.inner >= 0); assert(self->opt.inner < cl_options_count); return &cl_options[self->opt.inner]; } PyObject * PyGccOption_New(gcc_option opt) { struct PyGccOption *opt_obj = NULL; opt_obj = PyGccWrapper_New(struct PyGccOption, &PyGccOption_TypeObj); if (!opt_obj) { goto error; } opt_obj->opt = opt; return (PyObject*)opt_obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccOption(PyGccOption *wrapper) { /* empty */ } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-parameter.c000066400000000000000000000027021342215241600212060ustar00rootroot00000000000000/* Copyright 2011, 2012 David Malcolm Copyright 2011, 2012 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" /* Wrapper for GCC's params.h. We specifically wrap "compiler_param" (a typedef to an enum) and use this to get at the associated "param_info" struct */ PyObject * PyGccParameter_New(compiler_param param_num) { struct PyGccParameter *param_obj = NULL; param_obj = PyGccWrapper_New(struct PyGccParameter, &PyGccParameter_TypeObj); if (!param_obj) { goto error; } param_obj->param_num = param_num; return (PyObject*)param_obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccParameter(PyGccParameter *wrapper) { /* empty */ } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-pass.c000066400000000000000000000463371342215241600202100ustar00rootroot00000000000000/* Copyright 2011-2013, 2015 David Malcolm Copyright 2011-2013, 2015 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "diagnostic.h" #include "gcc-c-api/gcc-function.h" #include "gcc-c-api/gcc-location.h" #if (GCC_VERSION >= 4009) #include "context.h" #include "pass_manager.h" #endif /* Wrapper for GCC's (opt_pass *) */ /* Ensure we have a unique PyGccPass per pass address (by maintaining a dict) For passes defined in Python, this dictionary maps from long (struct opt_pass *) to the gcc.Pass wrapper object for that pass The references on the right-hand-side keep these wrappers alive */ static PyObject *pass_wrapper_cache = NULL; static bool impl_gate(function *fun) { PyObject *pass_obj; PyObject *cfun_obj = NULL; PyObject* result_obj; int result; gcc_location saved_loc = gcc_get_input_location(); /* It appears that current_pass is not set by when gcc (4.7 at least) when it invokes gate for an IPA_PASS within execute_ipa_summary_passes (in gcc/passes.c), so we don't have a way of figuring out which pass we were called on. Reported as http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54959 */ if (NULL == current_pass) { return true; } assert(current_pass); pass_obj = PyGccPass_New(current_pass); assert(pass_obj); /* we own a ref at this point */ if (!PyObject_HasAttrString(pass_obj, "gate")) { /* No "gate" method? Always execute this pass: */ Py_DECREF(pass_obj); return true; } /* Supply the function, if any */ if (fun) { assert (fun == cfun); gcc_function cf = gcc_get_current_function(); /* Temporarily override input_location to the top of the function: */ gcc_set_input_location(gcc_function_get_start(cf)); cfun_obj = PyGccFunction_New(cf); if (!cfun_obj) { PyGcc_PrintException("Unhandled Python exception raised calling 'gate' method"); Py_DECREF(pass_obj); gcc_set_input_location(saved_loc); return false; } result_obj = PyObject_CallMethod(pass_obj, (char*)"gate", (char*)"O", cfun_obj, NULL); } else { result_obj = PyObject_CallMethod(pass_obj, (char*)"gate", NULL); } Py_XDECREF(cfun_obj); Py_DECREF(pass_obj); if (!result_obj) { PyGcc_PrintException("Unhandled Python exception raised calling 'gate' method"); gcc_set_input_location(saved_loc); return false; } result = PyObject_IsTrue(result_obj); Py_DECREF(result_obj); gcc_set_input_location(saved_loc); return result; } static unsigned int impl_execute(function *fun) { PyObject *pass_obj; PyObject *cfun_obj = NULL; PyObject* result_obj; gcc_location saved_loc = gcc_get_input_location(); assert(current_pass); pass_obj = PyGccPass_New(current_pass); assert(pass_obj); /* we own a ref at this point */ if (fun) { assert (fun == cfun); gcc_function cf = gcc_get_current_function(); /* Temporarily override input_location to the top of the function: */ gcc_set_input_location(gcc_function_get_start(cf)); cfun_obj = PyGccFunction_New(cf); if (!cfun_obj) { PyGcc_PrintException("Unhandled Python exception raised calling 'execute' method"); Py_DECREF(pass_obj); gcc_set_input_location(saved_loc); return false; } result_obj = PyObject_CallMethod(pass_obj, (char*)"execute", (char*)"O", cfun_obj, NULL); } else { result_obj = PyObject_CallMethod(pass_obj, (char*)"execute", NULL); } Py_XDECREF(cfun_obj); Py_DECREF(pass_obj); if (!result_obj) { PyGcc_PrintException("Unhandled Python exception raised calling 'execute' method"); gcc_set_input_location(saved_loc); return 0; } if (result_obj == Py_None) { Py_DECREF(result_obj); gcc_set_input_location(saved_loc); return 0; } #if PY_MAJOR_VERSION < 3 if (PyInt_Check(result_obj)) { long result = PyInt_AS_LONG(result_obj); Py_DECREF(result_obj); gcc_set_input_location(saved_loc); return result; } #endif if (PyLong_Check(result_obj)) { long result = PyLong_AsLong(result_obj); Py_DECREF(result_obj); gcc_set_input_location(saved_loc); return result; } PyErr_Format(PyExc_TypeError, "execute returned a non-integer" \ "(type %.200s)", Py_TYPE(result_obj)->tp_name); Py_DECREF(result_obj); PyGcc_PrintException("Unhandled Python exception raised calling 'execute' method"); gcc_set_input_location(saved_loc); return 0; } #if (GCC_VERSION >= 4009) /* GCC 4.9 converted passes to a C++ class hierarchy, with methods for gate and execute. */ #if (GCC_VERSION >= 5000) /* GCC 5 added a "fun" param to the "gate" and "execute" vfuncs of pass opt_pass. */ # define PASS_DECLARE_GATE_AND_EXECUTE \ bool gate (function *fun) { return impl_gate(fun); } \ unsigned int execute (function *fun) { return impl_execute(fun); } #else /* ...whereas in GCC 4.9 they took no params, with cfun being implied. */ # define PASS_DECLARE_GATE_AND_EXECUTE \ bool gate () { return impl_gate(cfun); } \ unsigned int execute () { return impl_execute(cfun); } #endif /* #if (GCC_VERSION >= 5000) */ class PyGccGimplePass : public gimple_opt_pass { public: PyGccGimplePass(const pass_data& data, gcc::context *ctxt) : gimple_opt_pass(data, ctxt) { } PASS_DECLARE_GATE_AND_EXECUTE opt_pass *clone() {return this; } }; class PyGccRtlPass : public rtl_opt_pass { public: PyGccRtlPass (const pass_data& data, gcc::context *ctxt) : rtl_opt_pass (data, ctxt) { } PASS_DECLARE_GATE_AND_EXECUTE opt_pass *clone() {return this; } }; class PyGccIpaPass : public ipa_opt_pass_d { public: PyGccIpaPass(const pass_data& data, gcc::context *ctxt) : ipa_opt_pass_d (data, ctxt, NULL, /* void (*generate_summary) (void), */ NULL, /* void (*write_summary) (void), */ NULL, /* void (*read_summary) (void), */ NULL, /* void (*write_optimization_summary) (void), */ NULL, /* void (*read_optimization_summary) (void), */ NULL, /* void (*stmt_fixup) (struct cgraph_node *, gimple *), */ 0, /* unsigned int function_transform_todo_flags_start, */ NULL, /* unsigned int (*function_transform) (struct cgraph_node *), */ NULL) /* void (*variable_transform) (struct varpool_node *)) */ { } PASS_DECLARE_GATE_AND_EXECUTE opt_pass *clone() {return this; } }; class PyGccSimpleIpaPass : public simple_ipa_opt_pass { public: PyGccSimpleIpaPass(const pass_data& data, gcc::context *ctxt) : simple_ipa_opt_pass(data, ctxt) { } PASS_DECLARE_GATE_AND_EXECUTE opt_pass *clone() {return this; } }; #else /* #if (GCC_VERSION >= 4009) */ /* Before GCC 4.9, passes were implemented using callback functions. */ static bool gate_cb(void) { return impl_gate(cfun); } static unsigned int execute_cb(void) { return impl_execute(cfun); } #endif /* #else clause of if (GCC_VERSION >= 4009) */ static int do_pass_init(PyObject *s, PyObject *args, PyObject *kwargs, enum opt_pass_type pass_type, size_t sizeof_pass) { struct PyGccPass *self = (struct PyGccPass *)s; const char *name; const char *keywords[] = {"name", NULL}; struct opt_pass *pass; /* We need to call _track manually as we're not using PyGccWrapper_New(): */ PyGccWrapper_Track(&self->head); if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:gcc.Pass.__init__", (char**)keywords, &name)) { return -1; } #if (GCC_VERSION >= 4009) pass_data pass_data; memset(&pass_data, 0, sizeof(pass_data)); pass_data.type = pass_type; pass_data.name = PyGcc_strdup(name); #if (GCC_VERSION < 5000) pass_data.has_gate = true; pass_data.has_execute = true; #endif switch (pass_type) { case GIMPLE_PASS: pass = new PyGccGimplePass (pass_data, g); break; case RTL_PASS: pass = new PyGccRtlPass (pass_data, g); break; case SIMPLE_IPA_PASS: pass = new PyGccSimpleIpaPass (pass_data, g); break; case IPA_PASS: pass = new PyGccIpaPass (pass_data, g); break; default: gcc_unreachable(); } #else /* #if (GCC_VERSION >= 4009) */ pass = (struct opt_pass*)PyMem_Malloc(sizeof_pass); if (!pass) { return -1; } memset(pass, 0, sizeof_pass); pass->type = pass_type; pass->name = PyGcc_strdup(name); /* does the name need to be unique? mapping from opt_pass ptr to callable? (the ID (as a long) is unique) */ if (!pass->name) { PyMem_Free(pass); return -1; } pass->gate = gate_cb; pass->execute = execute_cb; #endif /* ending the #else clause of #if (GCC_VERSION >= 4009) */ if (PyGcc_insert_new_wrapper_into_cache(&pass_wrapper_cache, pass, s)) { return -1; } self->pass = pass; return 0; // FIXME } int PyGccGimplePass_init(PyObject *self, PyObject *args, PyObject *kwds) { return do_pass_init(self, args, kwds, GIMPLE_PASS, sizeof(struct gimple_opt_pass)); } int PyGccRtlPass_init(PyObject *self, PyObject *args, PyObject *kwds) { return do_pass_init(self, args, kwds, RTL_PASS, sizeof(struct rtl_opt_pass)); } int PyGccSimpleIpaPass_init(PyObject *self, PyObject *args, PyObject *kwds) { return do_pass_init(self, args, kwds, SIMPLE_IPA_PASS, sizeof(struct simple_ipa_opt_pass)); } int PyGccIpaPass_init(PyObject *self, PyObject *args, PyObject *kwds) { return do_pass_init(self, args, kwds, IPA_PASS, sizeof(struct ipa_opt_pass_d)); } PyObject * PyGccPass_repr(struct PyGccPass *self) { return PyGccString_FromFormat("%s(name='%s')", Py_TYPE(self)->tp_name, self->pass->name); } #if (GCC_VERSION >= 4009) static struct dump_file_info * get_dump_file_info(int phase) { gcc::dump_manager *dumps = g->get_dumps (); return dumps->get_dump_file_info (phase); } #endif /* In GCC 4.8, dump_enabled_p changed from acting on one pass to the current phase (as of r192773), and dump_phase_enabled_p() was added (as of r192692). Unfortunately, dump_phase_enabled_p() is static within gcc/dumpfile.c, so we use a copy of the 2 lines of relevant code: */ static bool is_dump_enabled(struct opt_pass *pass) { #if (GCC_VERSION >= 4008) struct dump_file_info *dfi = get_dump_file_info(pass->static_pass_number); return dfi->pstate || dfi->alt_state; #else return dump_enabled_p(pass->static_pass_number); #endif } PyObject * PyGccPass_get_dump_enabled(struct PyGccPass *self, void *closure) { return PyBool_FromLong(is_dump_enabled(self->pass)); } /* In GCC 4.8, this field became "pstate" */ #if (GCC_VERSION >= 4008) #define DFI_STATE(dfi) (dfi)->pstate #else #define DFI_STATE(dfi) (dfi)->state #endif int PyGccPass_set_dump_enabled(struct PyGccPass *self, PyObject *value, void *closure) { struct dump_file_info *dfi = get_dump_file_info (self->pass->static_pass_number); assert(dfi); int newbool = PyObject_IsTrue(value); if (newbool == -1) { return -1; } if (DFI_STATE (dfi) == 0) { /* Dumping was disabled: */ if (newbool) { /* Enabling: */ DFI_STATE (dfi) = -1; return 0; } else { /* No change: */ return 0; } } else { if (DFI_STATE (dfi) < 0) { /* Dumping was enabled but has not yet started */ if (newbool) { /* No change: */ return 0; } else { /* Disabling: */ DFI_STATE (dfi) = 0; return 0; } } else { assert(DFI_STATE (dfi) > 0); /* Dumping was enabled and has already started */ if (newbool) { /* No change: */ return 0; } else { /* Can't disable after it's started: */ PyErr_SetString(PyExc_RuntimeError, "Can't disable dumping: already started"); return -1; } } } } /* In GCC 4.9, passes moved from being globals to fields of the pass_manager. */ #if (GCC_VERSION >= 4009) #define GET_PASS_LIST(PASS_NAME) (g->get_passes()->PASS_NAME) #else #define GET_PASS_LIST(PASS_NAME) (PASS_NAME) #endif PyObject * PyGccPass_get_roots(PyObject *cls, PyObject *noargs) { /* There are 5 "roots" for the pass tree; see gcc/passes.c */ PyObject *result; PyObject *passobj; result = PyTuple_New(5); if (!result) { goto error; } #define SET_PASS(IDX, PASS_NAME) \ passobj = PyGccPass_New(GET_PASS_LIST(PASS_NAME)); \ if (!passobj) goto error; \ PyTuple_SET_ITEM(result, IDX, passobj); \ (void)0; SET_PASS(0, all_lowering_passes); SET_PASS(1, all_small_ipa_passes); SET_PASS(2, all_regular_ipa_passes); /* all_late_ipa_passes appeared in r175336 */ /* r204984 eliminated all_lto_gen_passes */ #if (GCC_VERSION >= 4009) SET_PASS(3, all_late_ipa_passes); #else SET_PASS(3, all_lto_gen_passes); #endif SET_PASS(4, all_passes); return result; error: Py_XDECREF(result); return NULL; } static struct opt_pass * find_pass_by_name(const char *name, struct opt_pass *pass_list) { struct opt_pass *pass; for (pass = pass_list; pass; pass = pass->next) { if (pass->name && !strcmp (name, pass->name)) { /* Found: */ return pass; } if (pass->sub) { /* Recurse: */ struct opt_pass *result = find_pass_by_name(name, pass->sub); if (result) { return result; } } } /* Not found: */ return NULL; } PyObject * PyGccPass_get_by_name(PyObject *cls, PyObject *args, PyObject *kwargs) { const char *name; const char *keywords[] = {"name", NULL}; struct opt_pass *result; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:get_by_name", (char**)keywords, &name)) { return NULL; } #define SEARCH_WITHIN_LIST(PASS_LIST) \ result = find_pass_by_name(name, (GET_PASS_LIST(PASS_LIST))); \ if (result) { \ return PyGccPass_New(result); \ } SEARCH_WITHIN_LIST(all_lowering_passes); SEARCH_WITHIN_LIST(all_small_ipa_passes); SEARCH_WITHIN_LIST(all_regular_ipa_passes); /* all_late_ipa_passes appeared in r175336 */ /* r204984 eliminated all_lto_gen_passes */ #if (GCC_VERSION >= 4009) SEARCH_WITHIN_LIST(all_late_ipa_passes); #else SEARCH_WITHIN_LIST(all_lto_gen_passes); #endif SEARCH_WITHIN_LIST(all_passes); /* Not found: */ PyErr_Format(PyExc_ValueError, "pass named '%s' not found", name); return NULL; } static PyObject * impl_register(struct PyGccPass *self, PyObject *args, PyObject *kwargs, enum pass_positioning_ops pos_op, const char *arg_format) { struct register_pass_info rpi; const char *keywords[] = {"name", "instance_number", NULL}; rpi.pass = self->pass; rpi.pos_op = pos_op; rpi.ref_pass_instance_number = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, arg_format, (char**)keywords, &rpi.reference_pass_name, &rpi.ref_pass_instance_number)) { return NULL; } /* (Failures lead to a fatal error) */ register_pass (&rpi); Py_RETURN_NONE; } PyObject * PyGccPass_register_before(struct PyGccPass *self, PyObject *args, PyObject *kwargs) { return impl_register(self, args, kwargs, PASS_POS_INSERT_BEFORE, "s|i:register_before"); } PyObject * PyGccPass_register_after(struct PyGccPass *self, PyObject *args, PyObject *kwargs) { return impl_register(self, args, kwargs, PASS_POS_INSERT_AFTER, "s|i:register_after"); } PyObject * PyGccPass_replace(struct PyGccPass *self, PyObject *args, PyObject *kwargs) { return impl_register(self, args, kwargs, PASS_POS_REPLACE, "s|i:replace"); } static PyGccWrapperTypeObject * get_type_for_pass_type(enum opt_pass_type pt) { switch (pt) { default: assert(0); case GIMPLE_PASS: return &PyGccGimplePass_TypeObj; case RTL_PASS: return &PyGccRtlPass_TypeObj; case SIMPLE_IPA_PASS: return &PyGccSimpleIpaPass_TypeObj; case IPA_PASS: return &PyGccIpaPass_TypeObj; } }; static PyObject * real_make_pass_wrapper(void *p) { struct opt_pass *pass = (struct opt_pass *)p; PyGccWrapperTypeObject *type_obj; struct PyGccPass *pass_obj = NULL; if (NULL == pass) { Py_RETURN_NONE; } type_obj = get_type_for_pass_type(pass->type); pass_obj = PyGccWrapper_New(struct PyGccPass, type_obj); if (!pass_obj) { goto error; } pass_obj->pass = pass; /* FIXME: do we need to do something for the GCC GC? */ return (PyObject*)pass_obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccPass(PyGccPass *wrapper) { /* This function is empty: struct opt_pass does not have a GTY() and any (struct opt_pass*) is either statically-allocated, or allocated by us within do_pass_init using PyMem_Malloc */ } PyObject * PyGccPass_New(struct opt_pass *pass) { return PyGcc_LazilyCreateWrapper(&pass_wrapper_cache, pass, real_make_pass_wrapper); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-plugin.spec000066400000000000000000000230011342215241600212270ustar00rootroot00000000000000Name: gcc-python-plugin Version: 0.17 Release: 1%{?dist} Summary: GCC plugin that embeds Python Group: Development/Languages License: GPLv3+ URL: https://fedorahosted.org/gcc-python-plugin/ Source0: https://fedorahosted.org/releases/g/c/gcc-python-plugin/gcc-python-plugin-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: gcc-plugin-devel # gcc 4.6.1's plugin/include/double-int.h includes "gmp.h", but on Fedora, # gmp-devel isn't yet listed in the requirements of gcc-plugin-devel # For now, explicitly require it: BuildRequires: gmp-devel # Filed as https://bugzilla.redhat.com/show_bug.cgi?id=725569 # Various python runtimes to build the plugin against: BuildRequires: python-devel BuildRequires: python-debug BuildRequires: python3-devel BuildRequires: python3-debug # "six" is used at buildtime: BuildRequires: python-six BuildRequires: python3-six # sphinx is used for building documentation: BuildRequires: python-sphinx # pygments is used when running the selftests: BuildRequires: python-pygments BuildRequires: python3-pygments # lxml is used when running the selftests: BuildRequires: python-lxml BuildRequires: python3-lxml %global gcc_plugins_dir %(gcc --print-file-name=plugin) %description Plugins for embedding various versions of Python within GCC %package -n gcc-python-plugin-c-api Summary: Shared library to make it easier to write GCC plugins Group: Development/Languages %description -n gcc-python-plugin-c-api Shared library to make it easier to write GCC plugins %package -n gcc-python2-plugin Summary: GCC plugin embedding Python 2 Group: Development/Languages Requires: python-six Requires: python-pygments Requires: python-lxml Requires: gcc-python-plugin-c-api%{?_isa} = %{version}-%{release} %description -n gcc-python2-plugin GCC plugin embedding Python 2 %package -n gcc-python3-plugin Summary: GCC plugin embedding Python 3 Group: Development/Languages Requires: python3-six Requires: python3-pygments Requires: python3-lxml Requires: gcc-python-plugin-c-api%{?_isa} = %{version}-%{release} %description -n gcc-python3-plugin GCC plugin embedding Python 3 %package -n gcc-python2-debug-plugin Summary: GCC plugin embedding Python 2 debug build Group: Development/Languages Requires: python-six Requires: python-pygments Requires: python-lxml Requires: gcc-python-plugin-c-api%{?_isa} = %{version}-%{release} %description -n gcc-python2-debug-plugin GCC plugin embedding debug build of Python 2 %package -n gcc-python3-debug-plugin Summary: GCC plugin embedding Python 3 debug build Group: Development/Languages Requires: python3-six Requires: python3-pygments Requires: python3-lxml Requires: gcc-python-plugin-c-api%{?_isa} = %{version}-%{release} %description -n gcc-python3-debug-plugin GCC plugin embedding debug build of Python 3 %package docs Summary: API documentation for the GCC Python plugin Group: Development/Languages %description docs This package contains API documentation for the GCC Python plugin %prep %setup -q # We will be building the plugin 4 times, each time against a different # Python runtime # # The plugin doesn't yet cleanly support srcdir != builddir, so for now # make 4 separate copies of the source, once for each build PrepPlugin() { PluginName=$1 BuildDir=../gcc-python-plugin-%{version}-building-for-$PluginName rm -rf $BuildDir cp -a . $BuildDir } PrepPlugin \ python2 PrepPlugin \ python2_debug PrepPlugin \ python3 PrepPlugin \ python3_debug %build BuildPlugin() { PythonExe=$1 PythonConfig=$2 PluginDso=$3 PluginName=$4 BuildDir=../gcc-python-plugin-%{version}-building-for-$PluginName pushd $BuildDir make \ %{?_smp_mflags} \ PLUGIN_NAME=$PluginName \ PLUGIN_DSO=$PluginDso \ PYTHON=$PythonExe \ PYTHON_CONFIG=$PythonConfig \ PLUGIN_PYTHONPATH=%{gcc_plugins_dir}/$PluginName \ plugin print-gcc-version popd } BuildPlugin \ python \ python-config \ python2.so \ python2 BuildPlugin \ python-debug \ python-debug-config \ python2_debug.so \ python2_debug BuildPlugin \ python3 \ python3-config \ python3.so \ python3 BuildPlugin \ python3-debug \ /usr/bin/python3.?dm-config \ python3_debug.so \ python3_debug # Documentation: cd docs make html # Avoid having a hidden file in the payload: rm _build/html/.buildinfo make man %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/%{gcc_plugins_dir} mkdir -p $RPM_BUILD_ROOT/%{_bindir} mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man1 InstallPlugin() { PythonExe=$1 PythonConfig=$2 PluginDso=$3 PluginName=$4 BuildDir=../gcc-python-plugin-%{version}-building-for-$PluginName pushd $BuildDir make install \ DESTDIR=$RPM_BUILD_ROOT \ PLUGIN_NAME=$PluginName \ PLUGIN_DSO=$PluginDso popd # (doing the above actually installs each build's copy of libgccapi.so, # all to the same location, but they should all be identical, so that's # OK) } InstallPlugin \ python \ python-config \ python2.so \ python2 InstallPlugin \ python-debug \ python-debug-config \ python2_debug.so \ python2_debug InstallPlugin \ python3 \ python3-config \ python3.so \ python3 InstallPlugin \ python3-debug \ python3.4dm-config \ python3_debug.so \ python3_debug %clean rm -rf $RPM_BUILD_ROOT %check CheckPlugin() { PythonExe=$1 PythonConfig=$2 PluginDso=$3 PluginName=$4 SelftestArgs=$5 BuildDir=../gcc-python-plugin-%{version}-building-for-$PluginName pushd $BuildDir # Run the selftests: LD_LIBRARY_PATH=gcc-c-api \ PLUGIN_NAME=$PluginName \ $PythonExe run-test-suite.py $SelftestArgs LD_LIBRARY_PATH=gcc-c-api \ PLUGIN_NAME=$PluginName \ $PythonExe testcpychecker.py -v popd } # Selftest for python2 (optimized) build # All tests ought to pass: CheckPlugin \ python \ python-config \ python2.so \ python2 \ %{nil} # Selftest for python2-debug build: # Disable the cpychecker tests for now: somewhat ironically, the extra # instrumentation in the debug build breaks the selftests for the refcount # tracker. (specifically, handling of _Py_RefTotal): # # Failed tests: # tests/cpychecker/refcounts/correct_py_none # tests/cpychecker/refcounts/correct_decref # tests/cpychecker/refcounts/use_after_dealloc # tests/cpychecker/refcounts/returning_dead_object # tests/cpychecker/refcounts/too_many_increfs # tests/cpychecker/refcounts/loop_n_times # CheckPlugin \ python-debug \ python-debug-config \ python2_debug.so \ python2_debug \ "-x tests/cpychecker" # Selftest for python3 (optimized) build: # Disable the cpychecker tests for now: # Failed tests: # tests/cpychecker/PyArg_ParseTuple/incorrect_codes_S_and_U # tests/cpychecker/PyArg_ParseTuple/correct_codes_S_and_U # tests/cpychecker/refcounts/correct_decref # tests/cpychecker/refcounts/fold_conditional # tests/cpychecker/refcounts/use_after_dealloc # tests/cpychecker/refcounts/missing_decref # tests/cpychecker/refcounts/returning_dead_object # tests/cpychecker/refcounts/too_many_increfs # tests/cpychecker/refcounts/loop_n_times # CheckPlugin \ python3 \ python3-config \ python3.so \ python3 \ "-x tests/cpychecker" # Selftest for python3-debug build: # (shares the issues of the above) CheckPlugin \ python3-debug \ python3.4dm-config \ python3_debug.so \ python3_debug \ "-x tests/cpychecker" %files -n gcc-python-plugin-c-api %{gcc_plugins_dir}/libgcc-c-api.so %files -n gcc-python2-plugin %defattr(-,root,root,-) %doc COPYING README.rst %{_bindir}/gcc-with-python2 %{gcc_plugins_dir}/python2.so %{gcc_plugins_dir}/python2 %doc %{_mandir}/man1/gcc-with-python2.1.gz %files -n gcc-python3-plugin %defattr(-,root,root,-) %doc COPYING README.rst %{_bindir}/gcc-with-python3 %{gcc_plugins_dir}/python3.so %{gcc_plugins_dir}/python3 %doc %{_mandir}/man1/gcc-with-python3.1.gz %files -n gcc-python2-debug-plugin %defattr(-,root,root,-) %doc COPYING README.rst %{_bindir}/gcc-with-python2_debug %{gcc_plugins_dir}/python2_debug.so %{gcc_plugins_dir}/python2_debug %doc %{_mandir}/man1/gcc-with-python2_debug.1.gz %files -n gcc-python3-debug-plugin %defattr(-,root,root,-) %doc COPYING README.rst %{_bindir}/gcc-with-python3_debug %{gcc_plugins_dir}/python3_debug.so %{gcc_plugins_dir}/python3_debug %doc %{_mandir}/man1/gcc-with-python3_debug.1.gz %files docs %defattr(-,root,root,-) %doc COPYING %doc docs/_build/html # Example scripts: %doc examples %changelog * Mon Feb 6 2012 David Malcolm - 0.9-1 - 0.9 * Tue Jan 10 2012 David Malcolm - 0.8-1 - 0.8 * Tue Aug 2 2011 David Malcolm - 0.6-1 - 0.6 * Wed Jul 27 2011 David Malcolm - 0.5-1 - 0.5 - examples are now in an "examples" subdirectory * Tue Jul 26 2011 David Malcolm - 0.4-1 - 0.4 - add requirement on pygments - run the upstream test suites during %%check * Mon Jul 25 2011 David Malcolm - 0.3-1 - add requirements on python-six and python3-six - add %%check section (empty for now) - set PYTHON and PLUGIN_PYTHONPATH during each build; install support files into build-specific directories below the gcc plugin dir - add helper gcc-with-python scripts, with man pages - package the license - add example scripts - add explicit BR on gmp-devel (rhbz#725569) * Tue May 24 2011 David Malcolm - 0.1-1 - initial packaging gcc-python-plugin-0.17/gcc-python-pretty-printer.c000066400000000000000000000063641342215241600222460ustar00rootroot00000000000000/* Copyright 2011, 2013 David Malcolm Copyright 2011, 2013 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #if (GCC_VERSION >= 4009) /* Needed for placement new */ #include #endif PyObject* PyGccPrettyPrinter_New(void) { struct PyGccPrettyPrinter *obj; obj = PyObject_New(struct PyGccPrettyPrinter, &PyGccPrettyPrinter_TypeObj); if (!obj) { return NULL; } //printf("PyGccPrettyPrinter_New\n"); /* Gross hack for getting at a FILE* ; rewrite using fopencookie? */ obj->buf[0] = '\0'; obj->file_ptr = fmemopen(obj->buf, sizeof(obj->buf), "w"); #if (GCC_VERSION >= 4009) /* GCC 4.9 eliminated pp_construct in favor of a C++ ctor. Use placement new to run it on obj->pp. */ new ((void*)&obj->pp) pretty_printer( # if (GCC_VERSION < 9000) /* GCC 9 eliminated the "prefix" param. */ NULL, # endif 0); #else pp_construct(&obj->pp, /* prefix */NULL, /* line-width */0); #endif pp_needs_newline(&obj->pp) = false; pp_translate_identifiers(&obj->pp) = false; /* Connect the pp to the (FILE*): */ obj->pp.buffer->stream = obj->file_ptr; //printf("PyGccPrettyPrinter_New returning: %p\n", obj); return (PyObject*)obj; } pretty_printer* PyGccPrettyPrinter_as_pp(PyObject *obj) { struct PyGccPrettyPrinter *ppobj; /* FIXME: */ assert(Py_TYPE(obj) == &PyGccPrettyPrinter_TypeObj); ppobj = (struct PyGccPrettyPrinter *)obj; return &ppobj->pp; } PyObject* PyGccPrettyPrinter_as_string(PyObject *obj) { struct PyGccPrettyPrinter *ppobj; int len; /* FIXME: */ assert(Py_TYPE(obj) == &PyGccPrettyPrinter_TypeObj); ppobj = (struct PyGccPrettyPrinter *)obj; /* Flush the pp first. This forcibly adds a trailing newline: */ pp_flush(&ppobj->pp); /* Convert to a python string, leaving off the trailing newline: */ len = strlen(ppobj->buf); assert(len > 0); if ('\n' == ppobj->buf[len - 1]) { return PyGccString_FromString_and_size(ppobj->buf, len - 1); } else { return PyGccString_FromString(ppobj->buf); } } void PyGccPrettyPrinter_dealloc(PyObject *obj) { struct PyGccPrettyPrinter *ppobj; /* FIXME: */ assert(Py_TYPE(obj) == &PyGccPrettyPrinter_TypeObj); ppobj = (struct PyGccPrettyPrinter *)obj; /* Close the (FILE*), if open: */ if (ppobj->file_ptr) { fclose(ppobj->file_ptr); ppobj->file_ptr = NULL; } Py_TYPE(obj)->tp_free(obj); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-rtl.c000066400000000000000000000127141342215241600200330ustar00rootroot00000000000000/* Copyright 2011-2013, 2015 David Malcolm Copyright 2011-2013, 2015 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-python-compat.h" #include "rtl.h" #include "gcc-c-api/gcc-rtl.h" #if (GCC_VERSION < 5000) PyObject * PyGccRtl_get_location(struct PyGccRtl *self, void *closure) { /* In gcc 4.8, INSN_LOCATOR was replaced by INSN_LOCATION (in r191494) */ #if (GCC_VERSION >= 4008) return PyGccLocation_New(gcc_private_make_location(INSN_LOCATION(self->insn.inner))); #else int locator = INSN_LOCATOR (self->insn.inner); if (locator && insn_file (self->insn.inner)) { return PyGccLocation_New(gcc_private_make_location(locator_location(locator))); } Py_RETURN_NONE; #endif } #endif PyObject * get_operand_as_object(const_rtx in_rtx, int idx, char fmt) { const char *str; /* The operand types are described in gcc/rtl.c */ switch (fmt) { case 'T': /* pointer to a string, with special meaning */ str = XTMPL (in_rtx, idx); goto string; case 'S': /* optional pointer to a string */ case 's': /* a pointer to a string */ str = XSTR (in_rtx, idx); string: return PyGccStringOrNone(str); case '0': /* unused, or used in a phase-dependent manner */ Py_RETURN_NONE; /* for now */ case 'e': /* pointer to an rtl expression */ /* Nested expression: */ return PyGccRtl_New( gcc_private_make_rtl_insn(XEXP (in_rtx, idx))); case 'E': case 'V': /* Nested list of expressions */ { PyObject *list = PyList_New(XVECLEN (in_rtx, idx)); int j; if (!list) { return NULL; } for (j = 0; j < XVECLEN (in_rtx, idx); j++) { PyObject *item = PyGccRtl_New( gcc_private_make_rtl_insn(XVECEXP (in_rtx, idx, j))); if (!item) { Py_DECREF(list); return NULL; } if (-1 == PyList_Append(list, item)) { Py_DECREF(item); Py_DECREF(list); return NULL; } Py_DECREF(item); } return list; } case 'w': return PyGccInt_FromLong(XWINT (in_rtx, idx)); case 'i': return PyGccInt_FromLong(XINT (in_rtx, idx)); case 'n': /* Return NOTE_INSN names rather than integer codes. */ return PyGccStringOrNone(GET_NOTE_INSN_NAME (XINT (in_rtx, idx))); case 'u': /* a pointer to another insn */ Py_RETURN_NONE; /* for now */ case 't': return PyGccTree_New(gcc_private_make_tree(XTREE (in_rtx, idx))); case '*': Py_RETURN_NONE; /* for now */ case 'B': return PyGccBasicBlock_New( gcc_private_make_cfg_block(XBBDEF (in_rtx, idx))); default: gcc_unreachable (); } } PyObject * PyGccRtl_get_operands(struct PyGccRtl *self, void *closure) { const int length = GET_RTX_LENGTH (GET_CODE (self->insn.inner)); PyObject *result; int i; const char *format_ptr; result = PyTuple_New(length); if (!result) { return NULL; } format_ptr = GET_RTX_FORMAT (GET_CODE (self->insn.inner)); for (i = 0; i < length; i++) { PyObject *item = get_operand_as_object(self->insn.inner, i, *format_ptr++); if (!item) { Py_DECREF(result); return NULL; } PyTuple_SET_ITEM(result, i, item); } return result; } PyObject * PyGccRtl_repr(struct PyGccRtl * self) { return PyGccString_FromFormat("%s()", Py_TYPE(self)->tp_name); } PyObject * PyGccRtl_str(struct PyGccRtl * self) { /* We use print_rtl_single() which takes a FILE*, so we need an fmemopen buffer to write to: */ char buf[2048]; /* FIXME */ FILE *f; buf[0] = '\0'; f = fmemopen(buf, sizeof(buf), "w"); if (!f) { return PyErr_SetFromErrno(PyExc_IOError); } print_rtl_single (f, self->insn.inner); fclose(f); return PyGccString_FromString(buf); } PyObject* PyGccRtl_New(gcc_rtl_insn insn) { struct PyGccRtl *rtl_obj = NULL; PyGccWrapperTypeObject* tp; if (!insn.inner) { Py_RETURN_NONE; } tp = PyGcc_autogenerated_rtl_type_for_stmt(insn); assert(tp); rtl_obj = PyGccWrapper_New(struct PyGccRtl, tp); if (!rtl_obj) { goto error; } rtl_obj->insn = insn; return (PyObject*)rtl_obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccRtl(PyGccRtl *wrapper) { /* Mark the underlying object (recursing into its fields): */ gcc_rtl_insn_mark_in_use(wrapper->insn); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-tree.c000066400000000000000000001046121342215241600201700ustar00rootroot00000000000000/* Copyright 2011-2015 David Malcolm Copyright 2011-2015 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-python-compat.h" #include "cp/cp-tree.h" #include "gimple.h" #include "cp/cp-tree.h" /* for TFF_* for use by PyGccFunctionDecl_get_fullname */ /* for op_symbol_code */ /* Moved to tree-pretty-print.h in gcc 4.9: */ #if (GCC_VERSION >= 4009) #include "tree-pretty-print.h" #else #include "tree-flow.h" #endif /* "maybe_get_identifier" was moved from tree.h to stringpool.h in 4.9 */ #if (GCC_VERSION >= 4009) #include "stringpool.h" /* for maybe_get_identifier */ #endif #include "gcc-c-api/gcc-tree.h" #include "gcc-c-api/gcc-type.h" #include "gcc-c-api/gcc-private-compat.h" /* for now */ extern PyGccWrapperTypeObject PyGccIntegerCst_TypeObj; __typeof__ (lang_check_failed) lang_check_failed __attribute__ ((weak)); /* Unfortunately, decl_as_string() is only available from the C++ frontend: cc1plus (it's defined in gcc/cp/error.c). See http://gcc.gnu.org/ml/gcc/2011-11/msg00504.html Hence we redeclare the symbol as weak, and then check its definition against 0 before using it. */ __typeof__ (decl_as_string) decl_as_string __attribute__ ((weak)); /* Similar for namespace_binding, though gcc 8's r247654 (aka f906dcc33dd818b71e16c88cef38f33c161070db) replaced it with get_namespace_value and reversed the order of the params and r247745 (aka 9d79db401edbb9665cde47ddea2702671c89e548) renamed it from get_namespace_value to get_namespace_binding. */ #if (GCC_VERSION >= 8000) __typeof__ (get_namespace_binding) get_namespace_binding __attribute__ ((weak)); #else __typeof__ (namespace_binding) namespace_binding __attribute__ ((weak)); #endif /* And for cp_namespace_decls: */ __typeof__ (cp_namespace_decls) cp_namespace_decls __attribute__ ((weak)); static PyObject * raise_cplusplus_only(const char *what) { return PyErr_Format(PyExc_RuntimeError, "%s is only available when compiling C++ code", what); } static PyObject * raise_not_during_lto(const char *what) { return PyErr_Format(PyExc_RuntimeError, "%s is not available during link-time optimization", what); } //#include "rtl.h" /* "struct rtx_def" is declarted within rtl.h, c.f: struct GTY((chain_next ("RTX_NEXT (&%h)"), chain_prev ("RTX_PREV (&%h)"), variable_size)) rtx_def { ... snip ... } and seems to be the fundamental instruction type Seems to use RTX_NEXT() and RTX_PREV() */ /* Code for various tree types */ static PyObject * do_pretty_print(struct PyGccTree * self, int spc, dump_flags_t flags) { PyObject *ppobj = PyGccPrettyPrinter_New(); PyObject *result = NULL; if (!ppobj) { return NULL; } dump_generic_node (PyGccPrettyPrinter_as_pp(ppobj), self->t.inner, spc, flags, 0); result = PyGccPrettyPrinter_as_string(ppobj); if (!result) { goto error; } Py_XDECREF(ppobj); return result; error: Py_XDECREF(ppobj); return NULL; } PyObject * PyGccBlock_New(gcc_block t) { return PyGccTree_New(gcc_block_as_gcc_tree(t)); } PyObject * PyGccPointerType_New(gcc_pointer_type t) { return PyGccTree_New(gcc_pointer_type_as_gcc_tree(t)); } PyObject * PyGccPointerType_repr(struct PyGccTree * self) { PyObject *repr_name = NULL; PyObject *result = NULL; repr_name = PyGcc_GetReprOfAttribute((PyObject*)self, "dereference"); if (!repr_name) { goto error; } result = PyGccString_FromFormat("%s(dereference=%s)", Py_TYPE(self)->tp_name, PyGccString_AsString(repr_name)); error: Py_XDECREF(repr_name); return result; } PyObject * PyGccCaseLabelExpr_New(gcc_case_label_expr t) { return PyGccTree_New(gcc_case_label_expr_as_gcc_tree(t)); } PyObject * PyGccTree_str(struct PyGccTree * self) { return do_pretty_print(self, 0, (dump_flags_t)0); } long PyGccTree_hash(struct PyGccTree * self) { if (Py_TYPE(self) == (PyTypeObject*)&PyGccComponentRef_TypeObj) { return (long)TREE_OPERAND(self->t.inner, 0) ^ (long)TREE_OPERAND(self->t.inner, 1); } if (Py_TYPE(self) == (PyTypeObject*)&PyGccIntegerCst_TypeObj) { /* Ensure that hash(cst) == hash(int(cst)) */ PyObject *constant = PyGccIntegerConstant_get_constant(self, NULL); long result; if (!constant) { return -1; } result = PyObject_Hash(constant); Py_DECREF(constant); return result; } /* Use the ptr as the hash value: */ return (long)self->t.inner; } PyObject * PyGccTree_richcompare(PyObject *o1, PyObject *o2, int op) { struct PyGccTree *treeobj1; struct PyGccTree *treeobj2; int cond; PyObject *result_obj; /* Specialcases: */ if (Py_TYPE(o1) == (PyTypeObject*)&PyGccIntegerCst_TypeObj) { o1 = PyGccIntegerConstant_get_constant((struct PyGccTree *)o1, NULL); if (!o1) { return NULL; } result_obj = PyObject_RichCompare(o1, o2, op); Py_DECREF(o1); return result_obj; } if (Py_TYPE(o2) == (PyTypeObject*)&PyGccIntegerCst_TypeObj) { o2 = PyGccIntegerConstant_get_constant((struct PyGccTree *)o2, NULL); if (!o2) { return NULL; } result_obj = PyObject_RichCompare(o1, o2, op); Py_DECREF(o2); return result_obj; } if (!PyObject_TypeCheck(o1, (PyTypeObject*)&PyGccTree_TypeObj)) { result_obj = Py_NotImplemented; goto out; } if (!PyObject_TypeCheck(o2, (PyTypeObject*)&PyGccTree_TypeObj)) { result_obj = Py_NotImplemented; goto out; } treeobj1 = (struct PyGccTree *)o1; treeobj2 = (struct PyGccTree *)o2; if (Py_TYPE(o1) == (PyTypeObject*)&PyGccComponentRef_TypeObj) { if (Py_TYPE(o2) == (PyTypeObject*)&PyGccComponentRef_TypeObj) { switch (op) { case Py_EQ: cond = ((TREE_OPERAND(treeobj1->t.inner, 0) == TREE_OPERAND(treeobj2->t.inner, 0)) && (TREE_OPERAND(treeobj1->t.inner, 1) == TREE_OPERAND(treeobj2->t.inner, 1))); break; case Py_NE: cond = !((TREE_OPERAND(treeobj1->t.inner, 0) == TREE_OPERAND(treeobj2->t.inner, 0)) && (TREE_OPERAND(treeobj1->t.inner, 1) == TREE_OPERAND(treeobj2->t.inner, 1))); break; default: result_obj = Py_NotImplemented; goto out; } result_obj = cond ? Py_True : Py_False; goto out; } } switch (op) { case Py_EQ: cond = (treeobj1->t.inner == treeobj2->t.inner); break; case Py_NE: cond = (treeobj1->t.inner != treeobj2->t.inner); break; default: result_obj = Py_NotImplemented; goto out; } result_obj = cond ? Py_True : Py_False; out: Py_INCREF(result_obj); return result_obj; } PyObject * PyGccTree_get_str_no_uid(struct PyGccTree *self, void *closure) { return do_pretty_print(self, 0, TDF_NOUID); } PyObject * PyGccTree_get_symbol(PyObject *cls, PyObject *args) { enum tree_code code; if (-1 == PyGcc_tree_type_object_as_tree_code(cls, &code)) { PyErr_SetString(PyExc_TypeError, "no symbol associated with this type"); return NULL; } return PyGccString_FromString(op_symbol_code(code)); } PyObject * PyGccDeclaration_repr(struct PyGccTree * self) { PyObject *name = NULL; PyObject *result = NULL; if (DECL_NAME(self->t.inner)) { name = PyGccDeclaration_get_name(self, NULL); if (!name) { goto error; } result = PyGccString_FromFormat("%s('%s')", Py_TYPE(self)->tp_name, PyGccString_AsString(name)); Py_DECREF(name); } else { result = PyGccString_FromFormat("%s(%u)", Py_TYPE(self)->tp_name, DECL_UID (self->t.inner)); } return result; error: Py_XDECREF(name); Py_XDECREF(result); return NULL; } PyObject * PyGccFunctionDecl_get_fullname(struct PyGccTree *self, void *closure) { const char *str; if (NULL == decl_as_string) { return raise_cplusplus_only("attribute 'fullname'"); } str = decl_as_string(self->t.inner, TFF_DECL_SPECIFIERS |TFF_RETURN_TYPE |TFF_FUNCTION_DEFAULT_ARGUMENTS |TFF_EXCEPTION_SPECIFICATION); return PyGccString_FromString(str); } PyObject * PyGccFunctionDecl_get_callgraph_node(struct PyGccTree *self, void *closure) { /* cgraph_get_node became cgraph_node::get in 5.0 */ struct cgraph_node *node; #if (GCC_VERSION >= 5000) node = cgraph_node::get(self->t.inner); #else node = cgraph_get_node(self->t.inner); #endif return PyGccCallgraphNode_New(gcc_private_make_cgraph_node(node)); } PyObject * PyGccArrayRef_repr(PyObject *self) { PyObject *array_repr = NULL; PyObject *index_repr = NULL; PyObject *result = NULL; array_repr = PyGcc_GetReprOfAttribute(self, "array"); if (!array_repr) { goto error; } index_repr =PyGcc_GetReprOfAttribute(self, "index"); if (!index_repr) { goto error; } result = PyGccString_FromFormat("%s(array=%s, index=%s)", Py_TYPE(self)->tp_name, PyGccString_AsString(array_repr), PyGccString_AsString(index_repr)); error: Py_XDECREF(array_repr); Py_XDECREF(index_repr); return result; } PyObject * PyGccComponentRef_repr(PyObject *self) { PyObject *target_repr = NULL; PyObject *field_repr = NULL; PyObject *result = NULL; target_repr = PyGcc_GetReprOfAttribute(self, "target"); if (!target_repr) { goto error; } field_repr = PyGcc_GetReprOfAttribute(self, "field"); if (!field_repr) { goto error; } result = PyGccString_FromFormat("%s(target=%s, field=%s)", Py_TYPE(self)->tp_name, PyGccString_AsString(target_repr), PyGccString_AsString(field_repr)); error: Py_XDECREF(target_repr); Py_XDECREF(field_repr); return result; } PyObject * PyGccIdentifierNode_repr(struct PyGccTree * self) { if (IDENTIFIER_POINTER(self->t.inner)) { return PyGccString_FromFormat("%s(name='%s')", Py_TYPE(self)->tp_name, IDENTIFIER_POINTER(self->t.inner)); } else { return PyGccString_FromFormat("%s(name=None)", Py_TYPE(self)->tp_name); } } PyObject * PyGccType_get_attributes(struct PyGccTree *self, void *closure) { /* gcc/tree.h defines TYPE_ATTRIBUTES(NODE) as: "A TREE_LIST of IDENTIFIER nodes of the attributes that apply to this type" Looking at: typedef int (example3)(const char *, const char *, const char *) __attribute__((nonnull(1))) __attribute__((nonnull(3))); (which is erroneous), we get this for TYPE_ATTRIBUTES: gcc.TreeList(purpose=gcc.IdentifierNode(name='nonnull'), value=gcc.TreeList(purpose=None, value=gcc.IntegerCst(3), chain=None), chain=gcc.TreeList(purpose=gcc.IdentifierNode(name='nonnull'), value=gcc.TreeList(purpose=None, value=gcc.IntegerCst(1), chain=None), chain=None) ) */ tree attr; PyObject *result = PyDict_New(); if (!result) { return NULL; } for (attr = TYPE_ATTRIBUTES(self->t.inner); attr; attr = TREE_CHAIN(attr)) { const char *attrname = IDENTIFIER_POINTER(TREE_PURPOSE(attr)); PyObject *values; values = PyGcc_TreeMakeListFromTreeList(TREE_VALUE(attr)); if (!values) { goto error; } if (-1 == PyDict_SetItemString(result, attrname, values)) { Py_DECREF(values); goto error; } Py_DECREF(values); } return result; error: Py_DECREF(result); return NULL; } /* Weakly import c_sizeof_or_alignof_type; it's not available in lto1 (link-time optimization) */ __typeof__ (c_sizeof_or_alignof_type) c_sizeof_or_alignof_type __attribute__ ((weak)); PyObject * PyGccType_get_sizeof(struct PyGccTree *self, void *closure) { if (NULL == c_sizeof_or_alignof_type) { return raise_not_during_lto("Type.sizeof"); } /* c_sizeof_or_alignof_type wants a location; we use a fake one */ tree t_sizeof = c_sizeof_or_alignof_type(input_location, self->t.inner, true, #if (GCC_VERSION >= 4009) false, #endif 0); PyObject *str; /* This gives us either an INTEGER_CST or the dummy error type: */ if (INTEGER_CST == TREE_CODE(t_sizeof)) { return PyGcc_int_from_int_cst (t_sizeof); } /* Error handling: */ str = PyGccTree_str(self); if (str) { PyErr_Format(PyExc_TypeError, "type \"%s\" does not have a \"sizeof\"", PyGccString_AsString(str)); Py_DECREF(str); } else { PyErr_Format(PyExc_TypeError, "type does not have a \"sizeof\""); } return NULL; } __typeof__ (c_common_signed_type) c_common_signed_type __attribute__ ((weak)); PyObject * PyGccIntegerType_get_signed_equivalent(struct PyGccTree * self, void *closure) { if (NULL == c_common_signed_type) return raise_not_during_lto("gcc.IntegerType.signed_equivalent"); return PyGccTree_New(gcc_private_make_tree(c_common_signed_type(self->t.inner))); } __typeof__ (c_common_unsigned_type) c_common_unsigned_type __attribute__ ((weak)); PyObject * PyGccIntegerType_get_unsigned_equivalent(struct PyGccTree * self, void *closure) { if (NULL == c_common_unsigned_type) return raise_not_during_lto("gcc.IntegerType.unsigned_equivalent"); return PyGccTree_New(gcc_private_make_tree(c_common_unsigned_type(self->t.inner))); } PyObject * PyGccIntegerType_repr(struct PyGccTree * self) { PyObject *repr_name = NULL; PyObject *result = NULL; repr_name = PyGcc_GetReprOfAttribute((PyObject*)self, "name"); if (!repr_name) { goto error; } result = PyGccString_FromFormat("%s(name=%s)", Py_TYPE(self)->tp_name, PyGccString_AsString(repr_name)); error: Py_XDECREF(repr_name); return result; } PyObject * PyGccFunction_TypeObj_get_argument_types(struct PyGccTree * self, void *closure) { PyObject *result; PyObject *item; int i, size; tree iter; tree head = TYPE_ARG_TYPES(self->t.inner); /* Get length of chain */ for (size = 0, iter = head; iter && iter != error_mark_node && iter != void_list_node; iter = TREE_CHAIN(iter), size++) { /* empty */ } /* "size" should now be the length of the chain */ result = PyTuple_New(size); if (!result) { return NULL; } for (i = 0, iter = head; i < size; iter = TREE_CHAIN(iter), i++) { item = PyGccTree_New(gcc_private_make_tree(TREE_VALUE(iter))); if (!item) { goto error; } if (0 != PyTuple_SetItem(result, i, item)) { Py_DECREF(item); goto error; } } return result; error: Py_XDECREF(result); return NULL; } PyObject * PyGccFunction_TypeObj_is_variadic(struct PyGccTree * self, void *closure) { tree iter; tree head = TYPE_ARG_TYPES(self->t.inner); /* Get length of chain */ for (iter = head; iter && iter != error_mark_node && iter != void_list_node; iter = TREE_CHAIN(iter)) { /* empty */ } if (iter == void_list_node) Py_RETURN_FALSE; Py_RETURN_TRUE; } PyObject * PyGccMethodType_get_argument_types(struct PyGccTree * self,void *closure) { return PyGccFunction_TypeObj_get_argument_types(self, closure); } PyObject * PyGccMethodType_is_variadic(struct PyGccTree * self,void *closure) { return PyGccFunction_TypeObj_is_variadic(self, closure); } PyObject * PyGccConstructor_get_elements(PyObject *self, void *closure) { struct PyGccTree * self_as_tree; PyObject *result = NULL; tree node; unsigned HOST_WIDE_INT cnt; tree index, value; self_as_tree = (struct PyGccTree *)self; /* FIXME */ node = self_as_tree->t.inner; result = PyList_New(GCC_COMPAT_VEC_LENGTH(constructor_elt, CONSTRUCTOR_ELTS (node))); if (!result) { goto error; } FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (node), cnt, index, value) { PyObject *obj_index = NULL; PyObject *obj_value = NULL; PyObject *obj_pair = NULL; obj_index = PyGccTree_New(gcc_private_make_tree(index)); if (!obj_index) { goto error; } obj_value = PyGccTree_New(gcc_private_make_tree(value)); if (!obj_value) { Py_DECREF(obj_index); goto error; } obj_pair = PyTuple_Pack(2, obj_index, obj_value); if (!obj_pair) { Py_DECREF(obj_value); Py_DECREF(obj_index); goto error; } Py_DECREF(obj_value); Py_DECREF(obj_index); if (-1 == PyList_SetItem(result, cnt, obj_pair)) { Py_DECREF(obj_pair); goto error; } } return result; error: Py_XDECREF(result); return NULL; } #if (GCC_VERSION >= 5000) static void print_integer_cst_to_buf(tree int_cst, char *buf, tree type) { /* GCC 8 commit r253595 (aka e3d0f65c14ffd7a63455dc1aa9d0405d25b327e4, 2017-10-10) introduces and requires the use of wi::to_wide on INTEGER_CST. */ #if (GCC_VERSION >= 8000) print_dec(wi::to_wide(int_cst), buf, TYPE_SIGN (type)); #else print_dec(int_cst, buf, TYPE_SIGN (type)); #endif } #endif /* #if (GCC_VERSION >= 5000) */ PyObject * PyGcc_int_from_int_cst(tree int_cst) { tree type = TREE_TYPE(int_cst); #if (GCC_VERSION >= 5000) char buf[WIDE_INT_PRINT_BUFFER_SIZE]; print_integer_cst_to_buf (int_cst, buf, type); return PyGcc_int_from_decimal_string_buffer(buf); #else return PyGcc_int_from_double_int(TREE_INT_CST(int_cst), TYPE_UNSIGNED(type)); #endif } PyObject * PyGccIntegerConstant_get_constant(struct PyGccTree * self, void *closure) { return PyGcc_int_from_int_cst(self->t.inner); } PyObject * PyGccIntegerConstant_repr(struct PyGccTree * self) { tree type = TREE_TYPE(self->t.inner); #if (GCC_VERSION >= 5000) char buf[WIDE_INT_PRINT_BUFFER_SIZE]; print_integer_cst_to_buf (self->t.inner, buf, type); #else char buf[512]; PyGcc_DoubleIntAsText(TREE_INT_CST(self->t.inner), TYPE_UNSIGNED(type), buf, sizeof(buf)); #endif return PyGccString_FromFormat("%s(%s)", Py_TYPE(self)->tp_name, buf); } PyObject * PyGccRealCst_get_constant(struct PyGccTree * self, void *closure) { /* "cheat" and go through the string representation: */ REAL_VALUE_TYPE *d; char buf[60]; PyObject *str; PyObject *result; d = TREE_REAL_CST_PTR(self->t.inner); real_to_decimal (buf, d, sizeof (buf), 0, 1); str = PyGccString_FromString(buf); if (!str) { return NULL; } /* PyFloat_FromString API dropped its redundant second argument in Python 3 */ #if PY_MAJOR_VERSION == 3 result = PyFloat_FromString(str); #else result = PyFloat_FromString(str, NULL); #endif Py_DECREF(str); return result; } PyObject * PyGccRealCst_repr(struct PyGccTree * self) { REAL_VALUE_TYPE *d; char buf[60]; d = TREE_REAL_CST_PTR(self->t.inner); real_to_decimal (buf, d, sizeof (buf), 0, 1); return PyGccString_FromFormat("%s(%s)", Py_TYPE(self)->tp_name, buf); } PyObject * PyGccStringConstant_repr(struct PyGccTree * self) { PyObject *str_obj; PyObject *result = NULL; str_obj = PyGccStringOrNone(TREE_STRING_POINTER(self->t.inner)); if (!str_obj) { return NULL; } #if PY_MAJOR_VERSION >= 3 result = PyGccString_FromFormat("%s(%R)", Py_TYPE(self)->tp_name, str_obj); #else { PyObject *str_repr; str_repr = PyObject_Repr(str_obj); if (!str_repr) { Py_DECREF(str_obj); return NULL; } result = PyGccString_FromFormat("%s(%s)", Py_TYPE(self)->tp_name, PyString_AsString(str_repr)); Py_DECREF(str_repr); } #endif Py_DECREF(str_obj); return result; } PyObject * PyGccTypeDecl_get_pointer(struct PyGccTree *self, void *closure) { tree decl_type = TREE_TYPE(self->t.inner); if (!decl_type) { PyErr_SetString(PyExc_ValueError, "gcc.TypeDecl has no associated type"); return NULL; } return PyGccTree_New(gcc_private_make_tree(build_pointer_type(decl_type))); } PyObject * PyGccSsaName_repr(struct PyGccTree * self) { int version; PyObject *repr_var = NULL; PyObject *result = NULL; version = gcc_ssa_name_get_version(gcc_tree_as_gcc_ssa_name(self->t)); repr_var = PyGcc_GetReprOfAttribute((PyObject*)self, "var"); if (!repr_var) { goto error; } result = PyGccString_FromFormat("%s(var=%s, version=%i)", Py_TYPE(self)->tp_name, PyGccString_AsString(repr_var), version); error: Py_XDECREF(repr_var); return result; } PyObject * PyGccTreeList_repr(struct PyGccTree * self) { PyObject *purpose = NULL; PyObject *value = NULL; PyObject *chain = NULL; PyObject *repr_purpose = NULL; PyObject *repr_value = NULL; PyObject *repr_chain = NULL; PyObject *result = NULL; purpose = PyGccTree_New(gcc_private_make_tree(TREE_PURPOSE(self->t.inner))); if (!purpose) { goto error; } value = PyGccTree_New(gcc_private_make_tree(TREE_VALUE(self->t.inner))); if (!value) { goto error; } chain = PyGccTree_New(gcc_private_make_tree(TREE_CHAIN(self->t.inner))); if (!chain) { goto error; } repr_purpose = PyObject_Repr(purpose); if (!repr_purpose) { goto error; } repr_value = PyObject_Repr(value); if (!repr_value) { goto error; } repr_chain = PyObject_Repr(chain); if (!repr_chain) { goto error; } result = PyGccString_FromFormat("%s(purpose=%s, value=%s, chain=%s)", Py_TYPE(self)->tp_name, PyGccString_AsString(repr_purpose), PyGccString_AsString(repr_value), PyGccString_AsString(repr_chain)); error: Py_XDECREF(purpose); Py_XDECREF(value); Py_XDECREF(chain); Py_XDECREF(repr_purpose); Py_XDECREF(repr_value); Py_XDECREF(repr_chain); return result; } PyObject * PyGccCaseLabelExpr_repr(PyObject * self) { PyObject *low_repr = NULL; PyObject *high_repr = NULL; PyObject *target_repr = NULL; PyObject *result = NULL; low_repr = PyGcc_GetReprOfAttribute(self, "low"); if (!low_repr) { goto cleanup; } high_repr = PyGcc_GetReprOfAttribute(self, "high"); if (!high_repr) { goto cleanup; } target_repr = PyGcc_GetReprOfAttribute(self, "target"); if (!target_repr) { goto cleanup; } result = PyGccString_FromFormat("%s(low=%s, high=%s, target=%s)", Py_TYPE(self)->tp_name, PyGccString_AsString(low_repr), PyGccString_AsString(high_repr), PyGccString_AsString(target_repr)); cleanup: Py_XDECREF(low_repr); Py_XDECREF(high_repr); Py_XDECREF(target_repr); return result; } PyObject * PyGccNamespaceDecl_lookup(struct PyGccTree * self, PyObject *args, PyObject *kwargs) { tree t_result; tree t_name; const char *name; const char *keywords[] = {"name", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:lookup", (char**)keywords, &name)) { return NULL; } #if (GCC_VERSION >= 8000) if (NULL == get_namespace_binding) { #else if (NULL == namespace_binding) { #endif return raise_cplusplus_only("gcc.NamespaceDecl.lookup"); } t_name = get_identifier(name); #if (GCC_VERSION >= 8000) t_result = get_namespace_binding(self->t.inner, t_name); #else t_result = namespace_binding(t_name, self->t.inner); #endif return PyGccTree_New(gcc_private_make_tree(t_result)); } PyObject * PyGccNamespaceDecl_unalias(struct PyGccTree * self, PyObject *args, PyObject *kwargs) { tree t = self->t.inner; if (NULL == DECL_NAMESPACE_ALIAS(t)) { Py_INCREF(self); return (PyObject *)self; } while (DECL_NAMESPACE_ALIAS(t)) { t = DECL_NAMESPACE_ALIAS(t); } return PyGccTree_New(gcc_private_make_tree(t)); } static PyObject * raise_namespace_alias(const char *what) { return PyErr_Format(PyExc_RuntimeError, "%s is not valid for an alias", what); } PyObject * PyGccNamespaceDecl_declarations(tree t) { if (NULL == cp_namespace_decls) return raise_cplusplus_only("gcc.NamespaceDecl.declarations"); /* throw if we are an alias, if we unalias it, it may return a list containing itself. */ if (DECL_NAMESPACE_ALIAS(t)) return raise_namespace_alias("gcc.NamespaceDecl.declarations"); return PyGcc_TreeListFromChain(cp_namespace_decls(t)); } #if (GCC_VERSION >= 8000) static int is_namespace (tree decl, void *) { return (TREE_CODE (decl) == NAMESPACE_DECL && !DECL_NAMESPACE_ALIAS (decl)); } #endif /* #if (GCC_VERSION >= 8000) */ PyObject * PyGccNamespaceDecl_namespaces(tree t) { /* We don't actually call cp_namespace_decls, but we only actually call c++ specific macros, not sure what happens. */ if (NULL == cp_namespace_decls) return raise_cplusplus_only("gcc.NamespaceDecl.namespaces"); /* throw if we are an alias, if we unalias it, it may return a list containing itself. */ if (DECL_NAMESPACE_ALIAS(t)) return raise_namespace_alias("gcc.NamespaceDecl.namespaces"); /* GCC 8's r248821 (aka f7564df45462679c3b0b82f2942f5fb50b640113) eliminated the "namespaces" field from cp_binding_level. */ #if (GCC_VERSION >= 8000) return PyGcc_TreeListFromChainWithFilter(NAMESPACE_LEVEL(t)->names, is_namespace, NULL); #else return PyGcc_TreeListFromChain(NAMESPACE_LEVEL(t)->namespaces); #endif } /* Accessing the fields and methods of compound types. GCC 8's r250413 (aka ab87ee8f509c0b600102195704105d4d98ec59d9) eliminated TYPE_METHODS, instead putting both fields and methods on the TYPE_FIELDS chain, using DECL_DECLARES_FUNCTION_P to distinguish them. */ #if (GCC_VERSION >= 8000) static int is_field (tree t, void *) { return !DECL_DECLARES_FUNCTION_P (t); } static int is_method (tree t, void *) { return DECL_DECLARES_FUNCTION_P (t); } #endif /* #if (GCC_VERSION >= 8000) */ PyObject * PyGcc_GetFields(struct PyGccTree *self) { #if (GCC_VERSION >= 8000) return PyGcc_TreeListFromChainWithFilter(TYPE_FIELDS(self->t.inner), is_field, NULL); #else return PyGcc_TreeListFromChain(TYPE_FIELDS(self->t.inner)); #endif } PyObject * PyGcc_GetMethods(struct PyGccTree *self) { #if (GCC_VERSION >= 8000) return PyGcc_TreeListFromChainWithFilter(TYPE_FIELDS(self->t.inner), is_method, NULL); #else return PyGcc_TreeListFromChain(TYPE_METHODS(self->t.inner)); #endif } /* GCC's debug_tree is implemented in: gcc/print-tree.c e.g. in: /usr/src/debug/gcc-4.6.0-20110321/gcc/print-tree.c and appears to be a good place to look when figuring out how the tree data works. dump_generic_node is defined around line 580 of tree-pretty-print.c */ union tree_or_ptr { gcc_tree tree; void *ptr; }; static PyObject * real_make_tree_wrapper(void *ptr) { struct PyGccTree *tree_obj = NULL; PyGccWrapperTypeObject* tp; union tree_or_ptr u; u.ptr = ptr; if (NULL == ptr) { Py_RETURN_NONE; } tp = PyGcc_autogenerated_tree_type_for_tree(u.tree, 1); assert(tp); tree_obj = PyGccWrapper_New(struct PyGccTree, tp); if (!tree_obj) { goto error; } tree_obj->t = u.tree; return (PyObject*)tree_obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccTree(PyGccTree *wrapper) { gcc_tree_mark_in_use(wrapper->t); } /* Ensure we have a unique PyGccTree per tree address (by maintaining a dict) (what about lifetimes?) */ static PyObject *tree_wrapper_cache = NULL; PyObject * PyGccTree_New(gcc_tree t) { union tree_or_ptr u; u.tree = t; return PyGcc_LazilyCreateWrapper(&tree_wrapper_cache, u.ptr, real_make_tree_wrapper); } PyObject * PyGccTree_NewUnique(gcc_tree t) { union tree_or_ptr u; u.tree = t; return real_make_tree_wrapper(u.ptr); } /* Walk the chain of a tree, building a python list of wrapper gcc.Tree instances */ PyObject * PyGcc_TreeListFromChain(tree t) { PyObject *result = NULL; result = PyList_New(0); if (!result) { goto error; } while (t) { PyObject *item; item = PyGccTree_New(gcc_private_make_tree(t)); if (!item) { goto error; } if (-1 == PyList_Append(result, item)) { Py_DECREF(item); goto error; } Py_DECREF(item); t = TREE_CHAIN(t); } return result; error: Py_XDECREF(result); return NULL; } /* As above, but only add nodes for which "filter" returns true. */ PyObject * PyGcc_TreeListFromChainWithFilter(tree t, int (*filter) (tree, void *), void *user_data) { PyObject *result = NULL; result = PyList_New(0); if (!result) { goto error; } while (t) { if (filter (t, user_data)) { PyObject *item; item = PyGccTree_New(gcc_private_make_tree(t)); if (!item) { goto error; } if (-1 == PyList_Append(result, item)) { Py_DECREF(item); goto error; } Py_DECREF(item); } t = TREE_CHAIN(t); } return result; error: Py_XDECREF(result); return NULL; } /* As above, but expect nodes of the form: tree_list ---> value +--chain--> tree_list ---> value +--chain--> tree_list ---> value +---chain--> NULL Extract a list of objects for the values */ PyObject * PyGcc_TreeMakeListFromTreeList(tree t) { PyObject *result = NULL; result = PyList_New(0); if (!result) { goto error; } while (t) { PyObject *item; item = PyGccTree_New(gcc_private_make_tree(TREE_VALUE(t))); if (!item) { goto error; } if (-1 == PyList_Append(result, item)) { Py_DECREF(item); goto error; } Py_DECREF(item); t = TREE_CHAIN(t); } return result; error: Py_XDECREF(result); return NULL; } /* As above, but expect nodes of the form: tree_list ---> (purpose, value) +--chain--> tree_list ---> (purpose, value) +--chain--> tree_list ---> (purpose, value) +---chain--> NULL Extract a list of objects for the values */ PyObject * PyGcc_TreeMakeListOfPairsFromTreeListChain(tree t) { PyObject *result = NULL; result = PyList_New(0); if (!result) { goto error; } while (t) { PyObject *purpose; PyObject *value; PyObject *pair; purpose = PyGccTree_New(gcc_private_make_tree(TREE_PURPOSE(t))); if (!purpose) { goto error; } value = PyGccTree_New(gcc_private_make_tree(TREE_VALUE(t))); if (!value) { Py_DECREF(purpose); goto error; } pair = Py_BuildValue("OO", purpose, value); Py_DECREF(purpose); Py_DECREF(value); if (!pair) { goto error; } if (-1 == PyList_Append(result, pair)) { Py_DECREF(pair); goto error; } Py_DECREF(pair); t = TREE_CHAIN(t); } return result; error: Py_XDECREF(result); return NULL; } PyObject * VEC_tree_as_PyList( #if (GCC_VERSION >= 4008) vec *vec_nodes #else VEC(tree,gc) *vec_nodes #endif ) { PyObject *result = NULL; int i; tree t; result = PyList_New(GCC_COMPAT_VEC_LENGTH(tree, vec_nodes)); if (!result) { goto error; } GCC_COMPAT_FOR_EACH_VEC_ELT(tree, vec_nodes, i, t) { PyObject *item; item = PyGccTree_New(gcc_private_make_tree(t)); if (!item) { goto error; } PyList_SetItem(result, i, item); } return result; error: Py_XDECREF(result); return NULL; } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-variable.c000066400000000000000000000026641342215241600210220ustar00rootroot00000000000000/* Copyright 2011 David Malcolm Copyright 2011 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-c-api/gcc-variable.h" PyObject * PyGccVariable_New(gcc_variable var) { struct PyGccVariable *var_obj = NULL; if (NULL == var.inner) { Py_RETURN_NONE; } var_obj = PyGccWrapper_New(struct PyGccVariable, &PyGccVariable_TypeObj); if (!var_obj) { goto error; } var_obj->var = var; return (PyObject*)var_obj; error: return NULL; } void PyGcc_WrtpMarkForPyGccVariable(PyGccVariable *wrapper) { /* Mark the underlying object (recursing into its fields): */ gcc_variable_mark_in_use(wrapper->var); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-version.c000066400000000000000000000047771342215241600207310ustar00rootroot00000000000000/* Copyright 2011, 2012 David Malcolm Copyright 2011, 2012 Red Hat, Inc. This 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 . */ #include #include #include "gcc-python.h" #include "plugin-version.h" /* Version handling: */ static struct plugin_gcc_version *actual_gcc_version; /* Define a gcc.Version type, as a structseq */ static struct PyStructSequence_Field gcc_version_fields[] = { {(char*)"basever", NULL}, {(char*)"datestamp", NULL}, {(char*)"devphase", NULL}, {(char*)"revision", NULL}, {(char*)"configuration_arguments", NULL}, {0} }; static struct PyStructSequence_Desc gcc_version_desc = { (char*)"gcc.Version", /* name */ NULL, /* doc */ gcc_version_fields, 5 }; PyTypeObject GccVersion_TypeObj; void PyGcc_version_init(struct plugin_gcc_version *version) { actual_gcc_version = version; PyStructSequence_InitType(&GccVersion_TypeObj, &gcc_version_desc); } static PyObject * gcc_version_to_object(struct plugin_gcc_version *version) { PyObject *obj = PyStructSequence_New(&GccVersion_TypeObj); if (!obj) { return NULL; } #define SET_ITEM(IDX, FIELD) \ PyStructSequence_SET_ITEM(obj, (IDX), PyGccStringOrNone(version->FIELD)); SET_ITEM(0, basever); SET_ITEM(1, datestamp); SET_ITEM(2, devphase); SET_ITEM(3, revision); SET_ITEM(4, configuration_arguments); #undef SET_ITEM return obj; } PyObject * PyGcc_get_plugin_gcc_version(PyObject *self, PyObject *args) { /* "gcc_version" is compiled in to the plugin, as part of plugin-version.h: */ return gcc_version_to_object(&gcc_version); } PyObject * PyGcc_get_gcc_version(PyObject *self, PyObject *args) { /* "actual_gcc_version" is passed in when the plugin is initialized */ return gcc_version_to_object(actual_gcc_version); } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-wrapper.c000066400000000000000000000307731342215241600207170ustar00rootroot00000000000000/* Copyright 2012 David Malcolm Copyright 2012 Red Hat, Inc. This 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 . */ /* Low-level wrapper support, with integration with GCC's garbage collector (aka "GGC") High-level overview =================== We keep track of all "live" wrapper objects, and each one knows how to mark the object it wraps; we register a hook into GCC"s GC so that when it starts marking, we iterate through all our live wrapper objects, marking the underlying GCC objects. Implementation details ====================== All of our wrapper types are subclasses of PyGccWrapper, which adds a doubly-linked list to the top of the objects, so that we can track all live wrapper objects. This list is updated via their PyTypeObject's tp_alloc and tp_dealloc. Each has a PyTypeObject that's actually a PyGccWrapperTypeObject, which adds a "wrtp_mark" hook to a PyTypeObject, so that it can participate in GCC's garbage collector. It actually has to add it to PyHeapTypeObject, given that that's the (larger) in-memory layout of a user-defined type. In order to correctly inherit this wrtp_mark slot, we need to override the creation of any user-defined subclases of these PyTypeObjects, adding the "inherit the wrtp_mark callback" logic. We do this by setting the ob_type of our PyGccWrapperTypeObject to be PyGccWrapperMeta_TypeObj, which supplies a custom tp_new hook: analogous to setting a __metaclass__: http://docs.python.org/reference/datamodel.html#customizing-class-creation Thus, given e.g. class MyPass(gcc.GimplePass): ...etc... p = MyPass('mypass') p is a PyGccPass ob_type == address on heap of user-defined type ("MyPass") "MyPass" is this heap-allocated PyGccWrapperTypeObject: ob_type == &PyGccWrapperMetaType tp_base == &PyGccGimplePassType (the parent class: "gcc.GimplePass") wrtp_mark == PyGcc_WrtpMarkForPyGccPass (inherited from tp_base via PyGccWrapperMetaType.tp_new) PyGccGimplePassType is a statically-allocated PyGccWrapperTypeObject (in autogenerated-pass.c): ob_type == &PyGccWrapperMetaType tp_base == &PyGccPass_TypeObj (the parent class: "gcc.Pass") wrtp_mark == PyGcc_WrtpMarkForPyGccPass ("manually" set up by generate-pass-c.py) PyGccPass_TypeObj is a statically-allocated PyGccWrapperTypeObject (in autogenerated-pass.c): ob_type == &PyGccWrapperMetaType tp_base == NULL, but becomes &PyBaseObject_Type aka "object" wrtp_mark == PyGcc_WrtpMarkForPyGccPass ("manually" set up by generate-pass-c.py) PyGccWrapperMetaType is the statically-allocated metaclass for the above type objects ("gcc.WrapperMeta") (as it happens, gcc.Pass wraps an object that doesn't participate in the GCC garbage collector, so this is somewhat redundant. However, it should matter when creating user-defined subclasses of types that *do* wrap a GC-managed object) */ #include #include "gcc-python.h" #include "gcc-python-wrappers.h" #include "gcc-python-compat.h" static void force_gcc_gc(void); /* Debugging, for use by selftest routine */ static int debug_PyGcc_wrapper = 0; /* This is the overriden tp_new in our metatype: when we create new subtypes, it copies up our added slots: */ static PyObject* PyGcc_wrapper_meta_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyGccWrapperTypeObject* new_type; PyGccWrapperTypeObject* base_type; /* Use PyType_Type's tp_new to do most of the work: */ new_type = (PyGccWrapperTypeObject*)PyType_Type.tp_new(type, args, kwds); if (!new_type) { return NULL; } /* Verify that the metaclass is sane, and that the created type object will thus be large enough for our extra information: */ assert(Py_TYPE(new_type)->tp_basicsize >= (int)sizeof(PyGccWrapperTypeObject)); base_type = (PyGccWrapperTypeObject*)((PyTypeObject*)new_type)->tp_base; assert(base_type); /* Inherit wrtp_mark: */ assert(base_type->wrtp_mark); new_type->wrtp_mark = base_type->wrtp_mark; return (PyObject*)new_type; } PyTypeObject PyGccWrapperMeta_TypeObj = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "gcc.WrapperMeta", /*tp_name*/ sizeof(PyGccWrapperTypeObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ NULL, /* tp_dealloc */ NULL, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ #if PY_MAJOR_VERSION < 3 0, /*tp_compare*/ #else 0, /*reserved*/ #endif NULL, /* tp_repr */ NULL, /* tp_as_number */ NULL, /* tp_as_sequence */ NULL, /* tp_as_mapping */ NULL, /* tp_hash */ NULL, /* tp_call */ NULL, /* tp_str */ NULL, /* tp_getattro */ NULL, /* tp_setattro */ NULL, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /*tp_doc*/ NULL, /* tp_traverse */ NULL, /* tp_clear */ NULL, /* tp_richcompare */ 0, /* tp_weaklistoffset */ NULL, /* tp_iter */ NULL, /* tp_iternext */ NULL, /* tp_methods */ NULL, /* tp_members */ NULL, /* tp_getset */ &PyType_Type, /* tp_base */ NULL, /* tp_dict */ NULL, /* tp_descr_get */ NULL, /* tp_descr_set */ 0, /* tp_dictoffset */ NULL, /* tp_init */ NULL, /* tp_alloc */ PyGcc_wrapper_meta_tp_new, /* tp_new */ NULL, /* tp_free */ NULL, /* tp_is_gc */ NULL, /* tp_bases */ NULL, /* tp_mro */ NULL, /* tp_cache */ NULL, /* tp_subclasses */ NULL, /* tp_weaklist */ NULL, /* tp_del */ #if PY_VERSION_HEX >= 0x02060000 0, /*tp_version_tag*/ #endif }; /* Maintain a circular linked list of PyGccWrapper instances: */ static struct PyGccWrapper sentinel = { PyObject_HEAD_INIT(NULL) &sentinel, &sentinel, }; PyGccWrapper * _PyGccWrapper_New(PyGccWrapperTypeObject *typeobj) { PyGccWrapper *obj; assert(typeobj); obj = PyObject_New(PyGccWrapper, (PyTypeObject*)typeobj); if (!obj) { return NULL; } /* Add to list of live PyGccWrapper objects: */ PyGccWrapper_Track(obj); return obj; } extern void PyGccWrapper_Track(struct PyGccWrapper *obj) { assert(obj); assert(sentinel.wr_next); assert(sentinel.wr_prev); /* obj is uninitialized, apart from ob_type and ob_refcnt */ if (debug_PyGcc_wrapper) { printf(" PyGccWrapper_Track: %s\n", Py_TYPE(obj)->tp_name); } /* obj's type must use correct delloc, so that obj removes itself from the list. obj->ob_type->tp_dealloc should be either PyGccWrapper_Dealloc or subtype_dealloc */ /* Add to end of list, immediately before sentinel: */ assert(sentinel.wr_prev->wr_next == &sentinel); sentinel.wr_prev->wr_next = obj; obj->wr_prev = sentinel.wr_prev; obj->wr_next = &sentinel; sentinel.wr_prev = obj; assert(obj->wr_prev); assert(obj->wr_next); } void PyGcc_wrapper_untrack(struct PyGccWrapper *obj) { if (debug_PyGcc_wrapper) { printf(" PyGcc_wrapper_untrack: %s\n", Py_TYPE(obj)->tp_name); } assert(obj); assert(Py_REFCNT(obj) == 0); /* Remove from the linked list if it's within it (If the object constructor fails, then we have only a partially-constructed object, which might not have been added to the linked list yet) */ if (obj->wr_prev) { assert(sentinel.wr_next); assert(sentinel.wr_prev); assert(obj->wr_next); /* Remove from linked list: */ obj->wr_prev->wr_next = obj->wr_next; obj->wr_next->wr_prev = obj->wr_prev; obj->wr_prev = NULL; obj->wr_next = NULL; } } void PyGccWrapper_Dealloc(PyObject *obj) { assert(obj); assert(Py_REFCNT(obj) == 0); if (debug_PyGcc_wrapper) { printf(" PyGccWrapper_Dealloc: %s\n", Py_TYPE(obj)->tp_name); } PyGcc_wrapper_untrack((struct PyGccWrapper*)obj); Py_TYPE(obj)->tp_free(obj); } static void my_walker(void *arg ATTRIBUTE_UNUSED) { /* Callback for use by GCC's garbage collector when marking Walk all the PyGccWrapper objects here and if they reference GCC GC objects, mark the underlying GCC objects so that they don't get swept */ struct PyGccWrapper *iter; if (debug_PyGcc_wrapper) { printf(" walking the live PyGccWrapper objects\n"); } for (iter = sentinel.wr_next; iter != &sentinel; iter = iter->wr_next) { wrtp_marker wrtp_mark; if (debug_PyGcc_wrapper) { printf(" marking inner object for: "); PyObject_Print((PyObject*)iter, stdout, 0); printf("\n"); } wrtp_mark = ((PyGccWrapperTypeObject*)Py_TYPE(iter))->wrtp_mark; assert(wrtp_mark); wrtp_mark(iter); } if (debug_PyGcc_wrapper) { printf(" finished walking the live PyGccWrapper objects\n"); } } static struct ggc_root_tab myroottab[] = { { (char*)"", 1, 1, my_walker, NULL }, { NULL, } }; void PyGcc_wrapper_init(void) { /* Register our GC root-walking callback: */ ggc_register_root_tab(myroottab); PyType_Ready(&PyGccWrapperMeta_TypeObj); } static void force_gcc_gc(void) { bool stored = ggc_force_collect; ggc_force_collect = true; ggc_collect(); ggc_force_collect = stored; } PyObject * PyGcc__force_garbage_collection(PyObject *self, PyObject *args) { force_gcc_gc(); Py_RETURN_NONE; } #define MY_ASSERT(condition) \ if (!(condition)) { \ PyErr_SetString(PyExc_AssertionError, #condition); \ return NULL; \ } PyObject * PyGcc__gc_selftest(PyObject *self, PyObject *args) { tree tree_intcst; PyObject *wrapper_intcst; tree tree_str; PyObject *wrapper_str; printf("gcc._gc_selftest() starting\n"); /* Enable verbose logging: */ debug_PyGcc_wrapper = 1; /* Approach: construct various GCC objects here, unreferenced by anything. Wrap them in Python objects, then force the GCC GC, then verify that the underlying objects were marked, and weren't collected. See http://gcc.gnu.org/onlinedocs/gccint/Type-Information.html for some notes on GCC's garbage collector. */ printf("creating test GCC objects\n"); /* We're called from PLUGIN_FINISH, so integer_types[] should be ready: */ tree_intcst = build_int_cst(integer_types[itk_int], 42); wrapper_intcst = PyGccTree_NewUnique(gcc_private_make_tree(tree_intcst)); MY_ASSERT(wrapper_intcst); #define MY_TEST_STRING "I am only referenced via a python wrapper" tree_str = build_string(strlen(MY_TEST_STRING), MY_TEST_STRING); /* We should now have a newly-allocated tree node; it should not have been marked yet by the GC: */ MY_ASSERT(tree_str); //MY_ASSERT(!ggc_marked_p(str)); // this fails, why? why is it being marked? /* Wrap it with a Python object: */ wrapper_str = PyGccTree_NewUnique(gcc_private_make_tree(tree_str)); MY_ASSERT(wrapper_str); /* Force a garbage collection: */ printf("forcing a garbage collection:\n"); force_gcc_gc(); printf("completed the forced garbage collection\n"); /* Verify that the various wrapped objects were marked (via the Python wrapper). If it was not marked, then we're accessing freed memory, and this will likely fail with an Internal Compiler Error */ printf("verifying that the underlying GCC objects were marked\n"); MY_ASSERT(ggc_marked_p(tree_intcst)); MY_ASSERT(ggc_marked_p(tree_str)); printf("all of the underlying GCC objects were indeed marked\n"); /* Clean up the wrapper objects: */ printf("invoking DECREF on Python wrapper objects\n"); Py_DECREF(wrapper_intcst); Py_DECREF(wrapper_str); /* FIXME: need to do this for all of our types (base classes, at least) */ printf("gcc._gc_selftest() complete\n"); /* Turn off verbose logging: */ debug_PyGcc_wrapper = 0; Py_RETURN_NONE; } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python-wrappers.h000066400000000000000000000335141342215241600211030ustar00rootroot00000000000000/* Copyright 2011-2013, 2015, 2017 David Malcolm Copyright 2011-2013, 2015, 2017 Red Hat, Inc. This 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 . */ #ifndef INCLUDED__WRAPPERS_H #define INCLUDED__WRAPPERS_H #include "gcc-python.h" #include "tree-pass.h" #include "opts.h" #include "cgraph.h" /* Create a callback for use in a gcc for_each iterator to make wrapper objects for the underlying gcc objects being iterated, and append the wrapper objects to a Python list */ #define IMPL_APPENDER(FNNAME, KIND, MAKE_WRAPPER) \ static bool FNNAME(KIND var, void *user_data) \ { \ PyObject *result = (PyObject*)user_data; \ PyObject *obj_var; \ obj_var = MAKE_WRAPPER(var); \ if (!obj_var) { \ return true; \ } \ if (-1 == PyList_Append(result, obj_var)) { \ Py_DECREF(obj_var); \ return true; \ } \ /* Success: */ \ Py_DECREF(obj_var); \ return false; \ } /* Create the body of a function that builds a list by calling a for_each ITERATOR, passing in the APPENDER callback to convert the iterated items into Python wrapper objects */ #define IMPL_LIST_MAKER(ITERATOR, ARG, APPENDER) \ PyObject *result; \ result = PyList_New(0); \ if (!result) { \ return NULL; \ } \ if (ITERATOR((ARG), APPENDER, result)) { \ Py_DECREF(result); \ return NULL; \ } \ return result; /* As per IMPL_LIST_MAKER, but for a global iterator that takes no ARG */ #define IMPL_GLOBAL_LIST_MAKER(ITERATOR, APPENDER) \ PyObject *result; \ result = PyList_New(0); \ if (!result) { \ return NULL; \ } \ if (ITERATOR(APPENDER, result)) { \ Py_DECREF(result); \ return NULL; \ } \ return result; PyMODINIT_FUNC initoptpass(void); /* gcc-python-attribute.c: */ PyObject* PyGcc_RegisterAttribute(PyObject *self, PyObject *args, PyObject *kwargs); /* gcc-python-callbacks.c: */ int PyGcc_IsWithinEvent(enum plugin_event *out_event); PyObject* PyGcc_RegisterCallback(PyObject *self, PyObject *args, PyObject *kwargs); /* gcc-python-callgraph.c: */ PyObject * PyGccCallgraphEdge_repr(struct PyGccCallgraphEdge * self); PyObject * PyGccCallgraphEdge_str(struct PyGccCallgraphEdge * self); PyObject * PyGccCallgraphNode_repr(struct PyGccCallgraphNode * self); PyObject * PyGccCallgraphNode_str(struct PyGccCallgraphNode * self); extern PyObject * PyGccCallgraphNode_get_callers(struct PyGccCallgraphNode * self); extern PyObject * PyGccCallgraphNode_get_callees(struct PyGccCallgraphNode * self); PyObject * PyGcc_get_callgraph_nodes(PyObject *self, PyObject *args); /* gcc-python-diagnostics.c: */ PyObject* PyGcc_permerror(PyObject *self, PyObject *args); PyObject * PyGcc_error(PyObject *self, PyObject *args, PyObject *kwargs); PyObject * PyGcc_warning(PyObject *self, PyObject *args, PyObject *kwargs); PyObject * PyGcc_inform(PyObject *self, PyObject *args, PyObject *kwargs); /* gcc-python-pass.c: */ extern PyObject * PyGccPass_New(struct opt_pass *pass); /* gcc-python-location.c: */ int PyGccLocation_init(PyGccLocation *self, PyObject *args, PyObject *kwargs); PyObject * PyGccLocation_repr(struct PyGccLocation * self); PyObject * PyGccLocation_str(struct PyGccLocation * self); PyObject * PyGccLocation_richcompare(PyObject *o1, PyObject *o2, int op); long PyGccLocation_hash(struct PyGccLocation * self); PyObject * PyGccLocation_offset_column(PyGccLocation *self, PyObject *args); #if (GCC_VERSION >= 6000) PyObject * PyGccRichLocation_add_fixit_replace(PyGccRichLocation *self, PyObject *args, PyObject *kwargs); int PyGccRichLocation_init(PyGccRichLocation *self, PyObject *args, PyObject *kwargs); #endif /* gcc-python-cfg.c: */ PyObject * PyGccBasicBlock_repr(struct PyGccBasicBlock * self); PyObject * PyGccBasicBlock_get_preds(PyGccBasicBlock *self, void *closure); PyObject * PyGccBasicBlock_get_succs(PyGccBasicBlock *self, void *closure); PyObject * PyGccBasicBlock_get_gimple(PyGccBasicBlock *self, void *closure); PyObject * PyGccBasicBlock_get_phi_nodes(PyGccBasicBlock *self, void *closure); PyObject * PyGccBasicBlock_get_rtl(PyGccBasicBlock *self, void *closure); PyObject * PyGccCfg_get_basic_blocks(PyGccCfg *self, void *closure); PyObject * PyGccCfg_get_block_for_label(PyObject *self, PyObject *args); /* autogenerated-tree.c: */ /* return -1 if there isn't an enum tree_code associated with this type */ int PyGcc_tree_type_object_as_tree_code(PyObject *cls, enum tree_code *out); /* gcc-python-tree.c: */ /* FIXME: autogenerate these: */ extern gcc_decl PyGccTree_as_gcc_decl(struct PyGccTree * self); extern gcc_type PyGccTree_as_gcc_type(struct PyGccTree * self); extern gcc_fixed_point_type PyGccTree_as_gcc_fixed_point_type(struct PyGccTree * self); extern gcc_integer_type PyGccTree_as_gcc_integer_type(struct PyGccTree * self); extern gcc_real_type PyGccTree_as_gcc_real_type(struct PyGccTree * self); extern gcc_translation_unit_decl PyGccTree_as_gcc_translation_unit_decl(struct PyGccTree * self); extern gcc_ssa_name PyGccTree_as_gcc_ssa_name(struct PyGccTree * self); extern gcc_case_label_expr PyGccTree_as_gcc_case_label_expr(struct PyGccTree * self); extern PyObject * PyGccBlock_New(gcc_block t); extern PyObject * PyGccPointerType_New(gcc_pointer_type t); PyObject * PyGccPointerType_repr(struct PyGccTree * self); extern PyObject * PyGccCaseLabelExpr_New(gcc_case_label_expr t); PyObject * PyGccTree_str(struct PyGccTree * self); long PyGccTree_hash(struct PyGccTree * self); PyObject * PyGccTree_richcompare(PyObject *o1, PyObject *o2, int op); PyObject * PyGccTree_get_str_no_uid(struct PyGccTree *self, void *closure); PyObject * PyGccTree_get_symbol(PyObject *cls, PyObject *args); PyObject * PyGccFunction_repr(struct PyGccFunction * self); long PyGccFunction_hash(struct PyGccFunction * self); PyObject * PyGccFunction_richcompare(PyObject *o1, PyObject *o2, int op); PyObject * PyGccArrayRef_repr(PyObject *self); PyObject * PyGccComponentRef_repr(PyObject *self); PyObject * PyGccDeclaration_get_name(struct PyGccTree *self, void *closure); PyObject * PyGccDeclaration_repr(struct PyGccTree * self); PyObject * PyGccFunctionDecl_get_fullname(struct PyGccTree *self, void *closure); PyObject * PyGccFunctionDecl_get_callgraph_node(struct PyGccTree *self, void *closure); PyObject * PyGccIdentifierNode_repr(struct PyGccTree * self); PyObject * PyGccType_get_attributes(struct PyGccTree *self, void *closure); PyObject * PyGccType_get_sizeof(struct PyGccTree *self, void *closure); PyObject * PyGccFunction_TypeObj_get_argument_types(struct PyGccTree * self,void *closure); PyObject * PyGccFunction_TypeObj_is_variadic(struct PyGccTree * self, void *closure); PyObject * PyGccConstructor_get_elements(PyObject *self, void *closure); PyObject * PyGccIntegerConstant_get_constant(struct PyGccTree * self, void *closure); PyObject * PyGccIntegerConstant_repr(struct PyGccTree * self); PyObject * PyGccRealCst_get_constant(struct PyGccTree * self, void *closure); PyObject * PyGccRealCst_repr(struct PyGccTree * self); PyObject * PyGccIntegerType_get_signed_equivalent(struct PyGccTree * self, void *closure); PyObject * PyGccIntegerType_get_unsigned_equivalent(struct PyGccTree * self, void *closure); PyObject * PyGccIntegerType_repr(struct PyGccTree * self); PyObject * PyGccMethodType_get_argument_types(struct PyGccTree * self,void *closure); PyObject * PyGccMethodType_is_variadic(struct PyGccTree * self,void *closure); PyObject * PyGccStringConstant_repr(struct PyGccTree * self); PyObject * PyGccTypeDecl_get_pointer(struct PyGccTree *self, void *closure); PyObject * PyGccSsaName_repr(struct PyGccTree * self); PyObject * PyGccTreeList_repr(struct PyGccTree * self); PyObject * PyGccCaseLabelExpr_repr(PyObject *self); PyObject * PyGccNamespaceDecl_lookup(struct PyGccTree * self, PyObject *args, PyObject *kwargs); PyObject * PyGccNamespaceDecl_unalias(struct PyGccTree * self, PyObject *args, PyObject *kwargs); PyObject * PyGccNamespaceDecl_declarations(tree t); PyObject * PyGccNamespaceDecl_namespaces(tree t); PyObject * PyGcc_GetFields(struct PyGccTree *self); PyObject * PyGcc_GetMethods(struct PyGccTree *self); /* gcc-python-gimple.c: */ extern gcc_gimple_asm PyGccGimple_as_gcc_gimple_asm(struct PyGccGimple *self); extern gcc_gimple_assign PyGccGimple_as_gcc_gimple_assign(struct PyGccGimple *self); extern gcc_gimple_call PyGccGimple_as_gcc_gimple_call(struct PyGccGimple *self); extern gcc_gimple_return PyGccGimple_as_gcc_gimple_return(struct PyGccGimple *self); extern gcc_gimple_cond PyGccGimple_as_gcc_gimple_cond(struct PyGccGimple *self); extern gcc_gimple_phi PyGccGimple_as_gcc_gimple_phi(struct PyGccGimple *self); extern gcc_gimple_switch PyGccGimple_as_gcc_gimple_switch(struct PyGccGimple *self); extern gcc_gimple_label PyGccGimple_as_gcc_gimple_label(struct PyGccGimple *self); PyObject * PyGccGimple_repr(struct PyGccGimple * self); PyObject * PyGccGimple_str(struct PyGccGimple * self); long PyGccGimple_hash(struct PyGccGimple * self); PyObject * PyGccGimple_richcompare(PyObject *o1, PyObject *o2, int op); PyObject * PyGccGimple_walk_tree(struct PyGccGimple * self, PyObject *args, PyObject *kwargs); PyObject * PyGccGimple_get_rhs(struct PyGccGimple *self, void *closure); PyObject * PyGccGimple_get_str_no_uid(struct PyGccGimple *self, void *closure); PyObject * PyGccGimpleCall_get_args(struct PyGccGimple *self, void *closure); PyObject * PyGccGimpleLabel_repr(PyObject * self); PyObject * PyGccGimplePhi_get_args(struct PyGccGimple *self, void *closure); PyObject * PyGccGimpleSwitch_get_labels(struct PyGccGimple *self, void *closure); /* gcc-python-option.c: */ int PyGcc_option_is_enabled(enum opt_code opt_code); const struct cl_option* PyGcc_option_to_cl_option(PyGccOption * self); int PyGccOption_init(PyGccOption * self, PyObject *args, PyObject *kwargs); PyObject * PyGccOption_repr(PyGccOption * self); PyObject * PyGccOption_is_enabled(PyGccOption * self, void *closure); /* gcc-python-pass.c: */ int PyGccGimplePass_init(PyObject *self, PyObject *args, PyObject *kwds); int PyGccRtlPass_init(PyObject *self, PyObject *args, PyObject *kwds); int PyGccSimpleIpaPass_init(PyObject *self, PyObject *args, PyObject *kwds); int PyGccIpaPass_init(PyObject *self, PyObject *args, PyObject *kwds); PyObject * PyGccPass_repr(struct PyGccPass * self); PyObject * PyGccPass_get_dump_enabled(struct PyGccPass *self, void *closure); int PyGccPass_set_dump_enabled(struct PyGccPass *self, PyObject *value, void *closure); PyObject * PyGccPass_get_roots(PyObject *cls, PyObject *noargs); PyObject * PyGccPass_get_by_name(PyObject *cls, PyObject *args, PyObject *kwargs); PyObject * PyGccPass_register_before(struct PyGccPass *self, PyObject *args, PyObject *kwargs); PyObject * PyGccPass_register_after(struct PyGccPass *self, PyObject *args, PyObject *kwargs); PyObject * PyGccPass_replace(struct PyGccPass *self, PyObject *args, PyObject *kwargs); /* gcc-python-pretty-printer.c: */ #include "pretty-print.h" struct PyGccPrettyPrinter { PyObject_HEAD pretty_printer pp; FILE *file_ptr; char buf[1024]; /* FIXME */ }; extern PyTypeObject PyGccPrettyPrinter_TypeObj; PyObject* PyGccPrettyPrinter_New(void); pretty_printer* PyGccPrettyPrinter_as_pp(PyObject *obj); PyObject* PyGccPrettyPrinter_as_string(PyObject *obj); void PyGccPrettyPrinter_dealloc(PyObject *obj); /* gcc-python-rtl.c: */ #if (GCC_VERSION < 5000) PyObject * PyGccRtl_get_location(struct PyGccRtl *self, void *closure); #endif PyObject * PyGccRtl_get_operands(struct PyGccRtl *self, void *closure); PyObject * PyGccRtl_repr(struct PyGccRtl * self); PyObject * PyGccRtl_str(struct PyGccRtl * self); PyObject * PyGcc_TreeListFromChain(tree t); PyObject * PyGcc_TreeListFromChainWithFilter(tree t, int (*filter) (tree, void *), void *user_data); PyObject * PyGcc_TreeMakeListFromTreeList(tree t); PyObject * PyGcc_TreeMakeListOfPairsFromTreeListChain(tree t); /* gcc-python-version.c: */ void PyGcc_version_init(struct plugin_gcc_version *version); PyObject * PyGcc_get_plugin_gcc_version(PyObject *self, PyObject *args); PyObject * PyGcc_get_gcc_version(PyObject *self, PyObject *args); /* gcc-python-wrappers.c: */ void PyGcc_wrapper_init(void); PyObject * PyGcc__force_garbage_collection(PyObject *self, PyObject *args); PyObject * PyGcc__gc_selftest(PyObject *self, PyObject *args); /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ #endif /* INCLUDED__WRAPPERS_H */ gcc-python-plugin-0.17/gcc-python.c000066400000000000000000000645361342215241600172450ustar00rootroot00000000000000/* Copyright 2011-2013, 2015 David Malcolm Copyright 2011-2013, 2015 Red Hat, Inc. This 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 . */ #include #include "gcc-python.h" #include "gcc-python-closure.h" #include "gcc-python-wrappers.h" #include "gcc-c-api/gcc-location.h" #include "gcc-c-api/gcc-variable.h" #include "gcc-c-api/gcc-declaration.h" #include "gcc-c-api/gcc-diagnostics.h" #include "gcc-c-api/gcc-option.h" int plugin_is_GPL_compatible; #include "plugin-version.h" #if 1 /* Ideally we wouldn't have these includes here: */ #if (GCC_VERSION >= 6000) #include "cp/cp-tree.h" /* for cp_expr */ #endif #include "cp/name-lookup.h" /* for global_namespace */ #include "tree.h" #include "function.h" #include "cgraph.h" //#include "opts.h" /* "maybe_get_identifier" was moved from tree.h to stringpool.h in 4.9 */ #if (GCC_VERSION >= 4009) #include "stringpool.h" #endif /* * Use an unqualified name here and rely on dual search paths to let the * compiler find it. This deals with c-pragma.h moving to a * subdirectory in newer versions of gcc. */ #include "c-pragma.h" /* for parse_in */ #endif #if 0 #define LOG(msg) \ (void)fprintf(stderr, "%s:%i:%s\n", __FILE__, __LINE__, (msg)) #else #define LOG(msg) ((void)0); #endif #define GCC_PYTHON_TRACE_ALL_EVENTS 0 #if GCC_PYTHON_TRACE_ALL_EVENTS static const char* event_name[] = { #define DEFEVENT(NAME) \ #NAME, # include "plugin.def" # undef DEFEVENT }; static void trace_callback(enum plugin_event event, void *gcc_data, void *user_data) { fprintf(stderr, "%s:%i:trace_callback(%s, %p, %p)\n", __FILE__, __LINE__, event_name[event], gcc_data, user_data); fprintf(stderr, " cfun:%p\n", cfun); } #define DEFEVENT(NAME) \ static void trace_callback_for_##NAME(void *gcc_data, void *user_data) \ { \ trace_callback(NAME, gcc_data, user_data); \ } # include "plugin.def" # undef DEFEVENT #endif /* GCC_PYTHON_TRACE_ALL_EVENTS */ /* Weakly import parse_in; it will be non-NULL in the C and C++ frontend, but it's not available lto1 (link-time optimization) */ __typeof__ (parse_in) parse_in __attribute__ ((weak)); static PyObject* PyGcc_define_macro(PyObject *self, PyObject *args, PyObject *kwargs) { const char *macro; const char *keywords[] = {"macro", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:define_preprocessor_name", (char**)keywords, ¯o)) { return NULL; } if (0) { fprintf(stderr, "gcc.define_macro(\"%s\")\n", macro); } if (!parse_in) { return PyErr_Format(PyExc_ValueError, "gcc.define_macro(\"%s\") called without a compilation unit", macro); } if (!PyGcc_IsWithinEvent(NULL)) { return PyErr_Format(PyExc_ValueError, "gcc.define_macro(\"%s\") called from outside an event callback", macro); } cpp_define (parse_in, macro); Py_RETURN_NONE; } static PyObject * PyGcc_set_location(PyObject *self, PyObject *args) { PyGccLocation *loc_obj; if (!PyArg_ParseTuple(args, "O!:set_location", &PyGccLocation_TypeObj, &loc_obj)) { return NULL; } gcc_set_input_location(loc_obj->loc); Py_RETURN_NONE; } static bool add_option_to_list(gcc_option opt, void *user_data) { PyObject *result = (PyObject*)user_data; PyObject *obj_opt; obj_opt = PyGccOption_New(opt); if (!obj_opt) { return true; } if (-1 == PyList_Append(result, obj_opt)) { Py_DECREF(obj_opt); return true; } /* Success: */ Py_DECREF(obj_opt); return false; } static PyObject * PyGcc_get_option_list(PyObject *self, PyObject *args) { PyObject *result; result = PyList_New(0); if (!result) { return NULL; } if (gcc_for_each_option(add_option_to_list, result)) { Py_DECREF(result); return NULL; } /* Success: */ return result; } static bool add_option_to_dict(gcc_option opt, void *user_data) { PyObject *dict = (PyObject*)user_data; PyObject *opt_obj; opt_obj = PyGccOption_New(opt); if (!opt_obj) { return true; } if (-1 == PyDict_SetItemString(dict, gcc_option_get_text(opt), opt_obj)) { Py_DECREF(opt_obj); return true; } Py_DECREF(opt_obj); return false; } static PyObject * PyGcc_get_option_dict(PyObject *self, PyObject *args) { PyObject *dict; dict = PyDict_New(); if (!dict) { return NULL; } if (gcc_for_each_option(add_option_to_dict, dict)) { Py_DECREF(dict); return NULL; } /* Success: */ return dict; } static PyObject * PyGcc_get_parameters(PyObject *self, PyObject *args) { PyObject *dict; size_t i; dict = PyDict_New(); if (!dict) { goto error; } for (i = 0; i < get_num_compiler_params(); i++) { PyObject *param_obj = PyGccParameter_New((compiler_param)i); if (!param_obj) { goto error; } if (-1 == PyDict_SetItemString(dict, compiler_params[i].option, param_obj)) { Py_DECREF(param_obj); goto error; } Py_DECREF(param_obj); } return dict; error: Py_XDECREF(dict); return NULL; } IMPL_APPENDER(add_var_to_list, gcc_variable, PyGccVariable_New) static PyObject * PyGcc_get_variables(PyObject *self, PyObject *args) { IMPL_GLOBAL_LIST_MAKER(gcc_for_each_variable, add_var_to_list) } static PyObject * PyGcc_maybe_get_identifier(PyObject *self, PyObject *args) { const char *str; tree t; if (!PyArg_ParseTuple(args, "s:maybe_get_identifier", &str)) { return NULL; } t = maybe_get_identifier(str); return PyGccTree_New(gcc_private_make_tree(t)); } static PyObject *PyGcc_make_translation_unit_decl(gcc_translation_unit_decl decl) { gcc_tree tree = gcc_translation_unit_decl_as_gcc_tree(decl); return PyGccTree_New(tree); } IMPL_APPENDER(add_translation_unit_decl_to_list, gcc_translation_unit_decl, PyGcc_make_translation_unit_decl) static PyObject * PyGcc_get_translation_units(PyObject *self, PyObject *args) { IMPL_GLOBAL_LIST_MAKER(gcc_for_each_translation_unit_decl, add_translation_unit_decl_to_list) } /* Weakly import global_namespace; it will be non-NULL for the C++ frontend. GCC 8's r247599 (aka c99e91fe8d1191b348cbc1659fbfbac2fc07e154) converted it from: extern GTY(()) tree global_namespace; to: #define global_namespace cp_global_trees[CPTI_GLOBAL] so we have to access cp_global_trees instead. */ #if (GCC_VERSION >= 8000) __typeof__ (cp_global_trees) cp_global_trees __attribute__ ((weak)); #else __typeof__ (global_namespace) global_namespace __attribute__ ((weak)); #endif /* #if (GCC_VERSION >= 8000) */ static PyObject * PyGcc_get_global_namespace(PyObject *self, PyObject *args) { /* (global_namespace will be NULL outside the C++ frontend, giving a result of None) */ return PyGccTree_New(gcc_private_make_tree(global_namespace)); } /* Dump files */ static PyObject * PyGcc_dump(PyObject *self, PyObject *arg) { PyObject *str_obj; /* gcc/output.h: declares: extern FILE *dump_file; This is NULL when not defined. */ if (!dump_file) { /* The most common case; make it fast */ Py_RETURN_NONE; } str_obj = PyObject_Str(arg); if (!str_obj) { return NULL; } /* FIXME: encoding issues */ /* FIXME: GIL */ if (!fwrite(PyGccString_AsString(str_obj), strlen(PyGccString_AsString(str_obj)), 1, dump_file)) { Py_DECREF(str_obj); return PyErr_SetFromErrnoWithFilename(PyExc_IOError, dump_file_name); } Py_DECREF(str_obj); Py_RETURN_NONE; } static PyObject * PyGcc_get_dump_file_name(PyObject *self, PyObject *noargs) { /* gcc/tree-pass.h declares: extern const char *dump_file_name; */ return PyGccStringOrNone(dump_file_name); } static PyObject * PyGcc_get_dump_base_name(PyObject *self, PyObject *noargs) { /* The generated gcc/options.h has: #ifdef GENERATOR_FILE extern const char *dump_base_name; #else const char *x_dump_base_name; #define dump_base_name global_options.x_dump_base_name #endif */ return PyGccStringOrNone(dump_base_name); } static PyObject * PyGcc_get_is_lto(PyObject *self, PyObject *noargs) { /* The generated gcc/options.h has: #ifdef GENERATOR_FILE extern bool in_lto_p; #else bool x_in_lto_p; #define in_lto_p global_options.x_in_lto_p #endif */ return PyBool_FromLong(in_lto_p); } static PyMethodDef GccMethods[] = { {"register_attribute", (PyCFunction)PyGcc_RegisterAttribute, (METH_VARARGS | METH_KEYWORDS), "Register an attribute."}, {"register_callback", (PyCFunction)PyGcc_RegisterCallback, (METH_VARARGS | METH_KEYWORDS), "Register a callback, to be called when various GCC events occur."}, {"define_macro", (PyCFunction)PyGcc_define_macro, (METH_VARARGS | METH_KEYWORDS), "Pre-define a named value in the preprocessor."}, /* Diagnostics: */ {"permerror", PyGcc_permerror, METH_VARARGS, NULL}, {"error", (PyCFunction)PyGcc_error, (METH_VARARGS | METH_KEYWORDS), ("Report an error\n" "FIXME\n")}, {"warning", (PyCFunction)PyGcc_warning, (METH_VARARGS | METH_KEYWORDS), ("Report a warning\n" "FIXME\n")}, {"inform", (PyCFunction)PyGcc_inform, (METH_VARARGS | METH_KEYWORDS), ("Report an information message\n" "FIXME\n")}, {"set_location", (PyCFunction)PyGcc_set_location, METH_VARARGS, ("Temporarily set the default location for error reports\n")}, /* Options: */ {"get_option_list", PyGcc_get_option_list, METH_NOARGS, "Get all command-line options, as a list of gcc.Option instances"}, {"get_option_dict", PyGcc_get_option_dict, METH_NOARGS, ("Get all command-line options, as a dict from command-line text strings " "to gcc.Option instances")}, {"get_parameters", PyGcc_get_parameters, METH_NOARGS, "Get all tunable GCC parameters. Returns a dictionary, mapping from" "option name -> gcc.Parameter instance"}, {"get_variables", PyGcc_get_variables, METH_NOARGS, "Get all variables in this compilation unit as a list of gcc.Variable"}, {"maybe_get_identifier", PyGcc_maybe_get_identifier, METH_VARARGS, "Get the gcc.IdentifierNode with this name, if it exists, otherwise None"}, {"get_translation_units", PyGcc_get_translation_units, METH_NOARGS, "Get a list of all gcc.TranslationUnitDecl"}, {"get_global_namespace", PyGcc_get_global_namespace, METH_NOARGS, "C++: get the global namespace (aka '::') as a gcc.NamespaceDecl"}, /* Version handling: */ {"get_plugin_gcc_version", PyGcc_get_plugin_gcc_version, METH_NOARGS, "Get the gcc.Version that this plugin was compiled with"}, {"get_gcc_version", PyGcc_get_gcc_version, METH_NOARGS, "Get the gcc.Version for this version of GCC"}, {"get_callgraph_nodes", PyGcc_get_callgraph_nodes, METH_VARARGS, "Get a list of all gcc.CallgraphNode instances"}, /* Dump files */ {"dump", PyGcc_dump, METH_O, "Dump str() of the argument to the current dump file (or silently discard it when no dump file is open)"}, {"get_dump_file_name", PyGcc_get_dump_file_name, METH_NOARGS, "Get the name of the current dump file (or None)"}, {"get_dump_base_name", PyGcc_get_dump_base_name, METH_NOARGS, "Get the base name used when writing dump files"}, {"is_lto", PyGcc_get_is_lto, METH_NOARGS, "Determine whether or not we're being invoked during link-time optimization"}, /* Garbage collection */ {"_force_garbage_collection", PyGcc__force_garbage_collection, METH_NOARGS, "Forcibly trigger a single run of GCC's garbage collector"}, {"_gc_selftest", PyGcc__gc_selftest, METH_NOARGS, "Run a garbage-collection selftest"}, /* Sentinel: */ {NULL, NULL, 0, NULL} }; static struct { PyObject *module; PyObject *argument_dict; PyObject *argument_tuple; } PyGcc_globals; #if PY_MAJOR_VERSION == 3 static struct PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "gcc", /* name of module */ NULL, -1, GccMethods }; #endif PyMODINIT_FUNC PyInit_gcc(void) { #if PY_MAJOR_VERSION == 3 PyObject *m; m = PyModule_Create(&module_def); #else Py_InitModule("gcc", GccMethods); #endif #if PY_MAJOR_VERSION == 3 return m; #endif } static int PyGcc_init_gcc_module(struct plugin_name_args *plugin_info) { int i; if (!PyGcc_globals.module) { return 0; } /* Set up int constants for each of the enum plugin_event values: */ #define DEFEVENT(NAME) \ PyModule_AddIntMacro(PyGcc_globals.module, NAME); # include "plugin.def" # undef DEFEVENT PyGcc_globals.argument_dict = PyDict_New(); if (!PyGcc_globals.argument_dict) { return 0; } PyGcc_globals.argument_tuple = PyTuple_New(plugin_info->argc); if (!PyGcc_globals.argument_tuple) { return 0; } /* PySys_SetArgvEx(plugin_info->argc, plugin_info->argv, 0); */ for (i=0; iargc; i++) { struct plugin_argument *arg = &plugin_info->argv[i]; PyObject *key; PyObject *value; PyObject *pair; key = PyGccString_FromString(arg->key); if (arg->value) { value = PyGccString_FromString(plugin_info->argv[i].value); } else { value = Py_None; } PyDict_SetItem(PyGcc_globals.argument_dict, key, value); // FIXME: ref counts? pair = Py_BuildValue("(s, s)", arg->key, arg->value); if (!pair) { return 1; } PyTuple_SetItem(PyGcc_globals.argument_tuple, i, pair); } PyModule_AddObject(PyGcc_globals.module, "argument_dict", PyGcc_globals.argument_dict); PyModule_AddObject(PyGcc_globals.module, "argument_tuple", PyGcc_globals.argument_tuple); /* Pass properties: */ PyModule_AddIntMacro(PyGcc_globals.module, PROP_gimple_any); PyModule_AddIntMacro(PyGcc_globals.module, PROP_gimple_lcf); PyModule_AddIntMacro(PyGcc_globals.module, PROP_gimple_leh); PyModule_AddIntMacro(PyGcc_globals.module, PROP_cfg); #if (GCC_VERSION >= 4008) /* PROP_referenced_vars went away in GCC 4.8 (in r190067) */ #else PyModule_AddIntMacro(PyGcc_globals.module, PROP_referenced_vars); #endif PyModule_AddIntMacro(PyGcc_globals.module, PROP_ssa); PyModule_AddIntMacro(PyGcc_globals.module, PROP_no_crit_edges); PyModule_AddIntMacro(PyGcc_globals.module, PROP_rtl); PyModule_AddIntMacro(PyGcc_globals.module, PROP_gimple_lomp); PyModule_AddIntMacro(PyGcc_globals.module, PROP_cfglayout); PyModule_AddIntMacro(PyGcc_globals.module, PROP_gimple_lcx); PyModule_AddIntMacro(PyGcc_globals.module, GCC_VERSION); /* Success: */ return 1; } static void PyGcc_run_any_command(void) { PyObject* command_obj; /* borrowed ref */ int result; const char *command_str; command_obj = PyDict_GetItemString(PyGcc_globals.argument_dict, "command"); if (!command_obj) { return; } command_str = PyGccString_AsString(command_obj); if (0) { fprintf(stderr, "Running: %s\n", command_str); } result = PyRun_SimpleString(command_str); if (-1 == result) { /* Error running the python command */ Py_Finalize(); exit(1); } } static void PyGcc_run_any_script(void) { PyObject* script_name; FILE *fp; int result; script_name = PyDict_GetItemString(PyGcc_globals.argument_dict, "script"); if (!script_name) { return; } fp = fopen(PyGccString_AsString(script_name), "r"); if (!fp) { fprintf(stderr, "Unable to read python script: %s\n", PyGccString_AsString(script_name)); exit(1); } result = PyRun_SimpleFile(fp, PyGccString_AsString(script_name)); fclose(fp); if (-1 == result) { /* Error running the python script */ Py_Finalize(); exit(1); } } int setup_sys(struct plugin_name_args *plugin_info) { /* * Sets up "sys.plugin_full_name" as plugin_info->full_name. This is the path to the plugin (as specified with -fplugin=) * Sets up "sys.plugin_base_name" as plugin_info->base_name. This is the short name, of the plugin (filename without .so suffix) * Add the directory containing the plugin to "sys.path", so that it can find modules relative to itself without needing PYTHONPATH to be set up. (sys.path has already been initialized by the call to Py_Initialize) * If PLUGIN_PYTHONPATH is defined, add it to "sys.path" */ int result = 0; /* failure */ PyObject *full_name = NULL; PyObject *base_name = NULL; const char *program = "import sys;\n" "import os;\n" "sys.path.append(os.path.abspath(os.path.dirname(sys.plugin_full_name)))\n"; /* Setup "sys.plugin_full_name" */ full_name = PyGccString_FromString(plugin_info->full_name); if (!full_name) { goto error; } if (-1 == PySys_SetObject((char*)"plugin_full_name", full_name)) { goto error; } /* Setup "sys.plugin_base_name" */ base_name = PyGccString_FromString(plugin_info->base_name); if (!base_name) { goto error; } if (-1 == PySys_SetObject((char*)"plugin_base_name", base_name)) { goto error; } /* Add the plugin's path to sys.path */ if (-1 == PyRun_SimpleString(program)) { goto error; } #ifdef PLUGIN_PYTHONPATH { /* Support having multiple builds of the plugin installed independently of each other, by supporting each having a directory for support files e.g. gccutils, libcpychecker, etc We do this by seeing if PLUGIN_PYTHONPATH was defined in the compile, and if so, adding it to sys.path: */ const char *program2 = "import sys;\n" "import os;\n" "sys.path.append('" PLUGIN_PYTHONPATH "')\n"; if (-1 == PyRun_SimpleString(program2)) { goto error; } } #endif /* Success: */ result = 1; error: Py_XDECREF(full_name); Py_XDECREF(base_name); return result; } /* Wired up to PLUGIN_FINISH, this callback handles finalization for the plugin: */ void on_plugin_finish(void *gcc_data, void *user_data) { /* Clean up the python runtime. For python 3, this flushes buffering of sys.stdout and sys.stderr */ Py_Finalize(); } extern int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) __attribute__((nonnull)); int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) { LOG("plugin_init started"); if (!plugin_default_version_check (version, &gcc_version)) { return 1; } #if PY_MAJOR_VERSION >= 3 /* Python 3 added internal buffering to sys.stdout and sys.stderr, but this leads to unpredictable interleavings of messages from gcc, such as from calling gcc.warning() vs those from python scripts, such as from print() and sys.stdout.write() Suppress the buffering, to better support mixed gcc/python output: */ Py_UnbufferedStdioFlag = 1; #endif PyImport_AppendInittab("gcc", PyInit_gcc); LOG("calling Py_Initialize..."); Py_Initialize(); LOG("Py_Initialize finished"); PyGcc_globals.module = PyImport_ImportModule("gcc"); PyEval_InitThreads(); if (!PyGcc_init_gcc_module(plugin_info)) { return 1; } if (!setup_sys(plugin_info)) { return 1; } /* Init other modules */ PyGcc_wrapper_init(); /* FIXME: properly integrate them within the module hierarchy */ PyGcc_version_init(version); autogenerated_callgraph_init_types(); /* FIXME: error checking! */ autogenerated_cfg_init_types(); /* FIXME: error checking! */ autogenerated_function_init_types(); /* FIXME: error checking! */ autogenerated_gimple_init_types(); /* FIXME: error checking! */ autogenerated_location_init_types(); /* FIXME: error checking! */ autogenerated_option_init_types(); /* FIXME: error checking! */ autogenerated_parameter_init_types(); /* FIXME: error checking! */ autogenerated_pass_init_types(); /* FIXME: error checking! */ autogenerated_pretty_printer_init_types(); /* FIXME: error checking! */ autogenerated_rtl_init_types(); /* FIXME: error checking! */ autogenerated_tree_init_types(); /* FIXME: error checking! */ autogenerated_variable_init_types(); /* FIXME: error checking! */ autogenerated_callgraph_add_types(PyGcc_globals.module); autogenerated_cfg_add_types(PyGcc_globals.module); autogenerated_function_add_types(PyGcc_globals.module); autogenerated_gimple_add_types(PyGcc_globals.module); autogenerated_location_add_types(PyGcc_globals.module); autogenerated_option_add_types(PyGcc_globals.module); autogenerated_parameter_add_types(PyGcc_globals.module); autogenerated_pass_add_types(PyGcc_globals.module); autogenerated_pretty_printer_add_types(PyGcc_globals.module); autogenerated_rtl_add_types(PyGcc_globals.module); autogenerated_tree_add_types(PyGcc_globals.module); autogenerated_variable_add_types(PyGcc_globals.module); /* Register at-exit finalization for the plugin: */ register_callback(plugin_info->base_name, PLUGIN_FINISH, on_plugin_finish, NULL); PyGcc_run_any_command(); PyGcc_run_any_script(); //printf("%s:%i:got here\n", __FILE__, __LINE__); #if GCC_PYTHON_TRACE_ALL_EVENTS #define DEFEVENT(NAME) \ if (NAME != PLUGIN_PASS_MANAGER_SETUP && \ NAME != PLUGIN_INFO && \ NAME != PLUGIN_REGISTER_GGC_ROOTS && \ NAME != PLUGIN_REGISTER_GGC_CACHES) { \ register_callback(plugin_info->base_name, NAME, \ trace_callback_for_##NAME, NULL); \ } # include "plugin.def" # undef DEFEVENT #endif /* GCC_PYTHON_TRACE_ALL_EVENTS */ LOG("init_plugin finished"); return 0; } PyObject * PyGccStringOrNone(const char *str_or_null) { if (str_or_null) { return PyGccString_FromString(str_or_null); } else { Py_RETURN_NONE; } } PyObject * PyGcc_int_from_decimal_string_buffer(const char *buf) { PyObject *long_obj; #if PY_MAJOR_VERSION < 3 long long_val; int overflow; #endif long_obj = PyLong_FromString((char *)buf, NULL, 10); if (!long_obj) { return NULL; } #if PY_MAJOR_VERSION >= 3 return long_obj; #else long_val = PyLong_AsLongAndOverflow(long_obj, &overflow); if (overflow) { /* Doesn't fit in a PyIntObject; use the PyLongObject: */ return long_obj; } else { /* Fits in a PyIntObject: use that */ PyObject *int_obj = PyInt_FromLong(long_val); if (!int_obj) { return long_obj; } Py_DECREF(long_obj); return int_obj; } #endif } #if (GCC_VERSION < 5000) /* "double_int" is declared in gcc/double-int.h as a pair of HOST_WIDE_INT. These in turn are defined in gcc/hwint.h as a #define to one of "long", "long long", or "__int64". It appears that they can be interpreted as either "unsigned" or "signed" How to convert this to other types? We "cheat", and take it through the decimal representation, then convert from decimal. This is probably slow, but is (I hope) at least correct. */ void PyGcc_DoubleIntAsText(double_int di, bool is_unsigned, char *out, int bufsize) { FILE *f; assert(out); assert(bufsize > 256); /* FIXME */ out[0] = '\0'; f = fmemopen(out, bufsize, "w"); dump_double_int (f, di, is_unsigned); fclose(f); } PyObject * PyGcc_int_from_double_int(double_int di, bool is_unsigned) { char buf[512]; /* FIXME */ PyGcc_DoubleIntAsText(di, is_unsigned, buf, sizeof(buf)); return PyGcc_int_from_decimal_string_buffer(buf); } #endif /* #if (GCC_VERSION < 5000) */ /* GCC's headers "poison" strdup to make it unavailable, so we provide our own. The buffer is allocated using PyMem_Malloc */ char * PyGcc_strdup(const char *str) { char *result; char *dst; result = (char*)PyMem_Malloc(strlen(str) + 1); if (!result) { return NULL; } dst = result; while (*str) { *(dst++) = *(str++); } *dst = '\0'; return result; } void PyGcc_PrintException(const char *msg) { /* Handler for Python exceptions */ assert(msg); /* Emit a gcc error, using GCC's "input_location" Typically, by the time our code is running, that's generally just the end of the source file. The value is saved and restored whenever calling into Python code, and within passes is initialized to the top of the function; it can be temporarily overridden using gcc.set_location() */ gcc_error_at(gcc_get_input_location(), msg); /* Print the traceback: */ PyErr_PrintEx(1); } PyObject * PyGcc_GetReprOfAttribute(PyObject *obj, const char *attrname) { PyObject *attr_obj; PyObject *attr_repr; attr_obj = PyObject_GetAttrString(obj, attrname); if (!attr_obj) { return NULL; } attr_repr = PyObject_Repr(attr_obj); if (!attr_repr) { Py_DECREF(attr_obj); return NULL; } return attr_repr; } /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ gcc-python-plugin-0.17/gcc-python.h000066400000000000000000000274021342215241600172410ustar00rootroot00000000000000/* Copyright 2011-2013, 2015, 2017 David Malcolm Copyright 2011-2013, 2015, 2017 Red Hat, Inc. This 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 . */ #ifndef INCLUDED__GCC_PYTHON_H #define INCLUDED__GCC_PYTHON_H #include #include "autogenerated-config.h" #if 1 #include "tree.h" #if (GCC_VERSION >= 4009) #include "basic-block.h" /* needed by gimple.h in 4.9 */ #include "function.h" /* needed by gimple.h in 4.9 */ #include "tree-ssa-alias.h" /* needed by gimple.h in 4.9 */ #include "internal-fn.h" /* needed by gimple.h in 4.9 */ #include "is-a.h" /* needed by gimple.h in 4.9 */ #include "predict.h" /* needed by gimple.h in 4.9 */ #include "gimple-expr.h" /* needed by gimple.h in 4.9 */ #endif #include "gimple.h" #include "params.h" #endif #include "gcc-c-api/gcc-cfg.h" /* GCC doesn't seem to give us an ID for "invalid event", so invent one: */ #define GCC_PYTHON_PLUGIN_BAD_EVENT (0xffff) /* Define some macros to allow us to use cpychecker's custom attributes when compiling the plugin using itself (gcc-with-cpychecker), but also to turn them off when compiling with vanilla gcc (and other compilers). */ #if defined(WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE) #define CPYCHECKER_RETURNS_BORROWED_REF \ __attribute__((cpychecker_returns_borrowed_ref)) #else #define CPYCHECKER_RETURNS_BORROWED_REF #endif #if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE) #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) \ __attribute__((cpychecker_steals_reference_to_arg(n))) #else #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) #endif #if defined(WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE) #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) \ __attribute__((cpychecker_type_object_for_typedef(typename))) #else #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) #endif /* PyObject shared header for wrapping GCC objects, for integration with GCC's garbage collector (so that things we wrap don't get collected from under us) */ typedef struct PyGccWrapper { PyObject_HEAD /* Keep track of a linked list of all live wrapper objects, so that we can mark the wrapped objects for GCC's garbage collector: */ struct PyGccWrapper *wr_prev; struct PyGccWrapper *wr_next; } PyGccWrapper; /* PyTypeObject subclass for PyGccWrapper, adding a GC-marking callback: */ typedef void (*wrtp_marker) (PyGccWrapper *wrapper); typedef struct PyGccWrapperTypeObject { PyHeapTypeObject wrtp_base; /* Callback for marking the wrapped objects when GCC's garbage collector runs: */ wrtp_marker wrtp_mark; } PyGccWrapperTypeObject; /* gcc-python-wrapper.c */ /* Allocate a new PyGccWrapper of the given type, setting up: - ob_refcnt - ob_type and calling PyGccWrapper_Track() on it: */ #define PyGccWrapper_New(ARG_structname, ARG_typeobj) \ ( (ARG_structname*) _PyGccWrapper_New(ARG_typeobj) ) extern PyGccWrapper * _PyGccWrapper_New(PyGccWrapperTypeObject *typeobj); extern void PyGccWrapper_Track(PyGccWrapper *obj); extern void PyGccWrapper_Dealloc(PyObject *obj); extern PyTypeObject PyGccWrapperMeta_TypeObj; /* Macro DECLARE_SIMPLE_WRAPPER(): ARG_structname: the struct tag for the resulting python wrapper class, e.g. "PyGccPass" ARG_typeobj: the singleton PyTypeObject for the resulting python wrapper class e.g. "PyGccPass_TypeObj" ARG_typename: a C identifier to use when referring to instances of the underlying type e.g. "pass" ARG_wrappedtype: the GCC type being wrapped e.g. "struct opt_pass *" ARG_fieldname: the name of the field within the CPython struct, containing the pointer to the GCC data */ #define DECLARE_SIMPLE_WRAPPER(ARG_structname, ARG_typeobj, ARG_typename, ARG_wrappedtype, ARG_fieldname) \ struct ARG_structname { \ struct PyGccWrapper head; \ ARG_wrappedtype ARG_fieldname; \ }; \ \ typedef struct ARG_structname ARG_structname; \ \ extern PyObject * \ ARG_structname##_New(ARG_wrappedtype ARG_fieldname); \ \ extern PyObject * \ ARG_structname##_NewUnique(ARG_wrappedtype ARG_fieldname); \ \ extern PyGccWrapperTypeObject ARG_typeobj \ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(#ARG_structname); \ \ extern void \ PyGcc_WrtpMarkFor##ARG_structname(ARG_structname *wrapper); \ /* end of macro */ DECLARE_SIMPLE_WRAPPER(PyGccCallgraphEdge, PyGccCallgraphEdge_TypeObj, cgraph_edge, gcc_cgraph_edge, edge) DECLARE_SIMPLE_WRAPPER(PyGccCallgraphNode, PyGccCallgraphNode_TypeObj, cgraph_node, gcc_cgraph_node, node) DECLARE_SIMPLE_WRAPPER(PyGccPass, PyGccPass_TypeObj, pass, struct opt_pass *, pass) DECLARE_SIMPLE_WRAPPER(PyGccLocation, PyGccLocation_TypeObj, location, gcc_location, loc) /* class rich_location was added to libcpp in gcc 6. */ #if (GCC_VERSION >= 6000) DECLARE_SIMPLE_WRAPPER(PyGccRichLocation, PyGccRichLocation_TypeObj, rich_location, rich_location, richloc) #endif DECLARE_SIMPLE_WRAPPER(PyGccGimple, PyGccGimple_TypeObj, gimple, gcc_gimple, stmt); DECLARE_SIMPLE_WRAPPER(PyGccEdge, PyGccEdge_TypeObj, edge, gcc_cfg_edge, e) DECLARE_SIMPLE_WRAPPER(PyGccBasicBlock, PyGccBasicBlock_TypeObj, basic_block, gcc_cfg_block, bb) DECLARE_SIMPLE_WRAPPER(PyGccCfg, PyGccCfg_TypeObj, cfg, gcc_cfg, cfg) DECLARE_SIMPLE_WRAPPER(PyGccFunction, PyGccFunction_TypeObj, function, gcc_function, fun) DECLARE_SIMPLE_WRAPPER(PyGccOption, PyGccOption_TypeObj, option, gcc_option, opt) DECLARE_SIMPLE_WRAPPER(PyGccParameter, PyGccParameter_TypeObj, param_num, compiler_param, param_num) DECLARE_SIMPLE_WRAPPER(PyGccRtl, PyGccRtl_TypeObj, #if (GCC_VERSION >= 5000) rtl_insn *, #else rtl, #endif gcc_rtl_insn, insn) DECLARE_SIMPLE_WRAPPER(PyGccTree, PyGccTree_TypeObj, tree, gcc_tree, t) DECLARE_SIMPLE_WRAPPER(PyGccVariable, PyGccVariable_TypeObj, variable, gcc_variable, var) /* autogenerated-callgraph.c */ int autogenerated_callgraph_init_types(void); void autogenerated_callgraph_add_types(PyObject *m); /* autogenerated-cfg.c */ int autogenerated_cfg_init_types(void); void autogenerated_cfg_add_types(PyObject *m); /* autogenerated-function.c */ int autogenerated_function_init_types(void); void autogenerated_function_add_types(PyObject *m); /* autogenerated-gimple.c */ int autogenerated_gimple_init_types(void); void autogenerated_gimple_add_types(PyObject *m); PyGccWrapperTypeObject* PyGcc_autogenerated_gimple_type_for_stmt(gcc_gimple stmt); /* autogenerated-location.c */ int autogenerated_location_init_types(void); void autogenerated_location_add_types(PyObject *m); /* autogenerated-option.c */ int autogenerated_option_init_types(void); void autogenerated_option_add_types(PyObject *m); /* autogenerated-parameter.c */ int autogenerated_parameter_init_types(void); void autogenerated_parameter_add_types(PyObject *m); /* autogenerated-pass.c */ int autogenerated_pass_init_types(void); void autogenerated_pass_add_types(PyObject *m); extern PyGccWrapperTypeObject PyGccGimplePass_TypeObj; extern PyGccWrapperTypeObject PyGccRtlPass_TypeObj; extern PyGccWrapperTypeObject PyGccSimpleIpaPass_TypeObj; extern PyGccWrapperTypeObject PyGccIpaPass_TypeObj; /* autogenerated-pretty-printer.c */ int autogenerated_pretty_printer_init_types(void); void autogenerated_pretty_printer_add_types(PyObject *m); /* autogenerated-rtl.c */ int autogenerated_rtl_init_types(void); void autogenerated_rtl_add_types(PyObject *m); PyGccWrapperTypeObject * PyGcc_autogenerated_rtl_type_for_stmt(gcc_rtl_insn insn); /* autogenerated-tree.c */ int autogenerated_tree_init_types(void); void autogenerated_tree_add_types(PyObject *m); PyGccWrapperTypeObject* PyGcc_autogenerated_tree_type_for_tree(gcc_tree t, int borrow_ref); PyGccWrapperTypeObject* PyGcc_autogenerated_tree_type_for_tree_code(enum tree_code code, int borrow_ref); extern PyGccWrapperTypeObject PyGccComponentRef_TypeObj; /* autogenerated-variable.c */ int autogenerated_variable_init_types(void); void autogenerated_variable_add_types(PyObject *m); PyObject * PyGccStringOrNone(const char *str_or_null); #if (GCC_VERSION >= 4008) PyObject * VEC_tree_as_PyList(vec *vec_nodes); #else PyObject * VEC_tree_as_PyList(VEC(tree,gc) *vec_nodes); #endif PyObject * PyGcc_int_from_int_cst(tree int_cst); PyObject * PyGcc_int_from_decimal_string_buffer(const char *buf); void PyGcc_DoubleIntAsText(double_int di, bool is_unsigned, char *out, int bufsize) __attribute__((nonnull)); #if (GCC_VERSION >= 5000) PyObject * PyGcc_int_from_wide_int(); #else PyObject * PyGcc_int_from_double_int(double_int di, bool is_unsigned); #endif PyObject * PyGcc_LazilyCreateWrapper(PyObject **cache, void *ptr, PyObject *(*ctor)(void *ptr)); int PyGcc_insert_new_wrapper_into_cache(PyObject **cache, void *ptr, PyObject *obj); /* gcc-python.c */ int PyGcc_IsWithinEvent(enum plugin_event *out_event); char * PyGcc_strdup(const char *str) __attribute__((nonnull)); void PyGcc_PrintException(const char *msg); /* Shorthand for: repr(getattr(obj, attrname)) */ PyObject * PyGcc_GetReprOfAttribute(PyObject *obj, const char *attrname); /* Python 2 vs Python 3 compat: */ #if PY_MAJOR_VERSION == 3 /* Python 3: use PyUnicode for "str" and PyLong for "int": */ #define PyGccString_FromFormat PyUnicode_FromFormat #define PyGccString_FromString PyUnicode_FromString #define PyGccString_FromString_and_size PyUnicode_FromStringAndSize #define PyGccString_AsString _PyUnicode_AsString #define PyGccInt_FromLong PyLong_FromLong #define PyGccInt_Check PyLong_Check #define PyGccInt_AsLong PyLong_AsLong #else /* Python 2: use PyString for "str" and PyInt for "int": */ #define PyGccString_FromFormat PyString_FromFormat #define PyGccString_FromString PyString_FromString #define PyGccString_FromString_and_size PyString_FromStringAndSize #define PyGccString_AsString PyString_AsString #define PyGccInt_FromLong PyInt_FromLong #define PyGccInt_Check PyInt_Check #define PyGccInt_AsLong PyInt_AsLong #endif /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ #endif /* INCLUDED__GCC_PYTHON_H */ gcc-python-plugin-0.17/gcc-with-cpychecker000077500000000000000000000101311342215241600205550ustar00rootroot00000000000000#!/usr/bin/env python # Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Harness for invoking GCC with the cpychecker Python code within the python # plugin, whilst dealing with some options # (This code runs under the regular Python interpreter, not within gcc) import argparse import os import subprocess import sys abspath = os.path.abspath(os.path.dirname(sys.argv[0])) # By default, look for the plugin relative to this harness. This is intended # to make it easier during development, so that we can make other projects with # CC=../../../gcc-python-plugin/gcc-with-cpychecker # and similar. # # When installed, this should be fixed up. PLUGIN = os.path.join(abspath, 'python.so') # Similarly, set the location to search for libgcc-c-api.so: LD_LIBRARY_PATH = os.path.join(abspath, 'gcc-c-api') if 'LD_LIBRARY_PATH' in os.environ: LD_LIBRARY_PATH = '%s:%s' % (LD_LIBRARY_PATH, os.environ['LD_LIBRARY_PATH']) # Create arg parser: parser = argparse.ArgumentParser(usage='%(prog)s [options] gcc-options') DEFAULT_MAXTRANS=256 parser.add_argument('--maxtrans', type=int, default=DEFAULT_MAXTRANS, help='Set the maximum number of transitions to consider before pruning the analysis tree (default: %i)' % DEFAULT_MAXTRANS) parser.add_argument('--dump-json', action='store_true', default=False, help=('Dump a JSON representation of any problems. For' ' example, given a function "foo.c", if any warnings' ' or errors are found in function "bar", a file' ' "foo.c.bar.json" will be written out in JSON' ' form')) parser.add_argument('--cpychecker-verbose', action='store_true', default=False, help=('Output extra information')) # Only consume args we understand, leaving the rest for gcc: ns, other_args = parser.parse_known_args() if 0: print(ns) print(other_args) # Enable the refcount-checker when running via this script # # We would use the regular keyword argument syntax: # verify_refcounting=True # but unfortunately gcc's option parser seems to not be able to cope with '=' # within an option's value. So we do it using dictionary syntax instead: dictstr = '"verify_refcounting":True' dictstr += ', "verbose":%i' % (ns.cpychecker_verbose) dictstr += ', "maxtrans":%i' % ns.maxtrans dictstr += ', "dump_json":%i' % ns.dump_json cmd = 'from libcpychecker import main; main(**{%s})' % dictstr # Do not use CC in the environment, to avoid forkbombing when setting # CC=gcc-with-cpychecker. Instead, use CC_FOR_CPYCHECKER. if 'CC_FOR_CPYCHECKER' in os.environ: gcc = os.environ['CC_FOR_CPYCHECKER'] else: gcc = 'gcc' args = [gcc, ('-fplugin=%s' % PLUGIN), ('-fplugin-arg-python-command=%s' % cmd)] args += other_args # (the args we didn't consume) # Beware of quoting: if the command is quoted within the Popen call, then # Python interprets it as a string literal, and does nothing. # # But if invoking from a shell, you need quotes aroung the command # # To add to the fun, "gcc -v" emits it in unquoted form, # which will need quotes added env = os.environ.copy() env['LD_LIBRARY_PATH'] = LD_LIBRARY_PATH if 0: print(' '.join(args)) p = subprocess.Popen(args, env=env) try: r = p.wait() except KeyboardInterrupt: r = 1 sys.exit(r) gcc-python-plugin-0.17/gcc-with-python000077500000000000000000000014621342215241600177650ustar00rootroot00000000000000#!/bin/sh # Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . ${CC:-gcc} -fplugin=$(pwd)/python.so -fplugin-arg-python-script=$@ gcc-python-plugin-0.17/gccutils/000077500000000000000000000000001342215241600166255ustar00rootroot00000000000000gcc-python-plugin-0.17/gccutils/__init__.py000066400000000000000000000557461342215241600207570ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . import gcc def sorted_dict_repr(d): return '{' + ', '.join(['%r: %r' % (k, d[k]) for k in sorted(d.keys())]) + '}' def get_src_for_loc(loc): # Given a gcc.Location, get the source line as a string import linecache return linecache.getline(loc.file, loc.line).rstrip() def get_field_by_name(typeobj, name): check_isinstance(typeobj, (gcc.RecordType, gcc.UnionType, gcc.QualUnionType)) for field in typeobj.fields: if field.name == name: return field def get_global_typedef(name): # Look up a typedef in global scope by name, returning a gcc.TypeDecl, # or None if not found for u in gcc.get_translation_units(): if u.language.startswith('GNU C++'): gns = gcc.get_global_namespace() return gns.lookup(name) if u.block: for v in u.block.vars: if isinstance(v, gcc.TypeDecl): if v.name == name: return v def get_variables_as_dict(): result = {} for var in gcc.get_variables(): result[var.decl.name] = var return result def get_global_vardecl_by_name(name): # Look up a variable in global scope by name, returning a gcc.VarDecl, # or None if not found for u in gcc.get_translation_units(): if u.language == 'GNU C++': gns = gcc.get_global_namespace() return gns.lookup(name) for v in u.block.vars: if isinstance(v, gcc.VarDecl): if v.name == name: return v def get_nonnull_arguments(funtype): """ 'nonnull' is an attribute on the fun.decl.type http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html It can either have no arguments (all pointer args are non-NULL), or be a list of integers. These integers are 1-based. Return a frozenset of 0-based integers, giving the arguments for which we can assume the "nonnull" property. (Note the 0-based vs 1-based differences) Compare with gcc/tree-vrp.c: nonnull_arg_p """ check_isinstance(funtype, (gcc.FunctionType, gcc.MethodType)) if 'nonnull' in funtype.attributes: result = [] nonnull = funtype.attributes['nonnull'] if nonnull == []: # All pointer args are nonnull: for idx, parm in enumerate(funtype.argument_types): if isinstance(parm, gcc.PointerType): result.append(idx) else: # Only the listed args are nonnull: for val in nonnull: check_isinstance(val, gcc.IntegerCst) result.append(val.constant - 1) return frozenset(result) else: # No "nonnull" attribute was given: return frozenset() def invoke_dot(dot, name='test'): from subprocess import Popen, PIPE if 1: fmt = 'png' else: # SVG generation seems to work, but am seeing some text-width issues # with rendering of the SVG by eog and firefox on this machine (though # not chromium). # # Looks like X coordinates allocated by graphviz don't contain quite # enough space for the elements. # # Presumably a font selection/font metrics issue fmt = 'svg' filename = '%s.%s' % (name, fmt) p = Popen(['dot', '-T%s' % fmt, '-o', filename], stdin=PIPE) p.communicate(dot.encode('ascii')) p = Popen(['xdg-open', filename]) p.communicate() def pprint(obj): pp = TextualPrettyPrinter() pp.pprint(obj) def pformat(obj): pp = TextualPrettyPrinter() return pp.pformat(obj) class PrettyPrinter(object): def __init__(self): self.show_addr = False def attr_to_str(self, name, value): if name == 'addr': return hex(value) if isinstance(value, str): return repr(value) return str(value) def iter_tree_attrs(self, obj): # Iterate through the interesting attributes of the object: for name in dir(obj): # Ignore private and "magic" attributes: if name.startswith('_'): continue value = getattr(obj, name) # Ignore methods: if hasattr(value, '__call__'): continue if not self.show_addr: if name == 'addr': continue # Don't follow infinite chains, e.g. # ptr to ptr to ... of a type: if isinstance(obj, gcc.Type): if (name == 'pointer' or name.endswith('equivalent')): continue #print 'attr %r obj.%s: %r' % (name, name, value) yield (name, value) class TextualPrettyPrinter(PrettyPrinter): """Convert objects to nice textual dumps, loosely based on Python's pprint module""" def __init__(self): super(TextualPrettyPrinter, self).__init__() self.maxdepth = 5 def pprint(self, obj): import sys sys.stdout.write(self.pformat(obj)) def make_indent(self, indent): return indent * ' ' def pformat(self, obj): return self._recursive_format_obj(obj, set(), 0) def indent(self, prefix, txt): return '\n'.join([prefix + line for line in txt.splitlines()]) def _recursive_format_obj(self, obj, visited, depth): def str_for_kv(key, value): return ' %s = %s\n' % (key, value) check_isinstance(obj, gcc.Tree) visited.add(obj.addr) result = '<%s\n' % obj.__class__.__name__ r = repr(obj) s = str(obj) result += str_for_kv('repr()', r) if s != r: result += str_for_kv('str()', '%r' % s) # Show MRO, stripping off this type from front and "object" from end: superclasses = obj.__class__.__mro__[1:-1] result += str_for_kv('superclasses', superclasses) for name, value in self.iter_tree_attrs(obj): if depth < self.maxdepth: if isinstance(value, gcc.Tree): if value.addr in visited: result += str_for_kv('.%s' % name, '... (%s)' % self.attr_to_str(name, repr(value))) else: # Recurse formatted_value = self._recursive_format_obj(value, visited, depth + 1) indented_value = self.indent(' ' * (len(name) + 6), formatted_value) result += str_for_kv('.%s' % name, indented_value.lstrip()) continue # Otherwise: just print short version of the attribute: result += str_for_kv('.%s' % name, self.attr_to_str(name, value)) result += '>\n' return result class DotPrettyPrinter(PrettyPrinter): # Base class for various kinds of data visualizations that use graphviz # (aka ".dot" source files) def to_html(self, text): html_escape_table = { "&": "&", '"': """, "'": "'", ">": ">", "<": "<", # 'dot' doesn't seem to like these: '{': '{', '}': '}', ']': ']', } return "".join(html_escape_table.get(c,c) for c in str(text)) def _dot_td(self, text, align="left", colspan=1, escape=1, bgcolor=None, port=None): if escape: text = self.to_html(text) attribs = 'align="%s" colspan="%i"' % (align, colspan) if bgcolor: attribs += ' bgcolor="%s"' % bgcolor if port: attribs += ' port="%s"' % port return ('%s' % (attribs, text)) def _dot_tr(self, td_text): return ('%s\n' % self._dot_td(td_text)) try: from pygments.formatter import Formatter from pygments.token import Token from pygments.styles import get_style_by_name class GraphvizHtmlFormatter(Formatter, DotPrettyPrinter): """ A pygments Formatter to turn source code fragments into graphviz's pseudo-HTML format. """ def __init__(self, style): Formatter.__init__(self) self.style = style def style_for_token(self, token): # Return a (hexcolor, isbold) pair, where hexcolor could be None # Lookup up pygments' color for this token type: col = self.style.styles[token] isbold = False # Extract a pure hex color specifier of the form that graphviz can # deal with if col: if col.startswith('bold '): isbold = True col = col[5:] return (col, isbold) def format_unencoded(self, tokensource, outfile): from pprint import pprint for t, piece in tokensource: # graphviz seems to choke on font elements with no inner text: if piece == '': continue # pygments seems to add this: if piece == '\n': continue # avoid croaking on '\n': if t == Token.Literal.String.Escape: continue color, isbold = self.style_for_token(t) if 0: print ('(color, isbold): (%r, %r)' % (color, isbold)) if isbold: outfile.write('') # Avoid empty color="" values: if color: outfile.write('' % color + self.to_html(piece) + '') else: outfile.write(self.to_html(piece)) if isbold: outfile.write('') from pygments import highlight from pygments.lexers import CLexer from pygments.formatters import HtmlFormatter def code_to_graphviz_html(code): style = get_style_by_name('default') return highlight(code, CLexer(), # FIXME GraphvizHtmlFormatter(style)) using_pygments = True except ImportError: using_pygments = False class CfgPrettyPrinter(DotPrettyPrinter): # Generate graphviz source for this gcc.Cfg instance, as a string def __init__(self, cfg, name=None): self.cfg = cfg if name: self.name = name def block_id(self, b): if b is self.cfg.entry: return 'entry' if b is self.cfg.exit: return 'exit' return 'block%i' % id(b) def block_to_dot_label(self, bb): # FIXME: font setting appears to work on my machine, but I invented # the attribute value; it may be exercising a failure path result = '\n' result += '\n' % bb.index curloc = None if isinstance(bb.phi_nodes, list): for stmtidx, phi in enumerate(bb.phi_nodes): result += '' + self.stmt_to_html(phi, stmtidx) + '\n' if isinstance(bb.gimple, list) and bb.gimple != []: for stmtidx, stmt in enumerate(bb.gimple): if curloc != stmt.loc: curloc = stmt.loc if curloc is not None: code = get_src_for_loc(stmt.loc).rstrip() pseudohtml = self.code_to_html(code) # print('pseudohtml: %r' % pseudohtml) result += ('') result += '' + self.stmt_to_html(stmt, stmtidx) + '\n' else: # (prevent graphviz syntax error for empty blocks): result += self._dot_tr(self.block_id(bb)) result += '
BLOCK %i
' + self.to_html('%4i ' % stmt.loc.line) + pseudohtml + '
' + (' ' * (5 + stmt.loc.column-1)) + '^' + '
\n' return result def code_to_html(self, code): if using_pygments: return code_to_graphviz_html(code) else: return self.to_html(code) def stmt_to_html(self, stmt, stmtidx): text = str(stmt).strip() text = self.code_to_html(text) bgcolor = None # Work towards visualization of CPython refcounting rules. # For now, paint assignments to (PyObject*) vars and to ob_refcnt # fields, to highlight the areas needing tracking: # print 'stmt: %s' % stmt if 0: # hasattr(stmt, 'lhs'): # print 'stmt.lhs: %s' % stmt.lhs # print 'stmt.lhs: %r' % stmt.lhs if stmt.lhs: # print 'stmt.lhs.type: %s' % stmt.lhs.type # Color assignments to (PyObject *) in red: if str(stmt.lhs.type) == 'struct PyObject *': bgcolor = 'red' # Color assignments to PTR->ob_refcnt in blue: if isinstance(stmt.lhs, gcc.ComponentRef): # print(dir(stmt.lhs)) # print 'stmt.lhs.target: %s' % stmt.lhs.target # print 'stmt.lhs.target.type: %s' % stmt.lhs.target.type # (presumably we need to filter these to structs that are # PyObject, or subclasses) # print 'stmt.lhs.field: %s' % stmt.lhs.field if stmt.lhs.field.name == 'ob_refcnt': bgcolor = 'blue' return self._dot_td(text, escape=0, bgcolor=bgcolor, port='stmt%i' % stmtidx) def edge_to_dot(self, e): if e.true_value: attrliststr = '[label = true]' elif e.false_value: attrliststr = '[label = false]' elif e.loop_exit: attrliststr = '[label = loop_exit]' elif e.can_fallthru: attrliststr = '[label = fallthru]' else: attrliststr = '' return (' %s -> %s %s;\n' % (self.block_id(e.src), self.block_id(e.dest), attrliststr)) def extra_items(self): # Hook for expansion return '' def to_dot(self): if hasattr(self, 'name'): name = self.name else: name = 'G' result = 'digraph %s {\n' % name result += ' subgraph cluster_cfg {\n' #result += ' label="CFG";\n' result += ' node [shape=box];\n' for block in self.cfg.basic_blocks: result += (' %s [label=<%s>];\n' % (self.block_id(block), self.block_to_dot_label(block))) for edge in block.succs: result += self.edge_to_dot(edge) # FIXME: this will have duplicates: #for edge in block.preds: # result += edge_to_dot(edge) result += ' }\n' # Potentially add extra material: result += self.extra_items() result += '}\n' return result class TreePrettyPrinter(DotPrettyPrinter): # Generate a graphviz visualization of this gcc.Tree and the graphs of # nodes it references, as a string def __init__(self, root): print('root: %s' % root) check_isinstance(root, gcc.Tree) self.root = root self.show_addr = False self.maxdepth = 6 # for now def tr_for_kv(self, key, value): return (' %s %s\n' % (self._dot_td(key), self._dot_td(value))) def label_for_tree(self, obj): result = '\n' r = repr(obj) s = str(obj) result += self.tr_for_kv('repr()', r) if s != r: result += self.tr_for_kv('str()', '%r' % s) # Show MRO, stripping off this type from front and "object" from end: superclasses = obj.__class__.__mro__[1:-1] result += self.tr_for_kv('superclasses', superclasses) for name, value in self.iter_tree_attrs(obj): result += (' %s %s \n' % (self._dot_td(name), self._dot_td(self.attr_to_str(name, value)))) result += '
\n' return result def tree_id(self, obj): return 'id%s' % id(obj) def tree_to_dot(self, obj): check_isinstance(obj, gcc.Tree) return (' %s [label=<%s>];\n' % (self.tree_id(obj), self.label_for_tree(obj))) def recursive_tree_to_dot(self, obj, visited, depth): print('recursive_tree_to_dot(%r, %r)' % (obj, visited)) check_isinstance(obj, gcc.Tree) result = self.tree_to_dot(obj) visited.add(obj.addr) if depth < self.maxdepth: for name, value in self.iter_tree_attrs(obj): if isinstance(value, gcc.Tree): if value.addr not in visited: # Recurse result += self.recursive_tree_to_dot(value, visited, depth + 1) # Add edge: result += (' %s -> %s [label = %s];\n' % (self.tree_id(obj), self.tree_id(value), name)) return result def to_dot(self): self.root.debug() result = 'digraph G {\n' result += ' node [shape=record];\n' result += self.recursive_tree_to_dot(self.root, set(), 0) result += '}\n' return result def cfg_to_dot(cfg, name = None): pp = CfgPrettyPrinter(cfg, name) return pp.to_dot() def tree_to_dot(tree): pp = TreePrettyPrinter(tree) return pp.to_dot() class Table(object): '''A table of text/numbers that knows how to print itself''' def __init__(self, columnheadings=None, rows=[], sepchar='-'): self.numcolumns = len(columnheadings) self.columnheadings = columnheadings self.rows = [] self._colsep = ' ' self._sepchar = sepchar def add_row(self, row): assert len(row) == self.numcolumns self.rows.append(row) def write(self, out): colwidths = self._calc_col_widths() self._write_separator(out, colwidths) self._write_row(out, colwidths, self.columnheadings) self._write_separator(out, colwidths) for row in self.rows: self._write_row(out, colwidths, row) self._write_separator(out, colwidths) def _calc_col_widths(self): result = [] for colIndex in range(self.numcolumns): result.append(self._calc_col_width(colIndex)) return result def _calc_col_width(self, idx): cells = [str(row[idx]) for row in self.rows] heading = self.columnheadings[idx] return max([len(c) for c in (cells + [heading])]) def _write_row(self, out, colwidths, values): for i, (value, width) in enumerate(zip(values, colwidths)): if i > 0: out.write(self._colsep) formatString = "%%-%ds" % width # to generate e.g. "%-20s" out.write(formatString % value) out.write('\n') def _write_separator(self, out, colwidths): for i, width in enumerate(colwidths): if i > 0: out.write(self._colsep) out.write(self._sepchar * width) out.write('\n') class CallgraphPrettyPrinter(DotPrettyPrinter): def node_id(self, cgn): return 'cgn%i' % id(cgn) def node_to_dot_label(self, cgn): return str(cgn.decl.name) def edge_to_dot(self, e): attrliststr = '' return (' %s -> %s %s;\n' % (self.node_id(e.caller), self.node_id(e.callee), attrliststr)) def to_dot(self): result = 'digraph Callgraph {\n' #result += ' subgraph cluster_callgraph {\n' result += ' node [shape=box];\n' for cgn in gcc.get_callgraph_nodes(): result += (' %s [label=<%s>];\n' % (self.node_id(cgn), self.node_to_dot_label(cgn))) for edge in cgn.callers: result += self.edge_to_dot(edge) #result += ' }\n' result += '}\n' return result def callgraph_to_dot(): pp = CallgraphPrettyPrinter() return pp.to_dot() def check_isinstance(obj, types): """ Like: assert isinstance(obj, types) but with better error messages """ if not isinstance(obj, types): raise TypeError('%s / %r is not an instance of %s' % (obj, obj, types)) def sorted_callgraph(): """ Return the callgraph, in topologically-sorted order """ return topological_sort(gcc.get_callgraph_nodes(), get_srcs=lambda n: [edge.caller for edge in n.callers # Strip out recursive calls: if edge.caller != n], get_dsts=lambda n: [edge.callee for edge in n.callees # Strip out recursive calls: if edge.callee != n]) def topological_sort(nodes, get_srcs, get_dsts): """ Topological sort in O(n), based on depth-first traversal """ result = [] visited = set() debug = False def visit(n): if n not in visited: if debug: print('first visit to %s' % n.decl) visited.add(n) for m in get_srcs(n): visit(m) if debug: print('adding to result: %s' % n.decl) result.append(n) else: if debug: print('already visited %s' % n.decl) for n in nodes: if not get_dsts(n): visit(n) return result gcc-python-plugin-0.17/gccutils/dot.py000066400000000000000000000050431342215241600177670ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . def to_html(text): html_escape_table = { "&": "&", '"': """, "'": "'", ">": ">", "<": "<", # 'dot' doesn't seem to like these: '{': '{', '}': '}', ']': ']', } return "".join(html_escape_table.get(c,c) for c in str(text)) # An easy way to construct graphviz' pseudo-html: class Node: def to_html(self): raise NotImplementedError() class Element(Node): def __init__(self, children=None, **kwargs): if children is None: children = [] else: assert isinstance(children, list) self.children = children self.attrs = kwargs def to_html(self): if self.attrs: attrstr = ''.join(' %s="%s"' % (attr, value) for attr, value in self.attrs.items()) else: attrstr = '' result = '<%s%s>' % (self.name, attrstr) for child in self.children: result += child.to_html() result += '' % self.name return result def add_child(self, child): self.children.append(child) return child class Table(Element): def to_html(self): result = ('\n' % (self.attrs.get('cellborder', 0), self.attrs.get('border', 0))) for row in self.children: result += row.to_html() result += '
' return result class Tr(Element): name = 'tr' class Td(Element): name = 'td' class Text(Node): def __init__(self, text): self.text = text def to_html(self): return to_html(self.text) class Br(Element): def to_html(self): return '
' class Font(Element): name = 'font' gcc-python-plugin-0.17/gccutils/graph/000077500000000000000000000000001342215241600177265ustar00rootroot00000000000000gcc-python-plugin-0.17/gccutils/graph/__init__.py000066400000000000000000000260751342215241600220510ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . from gccutils.dot import to_html ############################################################################ # Generic directed graphs ############################################################################ class Graph(object): __slots__ = ('nodes', 'edges') def __init__(self): self.nodes = set() self.edges = set() def add_node(self, node): self.nodes.add(node) return node def add_edge(self, srcnode, dstnode, *args, **kwargs): assert isinstance(srcnode, Node) assert isinstance(dstnode, Node) e = self._make_edge(srcnode, dstnode, *args, **kwargs) self.edges.add(e) srcnode.succs.add(e) dstnode.preds.add(e) return e def _make_edge(self, srcnode, dstnode): return Edge(srcnode, dstnode) def remove_node(self, node): if node not in self.nodes: return 0 self.nodes.remove(node) victims = 1 for edge in list(node.succs): victims += self.remove_edge(edge) for edge in list(node.preds): victims += self.remove_edge(edge) return victims def remove_edge(self, edge): if edge not in self.edges: return 0 self.edges.remove(edge) edge.srcnode.succs.remove(edge) edge.dstnode.preds.remove(edge) victims = 0 if not edge.dstnode.preds: # We removed last inedge: recurse if edge.dstnode in self.nodes: victims += self.remove_node(edge.dstnode) return victims def to_dot(self, name, ctxt=None): result = 'digraph %s {\n' % name result += ' node [shape=box];\n' result += self._nodes_to_dot(ctxt) result += self._edges_to_dot(ctxt) result += '}\n' return result def _nodes_to_dot(self, ctxt): # A subgraph path is a tuple of Subgraph instances from pprint import pprint # 1st pass: get the subgraph path for every node # This is a dict from subgraph path to set of nodes: subgraph_paths = {} for node in self.nodes: subgraph_path = node.get_subgraph_path(ctxt) assert isinstance(subgraph_path, tuple) if 0: print('node: %s' % node) print('subgraph_path: %s' % (subgraph_path, )) if subgraph_path in subgraph_paths: subgraph_paths[subgraph_path].add(node) else: subgraph_paths[subgraph_path] = set([node]) if 0: print('subgraph_paths:') pprint(subgraph_paths) # 2nd pass: construct a tree of subgraphs: # dict from subgraph path (parent) to set of subgraph paths # (immediate children): child_paths = {} # sort the paths, so that they are in order of increasing # length: for path in sorted(subgraph_paths.keys()): if path: for i in range(len(path) + 1): subpath = path[0:i] if 0: print('subpath: %s' % (subpath, )) if subpath: parent = subpath[0:-1] if parent in child_paths: child_paths[parent].add(subpath) else: child_paths[parent] = set([subpath]) if 0: print('child_paths:') pprint(child_paths) # 3rd pass: recursively render the subgraph paths: def render_subgraph_path(subgraph_path, indent): def _indent(): return ' ' * indent result = '' if subgraph_path: result += ('%ssubgraph cluster_%s {\n' % (_indent(), subgraph_path[-1].id)) indent += 2 result += ('%slabel = "%s";\n' % (_indent(), subgraph_path[-1].label)) for node in subgraph_paths.get(subgraph_path, set()): result += ('%s%s [label=<%s>];\n' % (_indent(), node.to_dot_id(), node.to_dot_label(ctxt))) # Recurse: for child_path in child_paths.get(subgraph_path, set()): result += render_subgraph_path(child_path, indent) if subgraph_path: indent -= 2 result += '%s}\n' % _indent() return result return render_subgraph_path( (), 2) def _edges_to_dot(self, ctxt): result = '' for edge in self.edges: result += (' %s -> %s [label=<%s>%s];\n' % (edge.srcnode.to_dot_id(), edge.dstnode.to_dot_id(), edge.to_dot_label(ctxt), edge.to_dot_attrs(ctxt))) return result def topologically_sorted_nodes(self): from gccutils import topological_sort def get_srcs(node): for pred in node.preds: yield pred.srcnode def get_dsts(node): for succ in node.succs: yield succ.dstnode return topological_sort(self.nodes, get_srcs, get_dsts) def get_shortest_path(self, srcnode, dstnode): ''' Locate the shortest path from the srcnode to the dstnode Return a list of Edge instances, or None if no such path exists ''' # Dijkstra's algorithm # A dict giving for each node the length of the shortest known path # from srcnode to this node: distance = {} # A dict giving for each node the previous node within that shortest # path: inedge = {} INFINITY = 0x80000000 for node in self.nodes: distance[node] = INFINITY inedge[node] = None distance[srcnode] = 0 # We use a heapq to keep the nodes sorted by distance # The items in the heapq are lists of the form: # [distance_to_node, node, is_live) # The first entry in the list ensures that the heapq is sorted # into the order needed for Dijkstra's algorithm # # Since we can't change the priority of items within a heapq, # whenever we need to update the distance we mark the existing # item as dead (setting the is_live boolean to False), and add a # new entry with the correct value; we ignore dead items during # the iteration # # This gets the time taken for a simple 10000 node graph down to # ~3 seconds, compared to minutes/hours. from heapq import heapify, heappop, heappush item_for_node = {} for node in self.nodes: item_for_node[node] = [distance[node], node, True] worklist = list(item_for_node.values()) heapify(worklist) while worklist: def get_next(): while 1: if not worklist: return None disttonode, node, islive = heappop(worklist) if islive: return node node = get_next() if node is None: # disjoint break if node == dstnode: # We've found the target node; build a path of the edges to # follow to get here: path = [] while inedge[node]: path = [inedge[node]] + path node = inedge[node].srcnode return path if distance[node] == INFINITY: # disjoint break for edge in node.succs: alt = distance[node] + 1 if alt < distance[edge.dstnode]: distance[edge.dstnode] = alt # Changing the distance of edge.dstnode requires us to # update the heapq: # Mark the existing item as dead: item_for_node[edge.dstnode][2] = False # Create a new itemwith the new distance: newitem = [alt, edge.dstnode, True] item_for_node[edge.dstnode] = newitem heappush(worklist, newitem) inedge[edge.dstnode] = edge return None class Node(object): __slots__ = ('preds', 'succs') def __init__(self): self.preds = set() self.succs = set() def to_dot_id(self): return '%s' % id(self) def to_dot_label(self, ctxt): if hasattr(ctxt, 'node_to_dot_html'): htmlnode = ctxt.node_to_dot_html(self) else: htmlnode = self.to_dot_html(ctxt) if htmlnode: return htmlnode.to_html() else: return to_html(str(self)) def to_dot_html(self, ctxt): # Optionally, build a tree of gccutils.dot.Node return None def get_subgraph_path(self, ctxt): # Optionally, allow nodes to be partitioned into a tree of subgraphs # Return a tuple of Subgraph instances return () def __lt__(self, other): return id(self) < id(other) class Edge(object): __slots__ = ('srcnode', 'dstnode') def __init__(self, srcnode, dstnode): self.srcnode = srcnode self.dstnode = dstnode def __repr__(self): return '%s(srcnode=%r, dstnode=%r)' % (self.__class__.__name__, self.srcnode, self.dstnode) def __str__(self): return '%s -> %s' % (self.srcnode, self.dstnode) def to_dot_label(self, ctxt): return '' def to_dot_attrs(self, ctxt): return '' class Subgraph(object): __slots__ = ('id', 'label') def __init__(self, id_, label): self.id = '' for ch in id_: if ch.isalnum(): self.id += ch else: self.id += '_' self.label = label def __eq__(self, other): if self.id == other.id: if self.label == other.label: return True def __hash__(self): return hash(self.id) ^ hash(self.label) def __str__(self): return '(%r, %r)' % (self.id, self.label) def __repr__(self): return 'Subgraph(%r, %r)' % (self.id, self.label) def __lt__(self, other): return self.id < other.id gcc-python-plugin-0.17/gccutils/graph/ivpgraph.py000066400000000000000000000235061342215241600221260ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . from gccutils.graph import Graph, Node, Edge, Subgraph from gccutils.graph.supergraph import Supergraph, CallToStart, \ ExitToReturnSite, CallNode ############################################################################ # Enhancement to a Supergraph to approximate the Interprocedural Valid Paths # (IVP), in which each node is extended to contain a callstring suffix # (i.e. the top N callnodes on the stack), thus modelling call/return # behavior. # Analogous to inlining ############################################################################ class Callstring: """ A callstring-suffix """ __slots__ = ('callnodes', ) def __init__(self, callnodes): self.callnodes = callnodes def __str__(self): def callnode_to_str(callnode): #return str(callnode) return '%s:%s' % (callnode.function.decl.name, callnode.stmt.loc.line) return '[%s]' % ' <| '.join(callnode_to_str(callnode) for callnode in self.callnodes) def __repr__(self): return 'Callstring(%r)' % str(self) def __eq__(self, other): return self.callnodes == other.callnodes def __hash__(self): return hash(self.callnodes) def to_dot_id(self): return '_'.join([str(id(callnode)) for callnode in self.callnodes]) class IvpGraph(Graph): __slots__ = ('sg', 'maxlength', 'ivpnodes', '_entrynodes') def __init__(self, sg, maxlength): Graph.__init__(self) self.sg = sg self.maxlength = maxlength # Dict mapping from (callstring, supernode) to IvpNode self.ivpnodes = {} self._entrynodes = set() # 1st pass: walk from the entrypoints, calling functions, # building nodes, and calling edges. # We will fill in the return edges later: # set of (ivpnode, inneredge) pairs deferred for later processsing: _pending_return_edges = set() def _add_node_for_key(key): callstring, supernode = key newnode = IvpNode(callstring, supernode) self.add_node(newnode) self.ivpnodes[key] = newnode return newnode # The "worklist" is a set of IvpNodes that we need to add # edges for. Doing so may lead to more IvpNodes being # created. worklist = set() for supernode in sg.get_entry_nodes(): key = (Callstring(tuple()), supernode) node = _add_node_for_key(key) worklist.add(node) self._entrynodes.add(node) while worklist: ivpnode = worklist.pop() if 0: print('ivpnode: %s' % ivpnode) print('ivpnode: %r' % ivpnode) for inneredge in ivpnode.innernode.succs: if 0: print(' inneredge: %s' % inneredge) print(' inneredge: %r' % inneredge) callstring = ivpnode.callstring def get_callstring(): if isinstance(inneredge, CallToStart): # interprocedural call: push onto stack: callnode = inneredge.srcnode assert len(callstring.callnodes) <= maxlength if len(callstring.callnodes) == maxlength: # Truncate, losing the bottom of the stack: oldstack = list(callstring.callnodes[1:]) else: oldstack = list(callstring.callnodes) return Callstring(tuple(oldstack + [callnode])) elif isinstance(inneredge, ExitToReturnSite): # interprocedural return: pop from stack if not callstring.callnodes: return None # Ensure that we're returning to the correct place # according to the top of the stack: callnode = callstring.callnodes[-1] if inneredge.dstnode == callnode.returnnode: # add to the pending list _pending_return_edges.add( (ivpnode, inneredge) ) return None else: # same stack depth: return ivpnode.callstring newcallstring = get_callstring() if newcallstring: key = (newcallstring, inneredge.dstnode) if key not in self.ivpnodes: dstnode = _add_node_for_key(key) worklist.add( dstnode ) else: dstnode = self.ivpnodes[key] self.add_edge(ivpnode, dstnode, inneredge) # FIXME: in case we don't terminate, this is useful for debugging why: #if len(self.nodes) > 100: # return # 2nd pass: now gather all valid callstrings: self.all_callstrings = set() for node in self.nodes: self.all_callstrings.add(node.callstring) if 0: print('self.all_callstrings: %s' % self.all_callstrings) # 3rd pass: go back and add the return edges (using the set of valid # callstrings to expand possible-truncated stacks): for srcivpnode, inneredge in _pending_return_edges: callstring = srcivpnode.callstring # We have a return edge, valid in the sense # that the dstnode is the call at the top of the stack # # What state should the stack end up in? def iter_valid_pops(callstring): # We could be at the top of an untruncated stack, in which # case we simply lose the top element: candidate = Callstring(callstring.callnodes[:-1]) if candidate in self.all_callstrings: yield candidate # Alternatively, the stack could be truncated, in which # case we need to generate all possible new elements for the # prefix part of the truncated stack if len(callstring.callnodes) == maxlength: suffix = callstring.callnodes[0:-1] for candidate in self.all_callstrings: if candidate.callnodes[1:] == suffix: yield candidate valid_pops = set(iter_valid_pops(callstring)) for newcallstring in valid_pops: key = (newcallstring, inneredge.dstnode) dstivpnode = self.ivpnodes[key] self.add_edge(srcivpnode, dstivpnode, inneredge) # (done) def _make_edge(self, srcnode, dstnode, edge): return IvpEdge(srcnode, dstnode, edge) def get_functions(self): for fun in self.sg.get_functions(): yield fun def get_entry_nodes(self): for node in self._entrynodes: yield node class IvpNode(Node): __slots__ = ('callstring', 'innernode', ) def __init__(self, callstring, innernode): Node.__init__(self) self.callstring = callstring self.innernode = innernode def to_dot_html(self, ctxt): from gccutils.dot import Table, Tr, Td, Text, Br, Font table = Table() tr = table.add_child(Tr()) td = tr.add_child(Td(align='left')) td.add_child(Text('%s' % (self.callstring))) #.to_dot_id()))) tr = table.add_child(Tr()) td = tr.add_child(Td(align='left')) td.add_child(self.innernode.to_dot_html(ctxt)) return table def __str__(self): return '%s: %s' % (self.callstring, self.innernode) def __repr__(self): return 'IvpNode(%r, %r)' % (self.callstring, self.innernode) @property def supergraphnode(self): return self.innernode @property def stmt(self): return self.innernode.stmt @property def function(self): return self.innernode.function def get_gcc_loc(self): return self.innernode.get_gcc_loc() def get_subgraph_path(self, ctxt): innerpath = self.innernode.get_subgraph_path(ctxt) if innerpath: sg_file, sg_func = innerpath return (sg_file, Subgraph('callstring_%s_function_%s' % (self.callstring, self.function), '%s : %s()' % (self.callstring, self.function.decl.name))) return () class IvpEdge(Edge): __slots__ = ('inneredge', ) def __init__(self, srcnode, dstnode, inneredge): Edge.__init__(self, srcnode, dstnode) self.inneredge = inneredge def to_dot_label(self, ctxt): return self.inneredge.to_dot_label(ctxt) def to_dot_attrs(self, ctxt): return self.inneredge.to_dot_attrs(ctxt) @property def true_value(self): return self.inneredge.true_value @property def false_value(self): return self.inneredge.false_value @property def stmtedge(self): return self.inneredge.stmtedge gcc-python-plugin-0.17/gccutils/graph/query.py000066400000000000000000000127721342215241600214560ustar00rootroot00000000000000# Copyright 2013 David Malcolm # Copyright 2013 Red Hat, Inc. # # This 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 # . # Queries on graphs, with composable filters # Query(graph).filters import gcc from gccutils.graph.supergraph import ReturnNode __all__ = ['Query'] class BaseQuery: def first(self): results = list(self) if len(results) < 1: raise ValueError('no nodes found satisfying: %s' % self) return results[0] def one(self): results = list(self) if len(results) > 1: raise ValueError('more than one node found satisfying: %s' % self) if len(results) < 1: raise ValueError('no nodes found satisfying: %s' % self) return results[0] ####################################################################### # Filters ####################################################################### def get_calls_of(self, funcname): class GetCallsOf(CompoundQuery): def __init__(self, innerquery, funcname): CompoundQuery.__init__(self, innerquery) self.funcname = funcname def __iter__(self): for node in self.innerquery: # For an interprocedural call, we want the CallNode, not the # ReturnNode. # For a call to an external function, the GimpleCall will be # within a regular SupergraphNode: if not isinstance(node, ReturnNode): stmt = node.stmt if isinstance(stmt, gcc.GimpleCall): if isinstance(stmt.fn, gcc.AddrExpr): if isinstance(stmt.fn.operand, gcc.FunctionDecl): if stmt.fn.operand.name == funcname: yield node def __repr__(self): return ('GetCallsOf(%r, funcname=%r)' % (self.innerquery, self.funcname)) def __str__(self): return '%s that are calls of %s()' % (self.innerquery, self.funcname) return GetCallsOf(self, funcname) def assigning_to(self, varname): class AssigningTo(CompoundQuery): def __init__(self, innerquery, varname): CompoundQuery.__init__(self, innerquery) self.varname = varname def __iter__(self): for node in self.innerquery: stmt = node.stmt if stmt.lhs.var.name == self.varname: yield node def __repr__(self): return ('AssigningTo(%r, varname=%r)' % (self.innerquery, self.varname)) def __str__(self): return '%s in which the LHS is assigned to a variable named %s' % (self.innerquery, self.varname) return AssigningTo(self, varname) def assigning_constant(self, constant): class AssigningConstant(CompoundQuery): def __init__(self, innerquery, constant): CompoundQuery.__init__(self, innerquery) self.constant = constant def __iter__(self): for node in self.innerquery: stmt = node.stmt if isinstance(stmt, gcc.GimpleAssign): if stmt.exprcode == gcc.IntegerCst: if stmt.rhs[0] == self.constant: yield node def __repr__(self): return ('AssigningConstant(%r, constant=%r)' % (self.innerquery, self.constant)) def __str__(self): return '%s in which an assignment of the value %s is made' % (self.innerquery, self.constant) return AssigningConstant(self, constant) def within(self, funcname): class Within(CompoundQuery): def __init__(self, innerquery, funcname): CompoundQuery.__init__(self, innerquery) self.funcname = funcname def __iter__(self): for node in self.innerquery: if node.function: if node.function.decl.name == self.funcname: yield node def __repr__(self): return ('Within(%r, funcname=%r)' % (self.innerquery, self.funcname)) def __str__(self): return '%s within %s' % (self.innerquery, self.funcname) return Within(self, funcname) class CompoundQuery(BaseQuery): def __init__(self, innerquery): self.innerquery = innerquery class Query(BaseQuery): def __init__(self, graph): self.graph = graph def __iter__(self): for node in self.graph.nodes: yield node def __repr__(self): return 'Query()' def __str__(self): return 'nodes' gcc-python-plugin-0.17/gccutils/graph/stmtgraph.py000066400000000000000000000276721342215241600223270ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . import gcc from gccutils.graph import Graph, Node, Edge ############################################################################ # A CFG, but with individual statements for nodes, rather than lumping them # together within basic blocks # It also has "empty" nodes i.e. those with no statements, to handle # the empty BBs in the original CFG (entry and exit) ############################################################################ class StmtGraph(Graph): __slots__ = ('fun', 'entry', 'exit', 'entry_of_bb', 'exit_of_bb', 'node_for_stmt', '__lastnode', 'supernode_for_stmtnode') def __init__(self, fun, split_phi_nodes, omit_complex_edges=False): """ fun : the underlying gcc.Function split_phi_nodes: if true, split phi nodes so that there is one copy of each phi node per edge as a SplitPhiNode instance, allowing client code to walk the StmtGraph without having to track which edge we came from if false, create a StmtNode per phi node at the top of the BB """ Graph.__init__(self) self.fun = fun self.entry = None self.exit = None # Mappings from gcc.BasicBlock to StmtNode so that we can wire up # the edges for the gcc.Edge: self.entry_of_bb = {} self.exit_of_bb = {} self.node_for_stmt = {} basic_blocks = fun.cfg.basic_blocks # 1st pass: create nodes and edges within BBs: for bb in basic_blocks: self.__lastnode = None def add_stmt(stmt): nextnode = self.add_node(StmtNode(fun, bb, stmt)) self.node_for_stmt[stmt] = nextnode if self.__lastnode: self.add_edge(self.__lastnode, nextnode, None) else: self.entry_of_bb[bb] = nextnode self.__lastnode = nextnode if bb.phi_nodes and not split_phi_nodes: # If we're not splitting the phi nodes, add them to the top # of each BB: for stmt in bb.phi_nodes: add_stmt(stmt) self.exit_of_bb[bb] = self.__lastnode if bb.gimple: for stmt in bb.gimple: add_stmt(stmt) self.exit_of_bb[bb] = self.__lastnode if self.__lastnode is None: # We have a BB with neither statements nor phis # Create a single node for this BB: if bb == fun.cfg.entry: cls = EntryNode elif bb == fun.cfg.exit: cls = ExitNode else: # gcc appears to create empty BBs for functions # returning void that contain multiple "return;" # statements: cls = StmtNode node = self.add_node(cls(fun, bb, None)) self.entry_of_bb[bb] = node self.exit_of_bb[bb] = node if bb == fun.cfg.entry: self.entry = node elif bb == fun.cfg.exit: self.exit = node assert self.entry_of_bb[bb] is not None assert self.exit_of_bb[bb] is not None # 2nd pass: wire up the cross-BB edges: for bb in basic_blocks: for edge in bb.succs: # If requested, omit "complex" edges e.g. due to # exception-handling: if omit_complex_edges: if edge.complex: continue last_node = self.exit_of_bb[bb] if split_phi_nodes: # add SplitPhiNode instances at the end of each edge # as a copy of each phi node, specialized for this edge if edge.dest.phi_nodes: for stmt in edge.dest.phi_nodes: split_phi = self.add_node(SplitPhiNode(fun, stmt, edge)) self.add_edge(last_node, split_phi, edge) last_node = split_phi # After optimization, the CFG sometimes contains edges that # point to blocks that are no longer within fun.cfg.basic_blocks # Skip them: if edge.dest not in basic_blocks: continue self.add_edge(last_node, self.entry_of_bb[edge.dest], edge) # 3rd pass: set up caselabelexprs for edges within switch statements # There doesn't seem to be any direct association between edges in a # CFG and the switch labels; store this information so that it's # trivial to go from an edge to the set of case labels that might be # being followed: for stmt in self.node_for_stmt: if isinstance(stmt, gcc.GimpleSwitch): labels = stmt.labels node = self.node_for_stmt[stmt] for edge in node.succs: caselabelexprs = set() for label in labels: dststmtnode_of_labeldecl = self.get_node_for_labeldecl(label.target) if dststmtnode_of_labeldecl == edge.dstnode: caselabelexprs.add(label) edge.caselabelexprs = frozenset(caselabelexprs) def _make_edge(self, srcnode, dstnode, edge): return StmtEdge(srcnode, dstnode, edge, len(self.edges)) def get_entry_nodes(self): return [self.entry] def get_node_for_labeldecl(self, labeldecl): assert isinstance(labeldecl, gcc.LabelDecl) bb = self.fun.cfg.get_block_for_label(labeldecl) return self.entry_of_bb[bb] class StmtNode(Node): __slots__ = ('fun', 'bb', 'stmt') def __init__(self, fun, bb, stmt): Node.__init__(self) self.fun = fun self.bb = bb self.stmt = stmt # can be None for empty BBs def __str__(self): return str(self.stmt) def __repr__(self): return 'StmtNode(%r)' % self.stmt def __hash__(self): return hash(self.stmt) def get_stmt(self): return self.stmt def get_gcc_loc(self): if self.stmt: return self.stmt.loc else: return None def to_dot_html(self, ctxt): from gccutils.dot import Table, Tr, Td, Text, Br, Font from gccutils import get_src_for_loc loc = self.get_gcc_loc() if loc: table = Table() code = get_src_for_loc(loc).rstrip() tr = table.add_child(Tr()) td = tr.add_child(Td(align='left')) td.add_child(Text('%4i %s' % (self.stmt.loc.line, code))) td.add_child(Br()) td.add_child(Text(' ' * (5 + self.stmt.loc.column-1) + '^')) td.add_child(Br()) td.add_child(Text(str(self))) return table # return Font([table], face="monospace") else: return Text(str(self)) def __eq__(self, other): return self.stmt == other.stmt class EntryNode(StmtNode): __slots__ = () def to_dot_html(self, ctxt): from gccutils.dot import Table, Tr, Td, Text funtype = self.fun.decl.type args = ','.join(['%s %s' % (arg.type, arg.name) for arg in self.fun.decl.arguments]) signature = '%s %s(%s)' % (funtype.type, self.fun.decl.name, args) table = Table([ Tr([ Td([ Text('ENTRY %s' % signature) ]) ]) ]) for var in self.fun.local_decls: table.add_child(Tr([ Td([ Text('%s %s;' % (var.type, var)) ]) ])) return table def __str__(self): return 'ENTRY %s' % self.fun.decl.name def __repr__(self): return 'EntryNode(%r)' % self.fun.decl.name class ExitNode(StmtNode): __slots__ = () def __str__(self): return 'EXIT %s' % self.fun.decl.name def __repr__(self): return 'ExitNode(%r)' % self.fun.decl.name @property def returnnode(self): """ Get the gcc.GimpleReturn statement associated with this function exit """ if len(self.preds) == 1: node = list(self.preds)[0].srcnode assert isinstance(node.stmt, gcc.GimpleReturn) return node @property def returnval(self): """ Get the gcc.Tree for the return value, or None """ returnnode = self.returnnode if returnnode: assert isinstance(returnnode.stmt, gcc.GimpleReturn) return returnnode.stmt.retval class SplitPhiNode(StmtNode): __slots__ = ('inneredge', 'rhs') def __init__(self, fun, stmt, inneredge): StmtNode.__init__(self, fun, None, stmt) self.inneredge = inneredge # Lookup the RHS for this edge: assert isinstance(stmt, gcc.GimplePhi) assert isinstance(inneredge, gcc.Edge) self.rhs = None for arg, edge in stmt.args: if edge == inneredge: self.rhs = arg break if self.rhs is None: raise UnknownEdge() def __str__(self): return '%s = %s (via %s)' % (self.stmt.lhs, self.rhs, self.stmt) def __repr__(self): return 'SplitPhiNode(%r, %r)' % (self.stmt, self.inneredge) def __eq__(self, other): return isinstance(other, SplitPhiNode) and self.inneredge == other.inneredge class StmtEdge(Edge): __slots__ = ('cfgedge', 'sortidx', 'caselabelexprs') def __init__(self, srcnode, dstnode, cfgedge, sortidx): Edge.__init__(self, srcnode, dstnode) self.cfgedge = cfgedge # will be None within a BB self.sortidx = sortidx # For use in handling switch statements: # the set of gcc.CaseLabelExpr for this edge self.caselabelexprs = frozenset() def to_dot_label(self, ctx): return str(self.sortidx) if self.cfgedge: if self.cfgedge.true_value: return 'true' elif self.cfgedge.false_value: return 'false' # Edges within a switch statement: if self.caselabelexprs: def cle_to_str(cle): if cle.low is not None: if cle.high is not None: return '%s ... %s' % (cle.low, cle.high) else: return str(cle.low) else: return 'default' return '{%s}' % ', '.join([cle_to_str(cle) for cle in self.caselabelexprs]) return '' @property def true_value(self): if self.cfgedge: return self.cfgedge.true_value @property def false_value(self): if self.cfgedge: return self.cfgedge.false_value def __cmp__(self, other): return cmp(self.sortidx, other.sortidx) gcc-python-plugin-0.17/gccutils/graph/supergraph.py000066400000000000000000000271201342215241600224620ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . from gccutils.graph import Graph, Node, Edge, Subgraph from gccutils.graph.stmtgraph import StmtGraph ############################################################################ # Supergraph of all CFGs, built from each functions' StmtGraph. # A graph in which the nodes wrap StmtNode ############################################################################ class Supergraph(Graph): __slots__ = ('supernode_for_stmtnode', 'stmtg_for_fun', 'fake_entry_node') def __init__(self, split_phi_nodes, add_fake_entry_node): Graph.__init__(self) self.supernode_for_stmtnode = {} # 1st pass: locate interprocedural instances of gcc.GimpleCall # i.e. where both caller and callee are within the supergraph # (perhaps the same function) ipcalls = set() from gcc import get_callgraph_nodes for node in get_callgraph_nodes(): fun = node.decl.function if fun: for edge in node.callees: if edge.callee.decl.function: ipcalls.add(edge.call_stmt) # 2nd pass: construct a StmtGraph for each function in the callgraph # and add nodes and edges to "self" wrapping the nodes and edges # within each StmtGraph: self.stmtg_for_fun = {} for node in get_callgraph_nodes(): fun = node.decl.function if fun: stmtg = StmtGraph(fun, split_phi_nodes) self.stmtg_for_fun[fun] = stmtg # Clone the stmtg nodes and edges into the Supergraph: stmtg.supernode_for_stmtnode = {} for node in stmtg.nodes: if node.stmt in ipcalls: # These nodes will have two supernodes, a CallNode # and a ReturnNode: callnode = self.add_node(CallNode(node, stmtg)) returnnode = self.add_node(ReturnNode(node, stmtg)) callnode.returnnode = returnnode returnnode.callnode = callnode stmtg.supernode_for_stmtnode[node] = (callnode, returnnode) self.add_edge( callnode, returnnode, CallToReturnSiteEdge, None) else: stmtg.supernode_for_stmtnode[node] = \ self.add_node(SupergraphNode(node, stmtg)) for edge in stmtg.edges: if edge.srcnode.stmt in ipcalls: # Begin the superedge from the ReturnNode: srcsupernode = stmtg.supernode_for_stmtnode[edge.srcnode][1] else: srcsupernode = stmtg.supernode_for_stmtnode[edge.srcnode] if edge.dstnode.stmt in ipcalls: # End the superedge at the CallNode: dstsupernode = stmtg.supernode_for_stmtnode[edge.dstnode][0] else: dstsupernode = stmtg.supernode_for_stmtnode[edge.dstnode] superedge = self.add_edge(srcsupernode, dstsupernode, SupergraphEdge, edge) # 3rd pass: add the interprocedural edges (call and return): for node in get_callgraph_nodes(): fun = node.decl.function if fun: for edge in node.callees: if edge.callee.decl.function: calling_stmtg = self.stmtg_for_fun[fun] called_stmtg = self.stmtg_for_fun[edge.callee.decl.function] calling_stmtnode = calling_stmtg.node_for_stmt[edge.call_stmt] assert calling_stmtnode entry_stmtnode = called_stmtg.entry assert entry_stmtnode exit_stmtnode = called_stmtg.exit assert exit_stmtnode superedge_call = self.add_edge( calling_stmtg.supernode_for_stmtnode[calling_stmtnode][0], called_stmtg.supernode_for_stmtnode[entry_stmtnode], CallToStart, None) superedge_return = self.add_edge( called_stmtg.supernode_for_stmtnode[exit_stmtnode], calling_stmtg.supernode_for_stmtnode[calling_stmtnode][1], ExitToReturnSite, None) superedge_return.calling_stmtnode = calling_stmtnode # 4th pass: create fake entry node: if not add_fake_entry_node: self.fake_entry_node = None return self.fake_entry_node = self.add_node(FakeEntryNode(None, None)) """ /* At file scope, the presence of a `static' or `register' storage class specifier, or the absence of all storage class specifiers makes this declaration a definition (perhaps tentative). Also, the absence of `static' makes it public. */ if (current_scope == file_scope) { TREE_PUBLIC (decl) = storage_class != csc_static; TREE_STATIC (decl) = !extern_ref; } """ # For now, assume all non-static functions are possible entrypoints: for fun in self.stmtg_for_fun: # Only for non-static functions: if fun.decl.is_public: stmtg = self.stmtg_for_fun[fun] self.add_edge(self.fake_entry_node, stmtg.supernode_for_stmtnode[stmtg.entry], FakeEntryEdge, None) def add_node(self, supernode): Graph.add_node(self, supernode) # Keep track of mapping from stmtnode -> supernode self.supernode_for_stmtnode[supernode.innernode] = supernode return supernode def _make_edge(self, srcnode, dstnode, cls, edge): return cls(srcnode, dstnode, edge) def get_entry_nodes(self): if self.fake_entry_node: yield self.fake_entry_node def get_functions(self): for fun in self.stmtg_for_fun: yield fun class SupergraphNode(Node): """ A node in the supergraph, wrapping a StmtNode """ __slots__ = ('innernode', 'stmtg') def __init__(self, innernode, stmtg): Node.__init__(self) self.innernode = innernode self.stmtg = stmtg def to_dot_html(self, ctxt): return self.innernode.to_dot_html(ctxt) def __str__(self): return str(self.innernode) def __repr__(self): return 'SupergraphNode(%r)' % self.innernode @property def supergraphnode(self): return self @property def stmtnode(self): return self.innernode @property def stmt(self): if self.innernode: return self.innernode.get_stmt() def get_stmt(self): if self.innernode: return self.innernode.get_stmt() def get_gcc_loc(self): if self.innernode: return self.innernode.get_gcc_loc() def get_subgraph_path(self, ctxt): if self.stmtg: func = self.stmtg.fun filename = func.start.file funcname = func.decl.name return (Subgraph(filename, filename), Subgraph(funcname, funcname), ) return () @property def function(self): """ Get the gcc.Function for this node """ if self.stmtg: return self.stmtg.fun class CallNode(SupergraphNode): """ A first node for a gcc.GimpleCall, representing the invocation of the function. It has the same stmt (the gcc.GimpleCall) as the ReturnNode """ __slots__ = ('returnnode', # the corresponding ReturnNode ) class ReturnNode(SupergraphNode): """ A second node for a gcc.GimpleCall, representing the assignment of the return value from the completed call into the LHS. It has the same stmt (the gcc.GimpleCall) as the CallNode """ __slots__ = ('callnode', # the corresponding CallNode ) class FakeEntryNode(SupergraphNode): """ Fake entry node which links to all externally-visible entry nodes, so that a supergraph can have a unique entrypoint. It represents "the outside world" when analyzing the supergraph of a shared library. """ __slots__ = () def __str__(self): return 'ALL ENTRYPOINTS' def __repr__(self): return 'FakeEntryNode' def to_dot_html(self, ctxt): from gccutils.dot import Text return Text('ALL ENTRYPOINTS') class SupergraphEdge(Edge): """ An edge in the supergraph, wrapping a StmtEdge, or None for the intraprocedual edges for function call/return """ __slots__ = ('inneredge', ) def __init__(self, srcnode, dstnode, inneredge): Edge.__init__(self, srcnode, dstnode) self.inneredge = inneredge def to_dot_label(self, ctxt): if self.inneredge: return self.inneredge.to_dot_label(ctxt) @property def true_value(self): if self.inneredge: return self.inneredge.true_value @property def false_value(self): if self.inneredge: return self.inneredge.false_value @property def stmtedge(self): return self.inneredge class CallToReturnSiteEdge(SupergraphEdge): """ The intraprocedural edge for a function call, from the gcc.GimpleCall to the next statement """ __slots__ = () def to_dot_label(self, ctxt): return 'within function' def to_dot_attrs(self, ctxt): return ' penwidth=2' class CallToStart(SupergraphEdge): """ The interprocedural edge for the start of a function call: from the gcc.GimpleCall to the entry node of the callee """ __slots__ = () def to_dot_label(self, ctxt): return 'call of %s' % self.dstnode.function.decl.name def to_dot_attrs(self, ctxt): #return ' constraint=false, style=dotted' return ' style=dotted' class ExitToReturnSite(SupergraphEdge): """ The interprocedural edge for the end of a function call: from the exit node of the callee to the successor node of the gcc.GimpleCall within the caller """ __slots__ = ('calling_stmtnode', ) def to_dot_label(self, ctxt): return 'return to %s' % self.dstnode.function.decl.name def to_dot_attrs(self, ctxt): #return ' constraint=false, style=dotted' return ' style=dotted' class FakeEntryEdge(SupergraphEdge): """ Fake edge from the FakeEntryNode to one of the entrypoints. This represents a call "from outside" the scope of the supergraph (e.g. for analyzing a library) """ __slots__ = () def to_dot_label(self, ctxt): return 'external call' gcc-python-plugin-0.17/gccutils/selftests.py000066400000000000000000000022761342215241600212220ustar00rootroot00000000000000# Copyright 2013 David Malcolm # Copyright 2013 Red Hat, Inc. # # This 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 # . # Helper functions for writing selftests # # Sometimes it's hard to use the stdlib's unittest module, given that in # the plugin we're typically effectively being run as a callback from gcc # (either literally, or as a gcc.Pass) def assertEqual(lhs, rhs): if lhs != rhs: raise ValueError('non-equal values: %r != %r' % (lhs, rhs)) def assertEndsWith(s, suffix): if not s.endswith(suffix): raise ValueError('%r does not end with %r' % (s, suffix)) gcc-python-plugin-0.17/generate-callgraph-c.py000066400000000000000000000125241342215241600213330ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("gcc-c-api/gcc-callgraph.h") cu.add_include("gcc-c-api/gcc-gimple.h") cu.add_include("gcc-c-api/gcc-tree.h") cu.add_include("gcc-c-api/gcc-declaration.h") modinit_preinit = '' modinit_postinit = '' def generate_callgraph_edge(): # # Generate the gcc.CallgraphEdge class: # global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccCallgraphEdge_getset_table', [], identifier_prefix='PyGccCallgraphEdge', typename='PyGccCallgraphEdge') getsettable.add_simple_getter(cu, 'caller', 'PyGccCallgraphNode_New(gcc_cgraph_edge_get_caller(self->edge))', 'The function that makes this call, as a gcc.CallgraphNode') getsettable.add_simple_getter(cu, 'callee', 'PyGccCallgraphNode_New(gcc_cgraph_edge_get_callee(self->edge))', 'The function that is called here, as a gcc.CallgraphNode') getsettable.add_simple_getter(cu, 'call_stmt', 'PyGccGimple_New(gcc_gimple_call_as_gcc_gimple(gcc_cgraph_edge_get_call_stmt(self->edge)))', 'The gcc.GimpleCall statememt for the function call') cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccCallgraphEdge_TypeObj', localname = 'CallgraphEdge', tp_name = 'gcc.CallgraphEdge', struct_name = 'PyGccCallgraphEdge', tp_new = 'PyType_GenericNew', tp_getset = getsettable.identifier, tp_repr = '(reprfunc)PyGccCallgraphEdge_repr', tp_str = '(reprfunc)PyGccCallgraphEdge_str', tp_dealloc = 'PyGccWrapper_Dealloc', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_callgraph_edge() def generate_callgraph_node(): # # Generate the gcc.CallgraphNode class: # global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccCallgraphNode_getset_table', [], identifier_prefix='PyGccCallgraphNode', typename='PyGccCallgraphNode') # FIXME: add getters getsettable.add_simple_getter(cu, 'decl', 'PyGccTree_New(gcc_function_decl_as_gcc_tree(gcc_cgraph_node_get_decl(self->node)))', 'The gcc.FunctionDecl for this node') getsettable.add_gsdef('callees', 'PyGccCallgraphNode_get_callees', None, 'The function calls made by this function, as a list of gcc.CallgraphEdge') getsettable.add_gsdef('callers', 'PyGccCallgraphNode_get_callers', None, 'The places that call this function, as a list of gcc.CallgraphEdge') cu.add_defn(getsettable.c_defn()) # see gcc/cgraph.c: dump_cgraph_node (FILE *f, struct cgraph_node *node) pytype = PyGccWrapperTypeObject(identifier = 'PyGccCallgraphNode_TypeObj', localname = 'CallgraphNode', tp_name = 'gcc.CallgraphNode', struct_name = 'PyGccCallgraphNode', tp_new = 'PyType_GenericNew', tp_getset = getsettable.identifier, tp_repr = '(reprfunc)PyGccCallgraphNode_repr', tp_str = '(reprfunc)PyGccCallgraphNode_str', tp_dealloc = 'PyGccWrapper_Dealloc', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_callgraph_node() cu.add_defn(""" int autogenerated_callgraph_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_callgraph_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-casts-c.py000066400000000000000000000077221342215241600205170ustar00rootroot00000000000000# Copyright 2013 David Malcolm # Copyright 2013 Red Hat, Inc. # # This 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 # . import glob import sys sys.path.append('gcc-c-api') from xmltypes import ApiRegistry, Api COPYRIGHT_HEADER = ''' Copyright 2013 David Malcolm Copyright 2013 Red Hat, Inc. This 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 . ''' def write_header(out): out.write('/* This file is autogenerated: do not edit */\n') out.write('/*%s*/\n' % COPYRIGHT_HEADER) out.write('\n') def write_footer(out): out.write(''' /* PEP-7 Local variables: c-basic-offset: 4 indent-tabs-mode: nil End: */ ''') def write_c(registry, c_out): write_header(c_out) c_out.write('#include \n') c_out.write('#include "gcc-python.h"\n') c_out.write('#include "gcc-python-wrappers.h"\n') c_out.write('#include "gcc-python-compat.h"\n') c_out.write('#include "cp/cp-tree.h"\n') c_out.write('#include "gimple.h"\n') c_out.write('#include "cp/cp-tree.h" /* for TFF_* for use by PyGccFunctionDecl_get_fullname */\n') c_out.write('/* op_symbol_code moved to tree-pretty-print.h in gcc 4.9\n') c_out.write(' but tree-pretty-print.h is only available from 4.7 onwards. */\n') c_out.write('#if (GCC_VERSION >= 4009)\n') c_out.write('#include "tree-pretty-print.h"\n') c_out.write('#endif\n') c_out.write('#include "gcc-c-api/gcc-tree.h"\n') c_out.write('#include "gcc-c-api/gcc-type.h"\n') c_out.write('#include "autogenerated-casts.h"\n\n\n') tree_type = registry.lookup_type('tree') for subclass in tree_type.get_subclasses(recursive=True): c_out.write('%s\n' 'PyGccTree_as_%s(struct PyGccTree * self)\n' '{\n' ' return gcc_tree_as_%s(self->t);\n' '}\n\n' % (subclass.get_c_name(), subclass.get_c_name(), subclass.get_c_name())) write_footer(c_out) def write_h(registry, h_out): write_header(h_out) h_out.write('#include \n') tree_type = registry.lookup_type('tree') for subclass in tree_type.get_subclasses(recursive=True): h_out.write('extern %s\n' 'PyGccTree_as_%s(struct PyGccTree * self);\n\n' % (subclass.get_c_name(), subclass.get_c_name())) write_footer(h_out) def main(c_filename, h_filename, xmldir): registry = ApiRegistry() for xmlfile in sorted(glob.glob(xmldir + '*.xml')): api = Api(registry, xmlfile) with open(c_filename, 'w') as c_out: write_c(registry, c_out) with open(h_filename, 'w') as h_out: write_h(registry, h_out) main(c_filename=sys.argv[1], h_filename=sys.argv[2], xmldir=sys.argv[3]) gcc-python-plugin-0.17/generate-cfg-c.py000066400000000000000000000227031342215241600201350ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include('gcc-c-api/gcc-cfg.h') #cu.add_include("basic-block.h") modinit_preinit = '' modinit_postinit = '' def generate_edge(): # # Generate the gcc.Edge class: # global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccEdge_getset_table', [PyGetSetDef('src', cu.add_simple_getter('PyGccEdge_get_src', 'PyGccEdge', 'PyGccBasicBlock_New(gcc_cfg_edge_get_src(self->e))'), None, 'The source gcc.BasicBlock of this edge'), PyGetSetDef('dest', cu.add_simple_getter('PyGccEdge_get_dest', 'PyGccEdge', 'PyGccBasicBlock_New(gcc_cfg_edge_get_dest(self->e))'), None, 'The destination gcc.BasicBlock of this edge')], identifier_prefix = 'PyGccEdge', typename = 'PyGccEdge') # We only expose the subset of the flags exposed by gcc-c-api for attrname, flaggetter in [('true_value', 'is_true_value'), ('false_value', 'is_false_value'), ('loop_exit', 'is_loop_exit'), ('can_fallthru', 'get_can_fallthru'), ('complex', 'is_complex'), ('eh', 'is_eh'), ]: getsettable.add_simple_getter(cu, attrname, 'PyBool_FromLong(gcc_cfg_edge_%s(self->e))' % flaggetter, None) cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccEdge_TypeObj', localname = 'Edge', tp_name = 'gcc.Edge', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccEdge', tp_new = 'PyType_GenericNew', #tp_repr = '(reprfunc)PyGccEdge_repr', #tp_str = '(reprfunc)PyGccEdge_repr', tp_getset = getsettable.identifier, ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_edge() def generate_basic_block(): # # Generate the gcc.BasicBlock class: # global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccBasicBlock_getset_table', [PyGetSetDef('preds', 'PyGccBasicBlock_get_preds', None, 'The list of predecessor gcc.Edge instances leading into this block'), PyGetSetDef('succs', 'PyGccBasicBlock_get_succs', None, 'The list of successor gcc.Edge instances leading out of this block'), PyGetSetDef('gimple', 'PyGccBasicBlock_get_gimple', None, 'The list of gcc.Gimple instructions, if appropriate for this pass, or None'), PyGetSetDef('phi_nodes', 'PyGccBasicBlock_get_phi_nodes', None, 'The list of gcc.GimplePhi phoney functions, if appropriate for this pass, or None'), PyGetSetDef('rtl', 'PyGccBasicBlock_get_rtl', None, 'The list of gcc.Rtl instructions, if appropriate for this pass, or None'), ], identifier_prefix='PyGccBasicBlock', typename='PyGccBasicBlock') getsettable.add_simple_getter(cu, 'index', 'PyGccInt_FromLong(gcc_cfg_block_get_index(self->bb))', None) cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccBasicBlock_TypeObj', localname = 'BasicBlock', tp_name = 'gcc.BasicBlock', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccBasicBlock', tp_new = 'PyType_GenericNew', tp_repr = '(reprfunc)PyGccBasicBlock_repr', #tp_str = '(reprfunc)PyGccBasicBlock_repr', tp_getset = getsettable.identifier, ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_basic_block() def generate_cfg(): # # Generate the gcc.Cfg class: # global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccCfg_getset_table', [PyGetSetDef('basic_blocks', 'PyGccCfg_get_basic_blocks', None, 'The list of gcc.BasicBlock instances in this graph'), PyGetSetDef('entry', cu.add_simple_getter('PyGccCfg_get_entry', 'PyGccCfg', 'PyGccBasicBlock_New(gcc_cfg_get_entry(self->cfg))'), None, 'The initial gcc.BasicBlock in this graph'), PyGetSetDef('exit', cu.add_simple_getter('PyGccCfg_get_exit', 'PyGccCfg', 'PyGccBasicBlock_New(gcc_cfg_get_exit(self->cfg))'), None, 'The final gcc.BasicBlock in this graph'), ]) cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccCfg_TypeObj', localname = 'Cfg', tp_name = 'gcc.Cfg', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccCfg', tp_new = 'PyType_GenericNew', #tp_repr = '(reprfunc)PyGccCfg_repr', #tp_str = '(reprfunc)PyGccCfg_repr', tp_getset = getsettable.identifier, ) methods = PyMethodTable('PyGccCfg_methods', []) methods.add_method('get_block_for_label', 'PyGccCfg_get_block_for_label', 'METH_VARARGS', "Given a gcc.LabelDecl, get the corresponding gcc.BasicBlock") cu.add_defn(methods.c_defn()) pytype.tp_methods = methods.identifier cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_cfg() cu.add_defn(""" int autogenerated_cfg_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_cfg_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-config-h.py000066400000000000000000000115431342215241600206500ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . import os import sys from configbuilder import ConfigBuilder, CheckFor class GccPythonPluginConfigBuilder(ConfigBuilder): def __init__(self, argv): import argparse parser = argparse.ArgumentParser() parser.add_argument('--gcc') parser.add_argument('--plugindir') args, argv = parser.parse_known_args(argv) ConfigBuilder.__init__(self, argv) self.gcc = args.gcc self.plugindir = args.plugindir self.extra_compilation_args = [] def main(self): prefix = 'GCC_PYTHON_PLUGIN_CONFIG_' if self.plugindir: plugindir = self.plugindir else: plugindir = self.capture_shell_output('locating plugin directory for %s' % self.gcc, '%s --print-file-name=plugin' % self.gcc).strip() extraargs = ['-I%s' % os.path.join(plugindir, 'include')] self.test_whether_built_with_cplusplus() extraargs += self.extra_compilation_args # In GCC 4.9 onwards, this header file uses C++ syntax, so we # must already have determined C vs C++ at this point self.test_for_mandatory_c_header('gcc-plugin.h', extraargs) self.test_c_compilation(initmsg='checking whether plugin.def defines PLUGIN_FINISH_DECL', src=''' #include int i[PLUGIN_FINISH_DECL]; ''', extraargs=extraargs, description='Does plugin.def define PLUGIN_FINISH_DECL?', defn=prefix+'has_PLUGIN_FINISH_DECL') self.test_rtti() self.write_outcome() self.write_EXTRA_CFLAGS() def test_whether_built_with_cplusplus(self): """ Determine if the GCC was built with C++ or C """ # According to http://gcc.gnu.org/ml/gcc/2012-03/msg00411.html # we should look in $(gcc -print-file-name=plugin)/include/auto-host.h # where we'll see either: # # Old 4.6, built with C: # /* Define if building with C++. */ # #ifndef USED_FOR_TARGET # /* #undef ENABLE_BUILD_WITH_CXX */ # #endif # # New 4.7, built with C++: # /* Define if building with C++. */ # #ifndef USED_FOR_TARGET # #define ENABLE_BUILD_WITH_CXX 1 # #endif # # 4.8, built with C++ doesn't have this (always built with C++) with CheckFor('Checking whether %s was built with C or C++' % self.gcc, True) as check: def with_cplusplus(): check.okmsg = 'C++' self.extra_compilation_args += ['-x', 'c++'] def with_c(): check.okmsg = 'C' auto_host_h = os.path.join(self.plugindir, 'include', 'auto-host.h') with open(auto_host_h, 'r') as f: content = f.read() if '/* #undef ENABLE_BUILD_WITH_CXX */' in content: with_c() elif '#define ENABLE_BUILD_WITH_CXX 1' in content: with_cplusplus() else: # Not found, assume C++: with_cplusplus() def test_rtti(self): # Only need to do this check for C++ compilation if 'c++' in self.extra_compilation_args: with CheckFor('checking whether passes are C++ classes', True) as check: tree_pass_h = os.path.join(self.plugindir, 'include', 'tree-pass.h') with open(tree_pass_h, 'r') as f: content = f.read() if 'class opt_pass' in content: # Passes are C++ classes (GCC 4.9 onwards); we need # -fno-rtti self.extra_compilation_args += ['-fno-rtti'] check.okmsg = 'yes' else: check.okmsg = 'no' def write_EXTRA_CFLAGS(self): filename = 'autogenerated-EXTRA_CFLAGS.txt' sys.stdout.write('writing %s\n' % filename) with open(filename, 'w') as f: f.write(' '.join(self.extra_compilation_args)) ch = GccPythonPluginConfigBuilder(sys.argv) ch.main() gcc-python-plugin-0.17/generate-function-c.py000066400000000000000000000106521342215241600212230ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("function.h") cu.add_include("gcc-c-api/gcc-function.h") cu.add_include("gcc-c-api/gcc-declaration.h") cu.add_include("gcc-c-api/gcc-tree.h") modinit_preinit = '' modinit_postinit = '' def generate_function(): # # Generate the gcc.Function class: # global modinit_preinit global modinit_postinit cu.add_defn("\n" "static PyObject *\n" "PyGccFunction_get_cfg(struct PyGccFunction *self, void *closure)\n" "{\n" " return PyGccCfg_New(gcc_function_get_cfg(self->fun));\n" "}\n" "\n") getsettable = PyGetSetDefTable('PyGccFunction_getset_table', [PyGetSetDef('cfg', 'PyGccFunction_get_cfg', None, 'Instance of gcc.Cfg for this function (or None for early passes)'), ], identifier_prefix='PyGccFunction', typename='PyGccFunction') getsettable.add_simple_getter(cu, 'decl', 'PyGccTree_New(gcc_function_decl_as_gcc_tree(gcc_function_get_decl(self->fun)))', 'The declaration of this function, as a gcc.FunctionDecl instance') getsettable.add_simple_getter(cu, 'local_decls', 'VEC_tree_as_PyList(self->fun.inner->local_decls)', "List of gcc.VarDecl for the function's local variables") getsettable.add_simple_getter(cu, 'funcdef_no', 'PyGccInt_FromLong(gcc_function_get_index(self->fun))', 'Function sequence number for profiling, debugging, etc.') getsettable.add_simple_getter(cu, 'start', 'PyGccLocation_New(gcc_function_get_start(self->fun))', 'Location of the start of the function') getsettable.add_simple_getter(cu, 'end', 'PyGccLocation_New(gcc_function_get_end(self->fun))', 'Location of the end of the function') cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccFunction_TypeObj', localname = 'Function', tp_name = 'gcc.Function', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccFunction', tp_new = 'PyType_GenericNew', tp_repr = '(reprfunc)PyGccFunction_repr', tp_str = '(reprfunc)PyGccFunction_repr', tp_hash = '(hashfunc)PyGccFunction_hash', tp_richcompare = 'PyGccFunction_richcompare', tp_getset = getsettable.identifier, ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_function() cu.add_defn(""" int autogenerated_function_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_function_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-gimple-c.py000066400000000000000000000574141342215241600206620ustar00rootroot00000000000000# Copyright 2011, 2012, 2013 David Malcolm # Copyright 2011, 2012, 2013 Red Hat, Inc. # # This 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 # . from maketreetypes import iter_gimple_types, iter_gimple_struct_types from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("gimple.h") cu.add_include("gcc-c-api/gcc-gimple.h") cu.add_include("gcc-c-api/gcc-declaration.h") modinit_preinit = '' modinit_postinit = '' gimple_struct_types = list(iter_gimple_struct_types()) gimple_types = list(iter_gimple_types()) # gimple.h declares a family of struct gimple_statement_* which internally express an inheritance hierarchy, via enum enum gimple_statement_structure_enum (see gsstruct.def). # FIXME there's also enum gimple_code, which is used to look up into this hierarchy # # From reading gimple.h, the struct hierarchy is: # # gimple_statement_base (GSS_BASE) # gimple_statement_with_ops_base # gimple_statement_with_ops (GSS_WITH_OPS) # gimple_statement_with_memory_ops_base (GSS_WITH_MEM_OPS_BASE) # gimple_statement_with_memory_ops (GSS_WITH_MEM_OPS) # gimple_statement_call (GSS_CALL) # gimple_statement_asm (GSS_ASM) # gimple_statement_omp (GSS_OMP) # gimple_statement_omp_critical (GSS_OMP_CRITICAL) # gimple_statement_omp_for (GSS_OMP_FOR) # gimple_statement_omp_parallel (GSS_OMP_PARALLEL) # gimple_statement_omp_task (GSS_OMP_TASK) # gimple_statement_omp_sections (GSS_OMP_SECTIONS) # gimple_statement_omp_single (GSS_OMP_SINGLE) # gimple_statement_bind (GSS_BIND) # gimple_statement_catch (GSS_CATCH) # gimple_statement_eh_filter (GSS_EH_FILTER) # gimple_statement_eh_mnt (GSS_EH_MNT) # gimple_statement_phi (GSS_PHI) # gimple_statement_eh_ctrl (GSS_EH_CTRL) # gimple_statement_try (GSS_TRY) # gimple_statement_wce (GSS_WCE) # gimple_statement_omp_continue (GSS_OMP_CONTINUE) (does not inherit from gimple_statement_omp) # gimple_statement_omp_atomic_load (GSS_OMP_ATOMIC_LOAD (likewise) # gimple_statement_omp_atomic_store (GSS_OMP_ATOMIC_STORE) (ditto) # The inheritance hierarchy of struct gimple_statement_*, # expressed as (structname, parentstructname) pairs: struct_hier = (('gimple_statement_base', None), ('gimple_statement_with_ops_base', 'simple_statement_base'), ('gimple_statement_with_ops', 'gimple_statement_with_ops_base'), ('gimple_statement_with_memory_ops_base', 'gimple_statement_with_ops_base opbase'), ('gimple_statement_with_memory_ops', 'gimple_statement_with_memory_ops_base membase'), ('gimple_statement_call', 'gimple_statement_with_memory_ops_base membase'), ('gimple_statement_omp', 'gimple_statement_base'), ('gimple_statement_bind', 'gimple_statement_base'), ('gimple_statement_catch', 'gimple_statement_base'), ('gimple_statement_eh_filter', 'gimple_statement_base'), ('gimple_statement_eh_mnt', 'gimple_statement_base'), ('gimple_statement_phi', 'gimple_statement_base'), ('gimple_statement_eh_ct', 'gimple_statement_base'), ('gimple_statement_try', 'gimple_statement_base'), ('gimple_statement_wce', 'gimple_statement_base'), ('gimple_statement_asm', 'gimple_statement_with_memory_ops_base'), ('gimple_statement_omp_critical', 'gimple_statement_omp'), ('gimple_statement_omp_for', 'gimple_statement_omp'), ('gimple_statement_omp_parallel', 'gimple_statement_omp'), ('gimple_statement_omp_task', 'gimple_statement_omp_parallel'), ('gimple_statement_omp_sections', 'gimple_statement_omp'), ('gimple_statement_omp_continue', 'gimple_statement_base'), ('gimple_statement_omp_single', 'gimple_statement_omp'), ('gimple_statement_omp_atomic_load', 'gimple_statement_base'), ('gimple_statement_omp_atomic_store', 'gimple_statement_base')) # Interleaving with the material from gimple.def: # gimple_statement_base (GSS_BASE) # GIMPLE_ERROR_MARK, "gimple_error_mark", GSS_BASE # GIMPLE_NOP, "gimple_nop", GSS_BASE # GIMPLE_OMP_RETURN, "gimple_omp_return", GSS_BASE # GIMPLE_OMP_SECTIONS_SWITCH, "gimple_omp_sections_switch", GSS_BASE # GIMPLE_PREDICT, "gimple_predict", GSS_BASE # gimple_statement_with_ops_base # gimple_statement_with_ops (GSS_WITH_OPS) # GIMPLE_COND, "gimple_cond", GSS_WITH_OPS # GIMPLE_DEBUG, "gimple_debug", GSS_WITH_OPS # GIMPLE_GOTO, "gimple_goto", GSS_WITH_OPS # GIMPLE_LABEL, "gimple_label", GSS_WITH_OPS # GIMPLE_SWITCH, "gimple_switch", GSS_WITH_OPS # gimple_statement_with_memory_ops_base (GSS_WITH_MEM_OPS_BASE) # gimple_statement_with_memory_ops (GSS_WITH_MEM_OPS) # GIMPLE_ASSIGN, "gimple_assign", GSS_WITH_MEM_OPS # GIMPLE_RETURN, "gimple_return", GSS_WITH_MEM_OPS # gimple_statement_call (GSS_CALL) # GIMPLE_CALL, "gimple_call", GSS_CALL # gimple_statement_asm (GSS_ASM) # GIMPLE_ASM, "gimple_asm", GSS_ASM # gimple_statement_omp (GSS_OMP) # GIMPLE_OMP_MASTER, "gimple_omp_master", GSS_OMP # GIMPLE_OMP_ORDERED, "gimple_omp_ordered", GSS_OMP # GIMPLE_OMP_SECTION, "gimple_omp_section", GSS_OMP # gimple_statement_omp_critical (GSS_OMP_CRITICAL) # GIMPLE_OMP_CRITICAL, "gimple_omp_critical", GSS_OMP_CRITICAL # gimple_statement_omp_for (GSS_OMP_FOR) # GIMPLE_OMP_FOR, "gimple_omp_for", GSS_OMP_FOR # gimple_statement_omp_parallel (GSS_OMP_PARALLEL) # GIMPLE_OMP_PARALLEL, "gimple_omp_parallel", GSS_OMP_PARALLEL # gimple_statement_omp_task (GSS_OMP_TASK) # GIMPLE_OMP_TASK, "gimple_omp_task", GSS_OMP_TASK # gimple_statement_omp_sections (GSS_OMP_SECTIONS) # GIMPLE_OMP_SECTIONS, "gimple_omp_sections", GSS_OMP_SECTIONS # gimple_statement_omp_single (GSS_OMP_SINGLE) # GIMPLE_OMP_SINGLE, "gimple_omp_single", GSS_OMP_SINGLE # gimple_statement_bind (GSS_BIND) # GIMPLE_BIND, "gimple_bind", GSS_BIND # gimple_statement_catch (GSS_CATCH) # GIMPLE_CATCH, "gimple_catch", GSS_CATCH # gimple_statement_eh_filter (GSS_EH_FILTER) # GIMPLE_EH_FILTER, "gimple_eh_filter", GSS_EH_FILTER # gimple_statement_eh_mnt (GSS_EH_MNT) # GIMPLE_EH_MUST_NOT_THROW, "gimple_eh_must_not_throw", GSS_EH_MNT # gimple_statement_phi (GSS_PHI) # GIMPLE_PHI, "gimple_phi", GSS_PHI # gimple_statement_eh_ctrl (GSS_EH_CTRL) # GIMPLE_RESX, "gimple_resx", GSS_EH_CTRL # GIMPLE_EH_DISPATCH, "gimple_eh_dispatch", GSS_EH_CTRL # gimple_statement_try (GSS_TRY) # GIMPLE_TRY, "gimple_try", GSS_TRY # gimple_statement_wce (GSS_WCE) # GIMPLE_WITH_CLEANUP_EXPR, "gimple_with_cleanup_expr", GSS_WCE # gimple_statement_omp_continue (GSS_OMP_CONTINUE) (does not inherit from gimple_statement_omp) # GIMPLE_OMP_CONTINUE, "gimple_omp_continue", GSS_OMP_CONTINUE # gimple_statement_omp_atomic_load (GSS_OMP_ATOMIC_LOAD (likewise) # GIMPLE_OMP_ATOMIC_LOAD, "gimple_omp_atomic_load", GSS_OMP_ATOMIC_LOAD # gimple_statement_omp_atomic_store (GSS_OMP_ATOMIC_STORE) (ditto) # GIMPLE_OMP_ATOMIC_STORE, "gimple_omp_atomic_store", GSS_OMP_ATOMIC_STORE def generate_gimple_struct_subclasses(): global modinit_preinit global modinit_postinit for gt in gimple_struct_types: #print gimple_types cc = gt.camel_cased_string() pytype = PyGccWrapperTypeObject(identifier = 'PyGccGimpleStructType%s_TypeObj' % cc, localname = 'GimpleStructType' + cc, tp_name = 'gcc.GimpleStructType%s' % cc, tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccGimple', tp_new = 'PyType_GenericNew', tp_base = '&PyGccGimple_TypeObj', #tp_getset = getsettable.identifier, #tp_repr = '(reprfunc)PyGccGimple_repr', #tp_str = '(reprfunc)PyGccGimple_str', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() #generate_gimple_struct_subclasses() # See gcc/gimple-pretty-print.c (e.g. /usr/src/debug/gcc-4.6.0-20110321/gcc/gimple-pretty-print.c ) # for hints on the innards of gimple, in particular, see dump_gimple_stmt def generate_gimple(): # # Generate the gcc.Gimple class: # global modinit_preinit global modinit_postinit cu.add_defn(""" static PyObject * PyGccGimple_get_location(struct PyGccGimple *self, void *closure) { return PyGccLocation_New(gcc_gimple_get_location(self->stmt)); } static PyObject * PyGccGimple_get_block(struct PyGccGimple *self, void *closure) { return PyGccTree_New(gcc_gimple_get_block(self->stmt)); } """) getsettable = PyGetSetDefTable('PyGccGimple_getset_table', [PyGetSetDef('loc', 'PyGccGimple_get_location', None, 'Source code location of this statement, as a gcc.Location'), PyGetSetDef('block', 'PyGccGimple_get_block', None, 'The lexical block holding this statement, as a gcc.Tree'), PyGetSetDef('exprtype', cu.add_simple_getter('PyGccGimple_get_exprtype', 'PyGccGimple', 'PyGccTree_New(gcc_gimple_get_expr_type(self->stmt))'), None, 'The type of the main expression computed by this statement, as a gcc.Tree (which might be gcc.Void_TypeObj)'), PyGetSetDef('str_no_uid', 'PyGccGimple_get_str_no_uid', None, 'A string representation of this statement, like str(), but without including any internal UID'), ]) cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccGimple_TypeObj', localname = 'Gimple', tp_name = 'gcc.Gimple', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccGimple', tp_new = 'PyType_GenericNew', tp_getset = getsettable.identifier, tp_repr = '(reprfunc)PyGccGimple_repr', tp_str = '(reprfunc)PyGccGimple_str', tp_hash = '(hashfunc)PyGccGimple_hash', tp_richcompare = 'PyGccGimple_richcompare', tp_flags = 'Py_TPFLAGS_BASETYPE', ) methods = PyMethodTable('PyGccGimple_methods', []) methods.add_method('walk_tree', '(PyCFunction)PyGccGimple_walk_tree', 'METH_VARARGS | METH_KEYWORDS', "Visit all gcc.Tree nodes associated with this statement") cu.add_defn(methods.c_defn()) pytype.tp_methods = methods.identifier cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_gimple() def generate_gimple_subclasses(): global modinit_preinit global modinit_postinit exprcode_getter = PyGetSetDef('exprcode', cu.add_simple_getter('PyGccGimple_get_exprcode', 'PyGccGimple', '(PyObject*)PyGcc_autogenerated_tree_type_for_tree_code(gimple_expr_code(self->stmt.inner), 0)'), None, 'The kind of the expression, as an gcc.Tree subclass (the type itself, not an instance)') rhs_getter = PyGetSetDef('rhs', 'PyGccGimple_get_rhs', None, 'The operands on the right-hand-side of the expression, as a list of gcc.Tree instances') def make_getset_Asm(): getsettable = PyGetSetDefTable('gcc_%s_getset_table' % cc, [exprcode_getter], 'PyGccGimpleAsm', 'PyGccGimple') getsettable.add_simple_getter(cu, 'string', 'PyGccString_FromString(gcc_gimple_asm_get_string(PyGccGimple_as_gcc_gimple_asm(self)))', 'The inline assembler as a string') return getsettable def make_getset_Assign(): return PyGetSetDefTable('gcc_%s_getset_table' % cc, [PyGetSetDef('lhs', cu.add_simple_getter('PyGccGimpleAssign_get_lhs', 'PyGccGimple', 'PyGccTree_New(gcc_gimple_assign_get_lhs(PyGccGimple_as_gcc_gimple_assign(self)))'), None, 'Left-hand-side of the assignment, as a gcc.Tree'), exprcode_getter, rhs_getter, ]) def make_getset_Call(): return PyGetSetDefTable('gcc_%s_getset_table' % cc, [PyGetSetDef('lhs', cu.add_simple_getter('PyGccGimpleCall_get_lhs', 'PyGccGimple', 'PyGccTree_New(gcc_gimple_call_get_lhs(PyGccGimple_as_gcc_gimple_call(self)))'), None, 'Left-hand-side of the call, as a gcc.Tree'), rhs_getter, PyGetSetDef('fn', cu.add_simple_getter('PyGccGimpleCall_get_fn', 'PyGccGimple', 'PyGccTree_New(gcc_gimple_call_get_fn(PyGccGimple_as_gcc_gimple_call(self)))'), None, 'The function being called, as a gcc.Tree'), PyGetSetDef('fndecl', cu.add_simple_getter('PyGccGimpleCall_get_fndecl', 'PyGccGimple', 'PyGccTree_New(gcc_gimple_call_get_fndecl(PyGccGimple_as_gcc_gimple_call(self)))'), None, 'The declaration of the function being called (if any), as a gcc.Tree'), exprcode_getter, PyGetSetDef('args', 'PyGccGimpleCall_get_args', None, 'The arguments for the call, as a list of gcc.Tree'), PyGetSetDef('noreturn', cu.add_simple_getter('PyGccGimpleCall_get_noreturn', 'PyGccGimple', 'PyBool_FromLong(gcc_gimple_call_is_noreturn(PyGccGimple_as_gcc_gimple_call(self)))'), None, 'Has this call been marked as not returning, as a boolean'), ], ) def make_getset_Return(): return PyGetSetDefTable('gcc_%s_getset_table' % cc, [PyGetSetDef('retval', cu.add_simple_getter('PyGccGimpleReturn_get_retval', 'PyGccGimple', 'PyGccTree_New(gcc_gimple_return_get_retval(PyGccGimple_as_gcc_gimple_return(self)))'), None, 'The return value, as a gcc.Tree'), ]) def make_getset_Cond(): getsettable = PyGetSetDefTable('gcc_%s_getset_table' % cc, [exprcode_getter], 'PyGccGimpleCond', 'PyGccGimple') getsettable.add_simple_getter(cu, 'lhs', 'PyGccTree_New(gcc_gimple_cond_get_lhs(PyGccGimple_as_gcc_gimple_cond(self)))', None) getsettable.add_simple_getter(cu, 'rhs', 'PyGccTree_New(gcc_gimple_cond_get_rhs(PyGccGimple_as_gcc_gimple_cond(self)))', None) getsettable.add_simple_getter(cu, 'true_label', 'PyGccTree_New(gcc_gimple_cond_get_true_label(PyGccGimple_as_gcc_gimple_cond(self)))', None) getsettable.add_simple_getter(cu, 'false_label', 'PyGccTree_New(gcc_gimple_cond_get_false_label(PyGccGimple_as_gcc_gimple_cond(self)))', None) return getsettable def make_getset_Phi(): getsettable = PyGetSetDefTable('gcc_%s_getset_table' % cc, [exprcode_getter], 'PyGccGimplePhi', 'PyGccGimple') getsettable.add_simple_getter(cu, 'lhs', 'PyGccTree_New(gcc_gimple_phi_get_result(PyGccGimple_as_gcc_gimple_phi(self)))', None) getsettable.add_gsdef('args', 'PyGccGimplePhi_get_args', None, 'Get a list of (gcc.Tree, gcc.Edge) pairs representing the possible (expr, edge) inputs') # FIXME: should we instead have a dict here? return getsettable def make_getset_Switch(): getsettable = PyGetSetDefTable('gcc_%s_getset_table' % cc, [exprcode_getter], 'PyGccGimpleSwitch', 'PyGccGimple') getsettable.add_simple_getter(cu, 'indexvar', 'PyGccTree_New(gcc_gimple_switch_get_indexvar(PyGccGimple_as_gcc_gimple_switch(self)))', 'Get the index variable used by the switch statement, as a gcc.Tree') getsettable.add_gsdef('labels', 'PyGccGimpleSwitch_get_labels', None, 'Get a list of labels, as a list of gcc.CaseLabelExpr The initial label in the list is always the default.') return getsettable def make_getset_Label(): getsettable = PyGetSetDefTable('gcc_%s_getset_table' % cc, [exprcode_getter], 'PyGccGimpleLabel', 'PyGccGimple') getsettable.add_simple_getter(cu, 'label', 'PyGccTree_New(gcc_label_decl_as_gcc_tree(gcc_gimple_label_get_label(PyGccGimple_as_gcc_gimple_label(self))))', 'Get the underlying gcc.LabelDecl for this statement') return getsettable for gt in gimple_types: cc = gt.camel_cased_string() tp_repr = None getsettable = None if cc == 'GimpleAsm': getsettable = make_getset_Asm() elif cc == 'GimpleAssign': getsettable = make_getset_Assign() elif cc == 'GimpleCall': getsettable = make_getset_Call() elif cc == 'GimpleCond': getsettable = make_getset_Cond() elif cc == 'GimpleReturn': getsettable = make_getset_Return() elif cc == 'GimplePhi': getsettable = make_getset_Phi() elif cc == 'GimpleSwitch': getsettable = make_getset_Switch() elif cc == 'GimpleLabel': getsettable = make_getset_Label() tp_repr = '(reprfunc)PyGccGimpleLabel_repr' if getsettable: cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGcc%s_TypeObj' % cc, localname = cc, tp_name = 'gcc.%s' % cc, tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccGimple', tp_new = 'PyType_GenericNew', tp_base = '&PyGccGimple_TypeObj', tp_getset = getsettable.identifier if getsettable else None, tp_repr = tp_repr, #tp_str = '(reprfunc)PyGccGimple_str', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_gimple_subclasses() def generate_gimple_code_map(): cu.add_defn('\n/* Map from GCC gimple codes to PyTypeObject* */\n') cu.add_defn('PyGccWrapperTypeObject *pytype_for_gimple_code[] = {\n') for gt in gimple_types: cc = gt.camel_cased_string() cu.add_defn(' &PyGcc%s_TypeObj, /* %s */\n' % (cc, gt.gimple_symbol)) cu.add_defn('};\n\n') cu.add_defn(""" PyGccWrapperTypeObject* PyGcc_autogenerated_gimple_type_for_stmt(gcc_gimple stmt) { enum gimple_code code = gimple_code(stmt.inner); /* printf("code:%i\\n", code); */ assert(code >= 0); assert(code < LAST_AND_UNUSED_GIMPLE_CODE); return pytype_for_gimple_code[code]; } """) generate_gimple_code_map() cu.add_defn(""" int autogenerated_gimple_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_gimple_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-location-c.py000066400000000000000000000154221342215241600212060ustar00rootroot00000000000000# Copyright 2011-2014, 2017 David Malcolm # Copyright 2011-2014, 2017 Red Hat, Inc. # # This 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 # . from cpybuilder import * from testcpychecker import get_gcc_version from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("gcc-c-api/gcc-location.h") modinit_preinit = '' modinit_postinit = '' def generate_location(): # # Generate the gcc.Location class: # global modinit_preinit global modinit_postinit cu.add_defn(""" static PyObject * PyGccLocation_get_file(struct PyGccLocation *self, void *closure) { const char *filename = gcc_location_get_filename(self->loc); if (!filename) { Py_RETURN_NONE; } return PyGccString_FromString(filename); } """) cu.add_defn(""" static PyObject * PyGccLocation_get_line(struct PyGccLocation *self, void *closure) { return PyGccInt_FromLong(gcc_location_get_line(self->loc)); } """) cu.add_defn(""" static PyObject * PyGccLocation_get_column(struct PyGccLocation *self, void *closure) { return PyGccInt_FromLong(gcc_location_get_column(self->loc)); } """) if get_gcc_version() >= 7000: cu.add_defn(""" static PyObject * PyGccLocation_get_caret(struct PyGccLocation *self, void *closure) { return PyGccLocation_New(gcc_location_get_caret(self->loc)); } """) cu.add_defn(""" static PyObject * PyGccLocation_get_start(struct PyGccLocation *self, void *closure) { return PyGccLocation_New(gcc_location_get_start(self->loc)); } """) cu.add_defn(""" static PyObject * PyGccLocation_get_finish(struct PyGccLocation *self, void *closure) { return PyGccLocation_New(gcc_location_get_finish(self->loc)); } """) getsettable = PyGetSetDefTable('PyGccLocation_getset_table', [PyGetSetDef('file', 'PyGccLocation_get_file', None, 'Name of the source file'), PyGetSetDef('line', 'PyGccLocation_get_line', None, 'Line number within source file'), PyGetSetDef('column', 'PyGccLocation_get_column', None, 'Column number within source file'), ], identifier_prefix='PyGccLocation', typename='PyGccLocation') if get_gcc_version() >= 7000: getsettable.gsdefs += [PyGetSetDef('caret', 'PyGccLocation_get_caret', None, 'Location of caret'), PyGetSetDef('start', 'PyGccLocation_get_start', None, 'Starting location of range'), PyGetSetDef('finish', 'PyGccLocation_get_finish', None, 'End location of range')] getsettable.add_simple_getter(cu, 'in_system_header', 'PyBool_FromLong(gcc_location_get_in_system_header(self->loc))', 'Boolean: is this location within a system header?') cu.add_defn(getsettable.c_defn()) methods = PyMethodTable('PyGccLocation_methods', []) if get_gcc_version() >= 5000: methods.add_method('offset_column', '(PyCFunction)PyGccLocation_offset_column', 'METH_VARARGS', "") cu.add_defn(methods.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccLocation_TypeObj', localname = 'Location', tp_name = 'gcc.Location', struct_name = 'PyGccLocation', tp_new = 'PyType_GenericNew', tp_init = '(initproc)PyGccLocation_init' if get_gcc_version() >= 7000 else None, tp_getset = getsettable.identifier, tp_hash = '(hashfunc)PyGccLocation_hash', tp_repr = '(reprfunc)PyGccLocation_repr', tp_str = '(reprfunc)PyGccLocation_str', tp_methods = methods.identifier, tp_richcompare = 'PyGccLocation_richcompare', tp_dealloc = 'PyGccWrapper_Dealloc') cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() def generate_rich_location(): # # Generate the gcc.RichLocation class: # # class rich_location was added to libcpp in gcc 6. GCC_VERSION = get_gcc_version() if GCC_VERSION < 6000: return global modinit_preinit global modinit_postinit methods = PyMethodTable('PyGccRichLocation_methods', []) methods.add_method('add_fixit_replace', '(PyCFunction)PyGccRichLocation_add_fixit_replace', 'METH_VARARGS | METH_KEYWORDS', "FIXME") cu.add_defn(methods.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccRichLocation_TypeObj', localname = 'RichLocation', tp_name = 'gcc.RichLocation', struct_name = 'PyGccRichLocation', tp_new = 'PyType_GenericNew', tp_init = '(initproc)PyGccRichLocation_init', #tp_getset = getsettable.identifier, #tp_hash = '(hashfunc)PyGccRichLocation_hash', #tp_repr = '(reprfunc)PyGccRichLocation_repr', #tp_str = '(reprfunc)PyGccRichLocation_str', tp_methods = methods.identifier, #tp_richcompare = 'PyGccRichLocation_richcompare', tp_dealloc = 'PyGccWrapper_Dealloc') cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_location() generate_rich_location() cu.add_defn(""" int autogenerated_location_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_location_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-option-c.py000066400000000000000000000073211342215241600207050ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("diagnostic.h") # FIXME: need to participate in the GC, to ensure ownership of the underlying # diag object modinit_preinit = '' modinit_postinit = '' def generate_option(): # # Generate the gcc.Option class: # global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccOption_getset_table', [], identifier_prefix='PyGccOption', typename='PyGccOption') def add_simple_getter(name, c_expression, doc): getsettable.add_simple_getter(cu, name, c_expression, doc) add_simple_getter('text', 'PyGccStringOrNone(PyGcc_option_to_cl_option(self)->opt_text)', '(string) The command-line text used to set this option') add_simple_getter('help', 'PyGccStringOrNone(PyGcc_option_to_cl_option(self)->help)', '(string) The help text shown for this option') for flag, helptext in ( ('CL_WARNING', '(bool) Does this option control a warning message?'), ('CL_OPTIMIZATION', '(bool) Does this option control an optimization?'), ('CL_DRIVER', '(bool) Is this a driver option?'), ('CL_TARGET', '(bool) Is this a target-specific option?'), ): add_simple_getter('is_%s' % flag[3:].lower(), 'PyBool_FromLong(PyGcc_option_to_cl_option(self)->flags & %s)' % flag, helptext) getsettable.add_gsdef('is_enabled', 'PyGccOption_is_enabled', None, 'True/False for whether or not this option is enabled, raising a ' 'NotImplementedError for options for which the plugin cannot tell') cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccOption_TypeObj', localname = 'Option', tp_name = 'gcc.Option', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccOption', tp_init = 'PyGccOption_init', tp_getset = getsettable.identifier, tp_repr = '(reprfunc)PyGccOption_repr', #tp_str = '(reprfunc)PyGccOption_str', #tp_richcompare = 'PyGccOption_richcompare' ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_option() cu.add_defn(""" int autogenerated_option_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_option_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-parameter-c.py000066400000000000000000000102541342215241600213540ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("params.h") cu._prototypes += ''' #define PARAM_INFO(self) (compiler_params[(self)->param_num]) ''' modinit_preinit = '' modinit_postinit = '' # GCC's params.h defines # typedef struct param_info param_info # and they're listed in params.def def generate_param(): # # Generate the gcc.Parameter class: # global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccParameter_getset_table', [], identifier_prefix='PyGccParameter', typename='PyGccParameter') def add_simple_getter(name, c_expression, doc): getsettable.add_simple_getter(cu, name, c_expression, doc) add_simple_getter('option', 'PyGccStringOrNone(PARAM_INFO(self).option)', "(string) The name used with the `--param =' switch to set this value") add_simple_getter('default_value', 'PyGccInt_FromLong(PARAM_INFO(self).default_value)', "(int/long)") add_simple_getter('min_value', 'PyGccInt_FromLong(PARAM_INFO(self).min_value)', "(int/long) The minimum acceptable value") add_simple_getter('max_value', 'PyGccInt_FromLong(PARAM_INFO(self).max_value)', "(int/long) The maximum acceptable value, if greater than min_value") add_simple_getter('help', 'PyGccStringOrNone(PARAM_INFO(self).help)', "(string) A short description of the option.") # "current_value": getter = cu.add_simple_getter('PyGccParameter_get_current_value', 'PyGccParameter', 'PyGccInt_FromLong(PARAM_VALUE(self->param_num))') setter = cu.add_simple_int_setter('PyGccParameter_set_current_value', 'PyGccParameter', 'current_value', 'global_options.x_param_values[self->param_num] = PyGccInt_AsLong(value)') #FIXME getsettable.add_gsdef('current_value', getter, setter, "(int/long)") cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccParameter_TypeObj', localname = 'Parameter', tp_name = 'gcc.Parameter', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccParameter', tp_new = 'PyType_GenericNew', tp_getset = getsettable.identifier, #tp_repr = '(reprfunc)PyGccParameter_repr', #tp_str = '(reprfunc)PyGccParameter_str', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_param() cu.add_defn(""" int autogenerated_parameter_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_parameter_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-pass-c.py000066400000000000000000000144051342215241600203440ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("tree-pass.h") modinit_preinit = '' modinit_postinit = '' # See GCC's tree-pass.h def generate_pass(): global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccPass_getset_table', [], identifier_prefix='PyGccPass', typename='PyGccPass') getsettable.add_simple_getter(cu, 'name', 'PyGccString_FromString(self->pass->name)', 'Name of the pass') getsettable.add_simple_getter(cu, 'next', 'PyGccPass_New(self->pass->next)', 'The next gcc.Pass after this one, or None') getsettable.add_simple_getter(cu, 'sub', 'PyGccPass_New(self->pass->sub)', "The first sub-pass (gated by this pass' predicate, if any)") for field in ('properties_required', 'properties_provided', 'properties_destroyed'): getsettable.add_simple_getter(cu, field, 'PyGccInt_FromLong(self->pass->%s)' % field, None) getsettable.add_simple_getter(cu, 'static_pass_number', 'PyGccInt_FromLong(self->pass->static_pass_number)', 'Number of this pass, used as a fragment of the dump file name') getsettable.add_gsdef('dump_enabled', 'PyGccPass_get_dump_enabled', 'PyGccPass_set_dump_enabled', '(boolean) Is dumping enabled for this pass?') cu.add_defn(getsettable.c_defn()) methods = PyMethodTable('PyGccPass_methods', []) methods.add_method('get_roots', 'PyGccPass_get_roots', 'METH_CLASS | METH_VARARGS', "Get a tuple of gcc.Pass instances, the roots of the compilation pass tree") methods.add_method('get_by_name', '(PyCFunction)PyGccPass_get_by_name', 'METH_CLASS | METH_VARARGS | METH_KEYWORDS', "Get the gcc.Pass instance for the pass with the given name, raising ValueError if it isn't found") methods.add_method('register_after', '(PyCFunction)PyGccPass_register_after', 'METH_VARARGS | METH_KEYWORDS', "Given the name of another pass, register this gcc.Pass to occur immediately after that other pass") methods.add_method('register_before', '(PyCFunction)PyGccPass_register_before', 'METH_VARARGS | METH_KEYWORDS', "Given the name of another pass, register this gcc.Pass to occur immediately before that other pass") methods.add_method('replace', '(PyCFunction)PyGccPass_replace', 'METH_VARARGS | METH_KEYWORDS', "Given the name of another pass, replace that pass with this gcc.Pass") cu.add_defn(methods.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccPass_TypeObj', localname = 'Pass', tp_name = 'gcc.Pass', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccPass', tp_new = 'PyType_GenericNew', tp_getset = getsettable.identifier, tp_repr = '(reprfunc)PyGccPass_repr', tp_str = '(reprfunc)PyGccPass_repr', tp_methods = methods.identifier, tp_flags = '(Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE)', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_pass() def generate_pass_subclasses(): global modinit_preinit global modinit_postinit for opt_pass_type in ('GIMPLE_PASS', 'RTL_PASS', 'SIMPLE_IPA_PASS', 'IPA_PASS'): cc = camel_case(opt_pass_type) pytype = PyGccWrapperTypeObject(identifier = 'PyGcc%s_TypeObj' % cc, localname = cc, tp_name = 'gcc.%s' % cc, tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccPass', tp_new = 'PyType_GenericNew', tp_init = 'PyGcc%s_init' % cc, tp_base = '&PyGccPass_TypeObj', tp_flags = '(Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE)' ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_pass_subclasses() cu.add_defn(""" int autogenerated_pass_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_pass_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-passes-svg.py000066400000000000000000000136321342215241600212520ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . import gcc import math HEADING_HEIGHT = 20 TREE_X_INDENT = 24 PASS_HEIGHT = 12 PASS_LEADING = 4 PHASE_SPACING = 50 propdata = [ ('PROP_gimple_any', (1, 0, 0)), ('PROP_gimple_lcf', (0, 1, 0)), ('PROP_gimple_leh', (0, 0, 1)), ('PROP_cfg', (0, 1, 1)), ('PROP_referenced_vars', (1, 1, 0)), ('PROP_ssa', (0.5, 0.5, 1)), ('PROP_no_crit_edges', (0, 0, 0)), ('PROP_rtl', (0.5, 0.5, 0.5)), ('PROP_gimple_lomp', (0.75, 1, 0)), ('PROP_cfglayout', (0, 1, 0.75)), ('PROP_gimple_lcx', (0.25, 0.25, 0.25)), ] # PROP_referenced_vars went away in GCC 4.8 (in r190067) if not hasattr(gcc, 'PROP_referenced_vars'): propdata = [(flag, color) for (flag, color) in propdata if flag != 'PROP_referenced_vars'] def show_text_x_centered(ctx, text, x, y): ctx.set_source_rgb(0, 0, 0) te = ctx.text_extents(text) ctx.move_to(x - te[2]/2, y) ctx.show_text(text) def show_text_y_centered(ctx, text, x, y): ctx.set_source_rgb(0, 0, 0) te = ctx.text_extents(text) ctx.move_to(x, y + te[3]/2) ctx.show_text(text) class Property: def __init__(self, flagname, color, x): assert flagname.startswith('PROP_') self.name = flagname[5:] self.color = color self.flag = getattr(gcc, flagname) self.provided_by = None self.destroyed_by = None self.x = x def render(self, ctx): # Draw a line showing the lifetime of the property: if self.provided_by: start_y = self.provided_by.y else: start_y = 0 if self.destroyed_by: end_y = self.destroyed_by.y else: end_y = 4000 ctx.set_source_rgb(*self.color) ctx.move_to(self.x, start_y) ctx.line_to(self.x, end_y) ctx.stroke() # Draw termini: if self.provided_by: # ctx.arc(self.x, self.provided_by.y, 10, 0, 2 * math.pi) ctx.set_source_rgb(*self.color) ctx.move_to(self.x - 5, start_y) ctx.line_to(self.x + 5, start_y) ctx.stroke() show_text_x_centered(ctx, self.name, self.x, start_y - 5) else: show_text_x_centered(ctx, self.name, self.x, PASS_HEIGHT) if self.destroyed_by: # ctx.arc(self.x, self.destroyed_by.y, 10, 0, 2 * math.pi) ctx.set_source_rgb(*self.color) ctx.move_to(self.x - 5, end_y) ctx.line_to(self.x + 5, end_y) ctx.stroke() properties = [Property(propname, col, 250 + (i*20)) for i, (propname, col) in enumerate(propdata)] class PassInfo: def __init__(self, d, ps, parent): self.d = d self.ps = ps for prop in properties: if self.ps.properties_provided & prop.flag: prop.provided_by = self if self.ps.properties_destroyed & prop.flag: prop.destroyed_by = self def render(self, ctx): show_text_y_centered(ctx, self.ps.name, self.x, self.y) for i, prop in enumerate(properties): if self.ps.properties_required & prop.flag: ctx.set_source_rgb(*prop.color) ctx.move_to(prop.x, self.y) ctx.line_to(prop.x - 5, self.y) ctx.stroke() class Phase: def __init__(self, rootname, base_y): self.passinfo = {} # gcc.Pass ->PassInfo self.rootname = rootname self.base_y = base_y self.height = base_y + HEADING_HEIGHT + PASS_LEADING def add_pass(self, ps, parent): # print ps.name pi = PassInfo(self, ps, parent) if parent: ppi = self.passinfo[parent] pi.x = ppi.x + TREE_X_INDENT else: pi.x = 0 pi.y = self.height if self.passinfo == {}: self.height += PASS_LEADING + PASS_HEIGHT else: self.height += PASS_HEIGHT self.passinfo[ps] = pi if ps.sub: self.add_pass(ps.sub, ps) if ps.next: self.add_pass(ps.next, parent) def render(self, ctx): ctx.move_to(0, self.base_y) ctx.set_source_rgb(0, 0, 0) ctx.set_font_size(HEADING_HEIGHT) ctx.show_text(self.rootname) ctx.set_font_size(PASS_HEIGHT) for k in self.passinfo: pi = self.passinfo[k] pi.render(ctx) phases = {} base_y = HEADING_HEIGHT for i, (rootname, reflabel, ps) in enumerate( zip(('The lowering passes', 'The "small IPA" passes', 'The "regular IPA" passes', 'Passes generating Link-Time Optimization data', 'The "all other passes" catch-all'), ('all_lowering_passes', 'all_small_ipa_passes', 'all_regular_ipa_passes', 'all_lto_gen_passes', 'all_passes'), gcc.Pass.get_roots())): # print rootname, reflabel, ps phases[i] = Phase(rootname, base_y) phases[i].add_pass(ps, None) base_y = phases[i].height + PHASE_SPACING import cairo surf = cairo.SVGSurface('docs/passes.svg', 550, base_y) ctx = cairo.Context(surf) # Fill with white: ctx.set_source_rgb(1, 1, 1) ctx.paint() for prop in properties: prop.render(ctx) for i in range(5): phases[i].render(ctx) surf.finish() gcc-python-plugin-0.17/generate-pretty-printer-c.py000066400000000000000000000035251342215241600224070ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . from cpybuilder import * cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') modinit_preinit = '' modinit_postinit = '' def generate_pretty_printer(): global modinit_preinit global modinit_postinit pytype = PyTypeObject(identifier = 'PyGccPrettyPrinter_TypeObj', localname = 'PrettyPrinter', tp_name = 'gcc.PrettyPrinter', struct_name = 'struct PyGccPrettyPrinter', tp_new = 'PyType_GenericNew', tp_dealloc = 'PyGccPrettyPrinter_dealloc', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_pretty_printer() cu.add_defn(""" int autogenerated_pretty_printer_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_pretty_printer_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-rtl-c.py000066400000000000000000000147211342215241600202000ustar00rootroot00000000000000# Copyright 2011-2012, 2015 David Malcolm # Copyright 2011-2012, 2015 Red Hat, Inc. # # This 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 # . from maketreetypes import iter_rtl_expr_types #, iter_rtl_struct_types from cpybuilder import * from testcpychecker import get_gcc_version from wrapperbuilder import PyGccWrapperTypeObject GCC_VERSION = get_gcc_version() cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("rtl.h") modinit_preinit = '' modinit_postinit = '' #rtl_struct_types = list(iter_rtl_struct_types()) rtl_expr_types = list(iter_rtl_expr_types()) #print rtl_types # Should be a three-level hierarchy: # # - Rtl base class # - intermediate subclasses, one per enum rtx_class # - concrete subclasses, one per enum rtx_code def generate_rtl_base_class(): # # Generate the gcc.Rtl class: # global modinit_preinit global modinit_postinit ''' cu.add_defn(""" static PyObject * PyGccRtl_get_block(struct PyGccRtl *self, void *closure) { return PyGccTree_New(rtl_block(self->stmt)); } """) ''' getsettable = PyGetSetDefTable('PyGccRtl_getset_table', []) if GCC_VERSION < 5000: getsettable.add_gsdef('loc', 'PyGccRtl_get_location', None, 'Source code location of this instruction, as a gcc.Location') getsettable.add_gsdef('operands', 'PyGccRtl_get_operands', None, 'Operands of this expression, as a tuple') cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccRtl_TypeObj', localname = 'Rtl', tp_name = 'gcc.Rtl', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccRtl', tp_new = 'PyType_GenericNew', tp_getset = getsettable.identifier, tp_repr = '(reprfunc)PyGccRtl_repr', tp_str = '(reprfunc)PyGccRtl_str', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_rtl_base_class() # enum rtx_class from gcc/rtl.h (as seen in 4.6.0): enum_rtx_class = ('RTX_COMPARE', 'RTX_COMM_COMPARE', 'RTX_BIN_ARITH', 'RTX_COMM_ARITH', 'RTX_UNARY', 'RTX_EXTRA', 'RTX_MATCH', 'RTX_INSN', 'RTX_OBJ', 'RTX_CONST_OBJ', 'RTX_TERNARY', 'RTX_BITFIELD_OPS', 'RTX_AUTOINC') def generate_intermediate_rtx_class_subclasses(): global modinit_preinit global modinit_postinit for rtx_class in enum_rtx_class: cc = camel_case(rtx_class) pytype = PyGccWrapperTypeObject(identifier = 'PyGccRtlClassType%s_TypeObj' % cc, localname = 'RtlClassType' + cc, tp_name = 'gcc.%s' % cc, struct_name = 'PyGccRtl', tp_new = 'PyType_GenericNew', tp_base = '&PyGccRtl_TypeObj', #tp_getset = getsettable.identifier, #tp_repr = '(reprfunc)PyGccRtl_repr', #tp_str = '(reprfunc)PyGccRtl_str', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_intermediate_rtx_class_subclasses() def generate_concrete_rtx_code_subclasses(): global modinit_preinit global modinit_postinit for expr_type in rtl_expr_types: cc = expr_type.camel_cased_string() getsettable = None if getsettable: cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGcc%s_TypeObj' % cc, localname = cc, tp_name = 'gcc.%s' % cc, struct_name = 'PyGccRtl', tp_new = 'PyType_GenericNew', tp_base = ('&PyGccRtlClassType%s_TypeObj' % camel_case(expr_type.CLASS)), tp_getset = getsettable.identifier if getsettable else None, tp_repr = '(reprfunc)PyGccRtl_repr', tp_str = '(reprfunc)PyGccRtl_str', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_concrete_rtx_code_subclasses() def generate_rtl_code_map(): cu.add_defn('\n/* Map from GCC rtl codes to PyGccWrapperTypeObject* */\n') cu.add_defn('PyGccWrapperTypeObject *pytype_for_rtx_code[] = {\n') for expr_type in rtl_expr_types: cc = expr_type.camel_cased_string() cu.add_defn(' &PyGcc%s_TypeObj, /* %s */\n' % (cc, expr_type.ENUM)) cu.add_defn('};\n\n') cu.add_defn(""" PyGccWrapperTypeObject* PyGcc_autogenerated_rtl_type_for_stmt(gcc_rtl_insn insn) { enum rtx_code code = insn.inner->code; /* printf("code:%i\\n", code); */ assert(code >= 0); assert(code < LAST_AND_UNUSED_RTX_CODE); return pytype_for_rtx_code[code]; } """) generate_rtl_code_map() cu.add_defn(""" int autogenerated_rtl_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_rtl_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-tables-of-passes-rst.py000066400000000000000000000073001342215241600231300ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # We use this script to regenerate docs/tables-of-passes.rst import gcc import sys import six from gccutils import Table pass_prop_flags = ['PROP_gimple_any', 'PROP_gimple_lcf', 'PROP_gimple_leh', 'PROP_cfg', 'PROP_referenced_vars', 'PROP_ssa', 'PROP_no_crit_edges', 'PROP_rtl', 'PROP_gimple_lomp', 'PROP_cfglayout', 'PROP_gimple_lcx'] # PROP_referenced_vars went away in GCC 4.8 (in r190067) if not hasattr(gcc, 'PROP_referenced_vars'): pass_prop_flags.remove('PROP_referenced_vars') def pass_properties_to_str(bitfield): result = [] for attrname in pass_prop_flags: flag = getattr(gcc, attrname) if bitfield & flag: result.append(attrname[5:]) # drop the 'PROP_' return ', '.join(result) p_to_s = pass_properties_to_str def foo(t, ps, indent): name = ps.name.replace('*', '\\*') t.add_row(('%s%s' % (six.u('> ') * indent, name), p_to_s(ps.properties_required), p_to_s(ps.properties_provided), p_to_s(ps.properties_destroyed))) if ps.sub: foo(t, ps.sub, indent + 1) if ps.next: foo(t, ps.next, indent) print('.. This file is autogenerated, using:') print(' ./gcc-with-python generate-tables-of-passes-rst.py test.c') print print("All of GCC's passes") print("===================") print print(''' This diagram shows the various GCC optimization passes, arranged vertically, showing child passes via indentation. The lifetime of the various properties that they maintain is shown, giving the pass that initially creates the data (if any), the pass that destroys it (if any), and each pass that requires a particular property (based on the PROP_* flags). .. image:: passes.svg :width: 550px :height: 3302px :scale: 50% ''') print(""" These tables contain the same information. The diagram and tables were autogenerated, using GCC %s""" % gcc.get_gcc_version().basever) print for rootname, reflabel, ps in zip(('The lowering passes', 'The "small IPA" passes', 'The "regular IPA" passes', 'Passes generating Link-Time Optimization data', 'The "all other passes" catch-all'), ('all_lowering_passes', 'all_small_ipa_passes', 'all_regular_ipa_passes', 'all_lto_gen_passes', 'all_passes'), gcc.Pass.get_roots()): print('.. _%s:' % reflabel) print print(rootname) print('-' * len(rootname)) print t = Table(['Pass Name', 'Required properties', 'Provided properties', 'Destroyed properties'], sepchar='=') foo(t, ps, 0) s = six.StringIO() t.write(s) for line in s.getvalue().splitlines(): print(' ' + line.rstrip()) print('\n') gcc-python-plugin-0.17/generate-tree-c.py000066400000000000000000001017201342215241600203320ustar00rootroot00000000000000# Copyright 2011-2013, 2015 David Malcolm # Copyright 2011-2013, 2015 Red Hat, Inc. # # This 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 # . from maketreetypes import iter_tree_types from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject from testcpychecker import get_gcc_version tree_types = list(iter_tree_types()) # FIXME: truncate the list, for ease of development: #tree_types = list(iter_tree_types())[:3] cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("tree.h") cu.add_include("function.h") cu.add_include("basic-block.h") cu.add_include("cp/cp-tree.h") cu.add_include("c-family/c-common.h") cu.add_include("gcc-c-api/gcc-tree.h") cu.add_include("gcc-c-api/gcc-constant.h") cu.add_include("gcc-c-api/gcc-declaration.h") cu.add_include("gcc-c-api/gcc-type.h") cu.add_include("cp/name-lookup.h") cu.add_include("autogenerated-casts.h") GCC_VERSION = get_gcc_version() if GCC_VERSION >= 4009: # GCC 4.9 moved debug_tree here: cu.add_include("print-tree.h") modinit_preinit = '' modinit_postinit = '' def generate_tree(): # # Generate the gcc.Tree class: # global modinit_preinit global modinit_postinit cu.add_defn(""" static PyObject * PyGccTree_get_type(struct PyGccTree *self, void *closure) { return PyGccTree_New(gcc_private_make_tree(TREE_TYPE(self->t.inner))); } static PyObject * PyGccTree_get_addr(struct PyGccTree *self, void *closure) { return PyLong_FromVoidPtr(self->t.inner); } """) getsettable = PyGetSetDefTable('PyGccTree_getset_table', [PyGetSetDef('type', 'PyGccTree_get_type', None, 'Instance of gcc.Tree giving the type of the node'), PyGetSetDef('addr', 'PyGccTree_get_addr', None, 'The address of the underlying GCC object in memory'), PyGetSetDef('str_no_uid', 'PyGccTree_get_str_no_uid', None, 'A string representation of this object, like str(), but without including any internal UID')], identifier_prefix='PyGccTree', typename='PyGccTree') cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccTree_TypeObj', localname = 'Tree', tp_name = 'gcc.Tree', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccTree', tp_new = 'PyType_GenericNew', tp_getset = 'PyGccTree_getset_table', tp_hash = '(hashfunc)PyGccTree_hash', tp_str = '(reprfunc)PyGccTree_str', tp_richcompare = 'PyGccTree_richcompare') methods = PyMethodTable('PyGccTree_methods', []) methods.add_method('debug', 'PyGccTree_debug', 'METH_VARARGS', "Dump the tree to stderr") cu.add_defn(""" PyObject* PyGccTree_debug(PyObject *self, PyObject *args) { PyGccTree *tree_obj; /* FIXME: type checking */ tree_obj = (PyGccTree *)self; debug_tree(tree_obj->t.inner); Py_RETURN_NONE; } """) cu.add_defn(methods.c_defn()) pytype.tp_methods = methods.identifier cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_tree() type_for_code_class = { 'tcc_exceptional' : 'PyGccTree_TypeObj', 'tcc_constant' : 'PyGccConstant_TypeObj', 'tcc_type' : 'PyGccType_TypeObj', 'tcc_declaration' : 'PyGccDeclaration_TypeObj', 'tcc_reference' : 'PyGccReference_TypeObj', 'tcc_comparison' : 'PyGccComparison_TypeObj', 'tcc_unary' : 'PyGccUnary_TypeObj', 'tcc_binary' : 'PyGccBinary_TypeObj', 'tcc_statement' : 'PyGccStatement_TypeObj', 'tcc_vl_exp' : 'PyGccVlExp_TypeObj', 'tcc_expression' : 'PyGccExpression_TypeObj', } def generate_intermediate_tree_classes(): # Generate a "middle layer" of gcc.Tree subclasses, corresponding to most of the # values of # enum_tree_code_class # from GCC's tree.h global modinit_preinit global modinit_postinit for code_type in type_for_code_class.values(): # We've already built the base class: if code_type == 'PyGccTree_TypeObj': continue # Strip off the "PyGcc" prefix and "_TypeObj" suffix: localname = code_type[5:-8] getsettable = PyGetSetDefTable('gcc_%s_getset_table' % localname, []) methods = PyMethodTable('gcc_%s_methods' % localname, []) pytype = PyGccWrapperTypeObject(identifier = code_type, localname = localname, tp_name = 'gcc.%s' % localname, struct_name = 'PyGccTree', tp_new = 'PyType_GenericNew', tp_base = '&PyGccTree_TypeObj', tp_getset = getsettable.identifier, tp_methods = methods.identifier) def add_simple_getter(name, c_expression, doc): getsettable.add_gsdef(name, cu.add_simple_getter('PyGcc%s_get_%s' % (localname, name), 'PyGccTree', c_expression), None, doc) if localname == 'Declaration': cu.add_defn(""" PyObject * PyGccDeclaration_get_name(struct PyGccTree *self, void *closure) { if (DECL_NAME(self->t.inner)) { return PyGccString_FromString(IDENTIFIER_POINTER (DECL_NAME (self->t.inner))); } Py_RETURN_NONE; } static PyObject * PyGccDeclaration_get_location(struct PyGccTree *self, void *closure) { return PyGccLocation_New(gcc_decl_get_location(PyGccTree_as_gcc_decl(self))); } """) getsettable.add_gsdef('name', 'PyGccDeclaration_get_name', None, 'The name of this declaration (string)') getsettable.add_gsdef('location', 'PyGccDeclaration_get_location', None, 'The gcc.Location for this declaration') add_simple_getter('is_artificial', 'PyBool_FromLong(gcc_decl_is_artificial(PyGccTree_as_gcc_decl(self)))', "Is this a compiler-generated entity?") add_simple_getter('is_builtin', 'PyBool_FromLong(gcc_decl_is_builtin(PyGccTree_as_gcc_decl(self)))', "Is this declaration built in by the compiler?") pytype.tp_repr = '(reprfunc)PyGccDeclaration_repr' if localname == 'Type': add_simple_getter('name', 'PyGccTree_New(gcc_type_get_name(PyGccTree_as_gcc_type(self)))', "The name of the type as a gcc.Tree, or None") add_simple_getter('pointer', 'PyGccPointerType_New(gcc_type_get_pointer(PyGccTree_as_gcc_type(self)))', "The gcc.PointerType representing '(this_type *)'") getsettable.add_gsdef('attributes', 'PyGccType_get_attributes', None, 'The user-defined attributes on this type') getsettable.add_gsdef('sizeof', 'PyGccType_get_sizeof', None, 'sizeof() this type, as a gcc.IntegerCst') def add_type(c_expr_for_node, typename): # Expose the given global type node within the gcc.Tree API # # The table is populated by tree.c:build_common_builtin_nodes # but unfortunately this seems to be called after our plugin is # initialized. # # Hence we add them as properties, so that they can be looked up on # demand, rather than trying to look them up once when the module # is set up cu.add_defn(""" PyObject* %s(PyObject *cls, PyObject *args) { return PyGccTree_New(gcc_private_make_tree(%s)); } """ % ('PyGccType_get_%s' % typename, c_expr_for_node)) if typename == 'size_t': desc = typename else: desc = typename.replace('_', ' ') methods.add_method('%s' % typename, 'PyGccType_get_%s' % typename, 'METH_CLASS|METH_NOARGS', "The builtin type '%s' as a gcc.Type (or None at startup before any compilation passes)" % desc) # Add the standard C integer types as properties. # # Tree nodes for the standard C integer types are defined in tree.h by # extern GTY(()) tree integer_types[itk_none]; # with macros to look into it of this form: # #define unsigned_type_node integer_types[itk_unsigned_int] # std_types = ['itk_char', 'itk_signed_char', 'itk_unsigned_char', 'itk_short', 'itk_unsigned_short', 'itk_int', 'itk_unsigned_int', 'itk_long', 'itk_unsigned_long', 'itk_long_long', 'itk_unsigned_long_long'] if GCC_VERSION < 5000: # int128 seems to have gone away in # 9f75f0266e3611513f196c898088e2712a71eaf4, discussed at # https://gcc.gnu.org/ml/gcc-patches/2014-08/msg01396.html std_types += ['itk_int128', 'itk_unsigned_int128'] for std_type in std_types: # strip off the "itk_" prefix assert std_type.startswith('itk_') stddef = std_type[4:] #add_simple_getter(stddef, # 'PyGccTree_New(gcc_private_make_tree(integer_types[%s]))' % std_type, # "The builtin type '%s' as a gcc.Type (or None at startup before any compilation passes)" % stddef.replace('_', ' ')) add_type('integer_types[%s]' % std_type, stddef) # Similarly, # extern GTY(()) tree global_trees[TI_MAX]; # holds various nodes, including many with a _TYPE suffix. # Here are some of them: for ti in ('TI_UINT32_TYPE', 'TI_UINT64_TYPE', 'TI_FLOAT_TYPE', 'TI_DOUBLE_TYPE', 'TI_LONG_DOUBLE_TYPE', 'TI_VOID_TYPE', 'TI_SIZE_TYPE'): # strip off the "TI_" prefix and "_TYPE" suffix: assert ti.startswith('TI_') assert ti.endswith('_TYPE') if ti == 'TI_SIZE_TYPE': name = 'size_t' else: name = ti[3:-5].lower() add_type('global_trees[%s]' % ti, name) if localname == 'Unary': add_simple_getter('operand', 'PyGccTree_New(gcc_unary_get_operand(PyGccTree_as_gcc_unary(self)))', 'The operand of this expression, as a gcc.Tree') # Corresponds to this gcc/tree.h macro: # #define IS_EXPR_CODE_CLASS(CLASS)\ # ((CLASS) >= tcc_reference && (CLASS) <= tcc_expression) if localname in ('Reference', 'Comparison', 'Unary', 'Binary', 'Statement' 'VlExp', 'Expression'): add_simple_getter('location', ('PyGccLocation_New(gcc_%s_get_location(PyGccTree_as_gcc_%s(self)))' % (localname.lower(), localname.lower())), "The source location of this expression") methods.add_method('get_symbol', 'PyGccTree_get_symbol', # they all share the implementation 'METH_CLASS|METH_NOARGS', "FIXME") cu.add_defn(methods.c_defn()) cu.add_defn(getsettable.c_defn()) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_intermediate_tree_classes() def generate_tree_code_classes(): # Generate all of the concrete gcc.Tree subclasses based on the: # enum tree_code # as subclasses of the above layer: global modinit_preinit global modinit_postinit for tree_type in tree_types: base_type = type_for_code_class[tree_type.TYPE] cc = tree_type.camel_cased_string() getsettable = PyGetSetDefTable('gcc_%s_getset_table' % cc, [], identifier_prefix='gcc_%s' % cc, typename='PyGccTree') tp_as_number = None tp_repr = None tp_str = None methods = PyMethodTable('gcc_%s_methods' % cc, []) def get_getter_identifier(name): return 'PyGcc%s_get_%s' % (cc, name) def add_simple_getter(name, c_expression, doc): getsettable.add_gsdef(name, cu.add_simple_getter(get_getter_identifier(name), 'PyGccTree', c_expression), None, doc) def add_complex_getter(name, doc): getsettable.add_gsdef(name, get_getter_identifier(name), None, doc) if cc == 'AddrExpr': add_simple_getter('operand', 'PyGccTree_New(gcc_addr_expr_get_operand(PyGccTree_as_gcc_addr_expr(self)))', 'The operand of this expression, as a gcc.Tree') if cc == 'StringCst': add_simple_getter('constant', 'PyGccString_FromString(gcc_string_constant_get_char_ptr(PyGccTree_as_gcc_string_constant(self)))', 'The actual value of this constant, as a str') tp_repr = '(reprfunc)PyGccStringConstant_repr' if cc == 'IntegerCst': getsettable.add_gsdef('constant', 'PyGccIntegerConstant_get_constant', None, 'The actual value of this constant, as an int/long') number_methods = PyNumberMethods('PyGccIntegerConstant_number_methods') tp_as_number = number_methods.identifier number_methods.nb_int = 'PyGccIntegerConstant_get_constant' cu.add_defn(number_methods.c_defn()) tp_repr = '(reprfunc)PyGccIntegerConstant_repr' if cc == 'RealCst': getsettable.add_gsdef('constant', 'PyGccRealCst_get_constant', None, 'The actual value of this constant, as a float') tp_repr = '(reprfunc)PyGccRealCst_repr' # TYPE_QUALS for various foo_TYPE classes: if tree_type.SYM in ('VOID_TYPE', 'INTEGER_TYPE', 'REAL_TYPE', 'FIXED_POINT_TYPE', 'COMPLEX_TYPE', 'VECTOR_TYPE', 'ENUMERAL_TYPE', 'BOOLEAN_TYPE'): for qual in ('const', 'volatile', 'restrict'): add_simple_getter(qual, 'PyBool_FromLong(TYPE_QUALS(self->t.inner) & TYPE_QUAL_%s)' % qual.upper(), "Boolean: does this type have the '%s' modifier?" % qual) add_simple_getter('%s_equivalent' % qual, 'PyGccTree_New(gcc_private_make_tree(build_qualified_type(self->t.inner, TYPE_QUALS(self->t.inner) | TYPE_QUAL_%s)))' % qual.upper(), 'The gcc.Type for the %s version of this type' % qual) add_simple_getter('unqualified_equivalent', 'PyGccTree_New(gcc_private_make_tree(build_qualified_type(self->t.inner, 0)))', 'The gcc.Type for the unqualified version of this type') if tree_type.SYM == 'RECORD_TYPE': add_simple_getter('const', 'PyBool_FromLong(TYPE_READONLY(self->t.inner))', "Boolean: does this type have the 'const' modifier?") if tree_type.SYM == 'INTEGER_TYPE': add_simple_getter('unsigned', 'PyBool_FromLong(gcc_integer_type_is_unsigned(PyGccTree_as_gcc_integer_type(self)))', "Boolean: True for 'unsigned', False for 'signed'") add_complex_getter('signed_equivalent', 'The gcc.IntegerType for the signed version of this type') add_complex_getter('unsigned_equivalent', 'The gcc.IntegerType for the unsigned version of this type') add_simple_getter('max_value', 'PyGccTree_New(gcc_integer_constant_as_gcc_tree(gcc_integer_type_get_max_value(PyGccTree_as_gcc_integer_type(self))))', 'The maximum possible value for this type, as a gcc.IntegerCst') add_simple_getter('min_value', 'PyGccTree_New(gcc_integer_constant_as_gcc_tree(gcc_integer_type_get_min_value(PyGccTree_as_gcc_integer_type(self))))', 'The minimum possible value for this type, as a gcc.IntegerCst') tp_repr = '(reprfunc)PyGccIntegerType_repr' if tree_type.SYM in ('INTEGER_TYPE', 'REAL_TYPE', 'FIXED_POINT_TYPE'): prefix = 'gcc_%s' % tree_type.SYM.lower() add_simple_getter('precision', 'PyGccInt_FromLong(%s_get_precision(PyGccTree_as_%s(self)))' % (prefix, prefix), 'The precision of this type in bits, as an int (e.g. 32)') if tree_type.SYM in ('POINTER_TYPE', 'ARRAY_TYPE', 'VECTOR_TYPE'): prefix = 'gcc_%s' % tree_type.SYM.lower() add_simple_getter('dereference', ('PyGccTree_New(gcc_type_as_gcc_tree(%s_get_dereference(PyGccTree_as_%s(self))))' % (prefix, prefix)), "The gcc.Type that this type points to'") if tree_type.SYM == 'POINTER_TYPE': tp_repr = '(reprfunc)PyGccPointerType_repr' if tree_type.SYM == 'ARRAY_TYPE': add_simple_getter('range', 'PyGccTree_New(gcc_private_make_tree(TYPE_DOMAIN(self->t.inner)))', "The gcc.Type that is the range of this array type") if tree_type.SYM == 'ARRAY_REF': add_simple_getter('array', 'PyGccTree_New(gcc_array_ref_get_array(PyGccTree_as_gcc_array_ref(self)))', "The gcc.Tree for the array being referenced'") add_simple_getter('index', 'PyGccTree_New(gcc_array_ref_get_index(PyGccTree_as_gcc_array_ref(self)))', "The gcc.Tree for index being referenced'") tp_repr = '(reprfunc)PyGccArrayRef_repr' if tree_type.SYM == 'COMPONENT_REF': add_simple_getter('target', 'PyGccTree_New(gcc_component_ref_get_target(PyGccTree_as_gcc_component_ref(self)))', "The gcc.Tree that for the container of the field'") add_simple_getter('field', 'PyGccTree_New(gcc_component_ref_get_field(PyGccTree_as_gcc_component_ref(self)))', "The gcc.FieldDecl for the field within the target'") tp_repr = '(reprfunc)PyGccComponentRef_repr' if tree_type.SYM == 'MEM_REF': add_simple_getter('operand', 'PyGccTree_New(gcc_mem_ref_get_operand(PyGccTree_as_gcc_mem_ref(self)))', "The gcc.Tree that for the pointer expression'") if tree_type.SYM == 'BIT_FIELD_REF': add_simple_getter('operand', 'PyGccTree_New(gcc_private_make_tree(TREE_OPERAND(self->t.inner, 0)))', "The gcc.Tree for the structure or union expression") add_simple_getter('num_bits', 'PyGccTree_New(gcc_private_make_tree(TREE_OPERAND(self->t.inner, 1)))', "The number of bits being referenced, as a gcc.IntegerCst") add_simple_getter('position', 'PyGccTree_New(gcc_private_make_tree(TREE_OPERAND(self->t.inner, 2)))', "The position of the first referenced bit, as a gcc.IntegerCst") if tree_type.SYM in ('RECORD_TYPE', 'UNION_TYPE', 'QUAL_UNION_TYPE'): add_simple_getter('fields', 'PyGcc_GetFields(self)', "The fields of this type") add_simple_getter('methods', 'PyGcc_GetMethods(self)', "The methods of this type") if tree_type.SYM == 'ENUMERAL_TYPE': add_simple_getter('values', 'PyGcc_TreeMakeListOfPairsFromTreeListChain(TYPE_VALUES(self->t.inner))', "The values of this type") if tree_type.SYM == 'IDENTIFIER_NODE': add_simple_getter('name', 'PyGccStringOrNone(IDENTIFIER_POINTER(self->t.inner))', "The name of this gcc.IdentifierNode, as a string") tp_repr = '(reprfunc)PyGccIdentifierNode_repr' if tree_type.SYM == 'VAR_DECL': add_simple_getter('initial', 'PyGccTree_New(gcc_constructor_as_gcc_tree(gcc_var_decl_get_initial(PyGccTree_as_gcc_var_decl(self))))', "The initial value for this variable as a gcc.Constructor, or None") add_simple_getter('static', 'PyBool_FromLong(gcc_var_decl_is_static(PyGccTree_as_gcc_var_decl(self)))', "Boolean: is this variable to be allocated with static storage") if tree_type.SYM == 'CONSTRUCTOR': add_complex_getter('elements', "The elements of this constructor, as a list of (index, gcc.Tree) pairs") if tree_type.SYM == 'TRANSLATION_UNIT_DECL': add_simple_getter('block', 'PyGccBlock_New(gcc_translation_unit_decl_get_block(PyGccTree_as_gcc_translation_unit_decl(self)))', "The gcc.Block for this namespace") add_simple_getter('language', 'PyGccString_FromString(gcc_translation_unit_decl_get_language(PyGccTree_as_gcc_translation_unit_decl(self)))', "The source language of this translation unit, as a string") if tree_type.SYM == 'BLOCK': add_simple_getter('vars', 'PyGcc_TreeListFromChain(BLOCK_VARS(self->t.inner))', "The list of gcc.Tree for the declarations and labels in this block") if tree_type.SYM == 'NAMESPACE_DECL': add_simple_getter('alias_of', 'PyGccTree_New(gcc_private_make_tree(DECL_NAMESPACE_ALIAS(self->t.inner)))', "None if not an alias, otherwise the gcc.NamespaceDecl we alias") add_simple_getter('declarations', 'PyGccNamespaceDecl_declarations(self->t.inner)', 'The list of gcc.Declarations within this namespace') add_simple_getter('namespaces', 'PyGccNamespaceDecl_namespaces(self->t.inner)', 'The list of gcc.NamespaceDecl objects and gcc.TypeDecl of Unions nested in this namespace') methods.add_method('lookup', '(PyCFunction)PyGccNamespaceDecl_lookup', 'METH_VARARGS|METH_KEYWORDS', "Look up the given string within this namespace") methods.add_method('unalias', '(PyCFunction)PyGccNamespaceDecl_unalias', 'METH_VARARGS|METH_KEYWORDS', "A gcc.NamespaceDecl of this namespace that is not an alias") if tree_type.SYM == 'TYPE_DECL': getsettable.add_gsdef('pointer', 'PyGccTypeDecl_get_pointer', None, "The gcc.PointerType representing '(this_type *)'") if tree_type.SYM == 'FUNCTION_TYPE': getsettable.add_gsdef('argument_types', 'PyGccFunction_TypeObj_get_argument_types', None, "A tuple of gcc.Type instances, representing the argument types of this function type") getsettable.add_gsdef('is_variadic', 'PyGccFunction_TypeObj_is_variadic', None, "Boolean: is this function variadic") if tree_type.SYM == 'METHOD_TYPE': getsettable.add_gsdef('argument_types', 'PyGccMethodType_get_argument_types', None, "A tuple of gcc.Type instances, representing the argument types of this method type") getsettable.add_gsdef('is_variadic', 'PyGccMethodType_is_variadic', None, "Boolean: is this method variadic") if tree_type.SYM == 'FUNCTION_DECL': getsettable.add_gsdef('fullname', 'PyGccFunctionDecl_get_fullname', None, 'C++ only: the full name of this function declaration') add_simple_getter('function', 'PyGccFunction_New(gcc_private_make_function(DECL_STRUCT_FUNCTION(self->t.inner)))', 'The gcc.Function (or None) for this declaration') add_simple_getter('arguments', 'PyGcc_TreeListFromChain(DECL_ARGUMENTS(self->t.inner))', 'List of gcc.ParmDecl') add_simple_getter('result', 'PyGccTree_New(gcc_private_make_tree(DECL_RESULT_FLD(self->t.inner)))', 'The gcc.ResultDecl for the return value') getsettable.add_gsdef('callgraph_node', 'PyGccFunctionDecl_get_callgraph_node', None, 'The gcc.CallgraphNode for this function declaration, or None') for attr in ('public', 'private', 'protected', 'static'): getsettable.add_simple_getter(cu, 'is_%s' % attr, 'PyBool_FromLong(TREE_%s(self->t.inner))' % attr.upper(), None) if tree_type.SYM == 'SSA_NAME': # c.f. "struct GTY(()) tree_ssa_name": add_simple_getter('var', 'PyGccTree_New(gcc_ssa_name_get_var(PyGccTree_as_gcc_ssa_name(self)))', "The variable being referenced'") add_simple_getter('def_stmt', 'PyGccGimple_New(gcc_ssa_name_get_def_stmt(PyGccTree_as_gcc_ssa_name(self)))', "The gcc.Gimple statement which defines this SSA name'") add_simple_getter('version', 'PyGccInt_FromLong(gcc_ssa_name_get_version(PyGccTree_as_gcc_ssa_name(self)))', "The SSA version number of this SSA name'") tp_repr = '(reprfunc)PyGccSsaName_repr' if tree_type.SYM == 'TREE_LIST': # c.f. "struct GTY(()) tree_list": tp_repr = '(reprfunc)PyGccTreeList_repr' if tree_type.SYM == 'CASE_LABEL_EXPR': add_simple_getter('low', 'PyGccTree_New(gcc_case_label_expr_get_low(PyGccTree_as_gcc_case_label_expr(self)))', "The low value of the case label, as a gcc.Tree (or None for the default)") add_simple_getter('high', 'PyGccTree_New(gcc_case_label_expr_get_high(PyGccTree_as_gcc_case_label_expr(self)))', "The high value of the case label, if any, as a gcc.Tree (None for the default and for single-valued case labels)") add_simple_getter('target', 'PyGccTree_New(gcc_label_decl_as_gcc_tree(gcc_case_label_expr_get_target(PyGccTree_as_gcc_case_label_expr(self))))', "The target of the case label, as a gcc.LabelDecl") tp_repr = '(reprfunc)PyGccCaseLabelExpr_repr' cu.add_defn(getsettable.c_defn()) cu.add_defn(methods.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGcc%s_TypeObj' % cc, localname = cc, tp_name = 'gcc.%s' % cc, struct_name = 'PyGccTree', tp_new = 'PyType_GenericNew', tp_base = '&%s' % base_type, tp_getset = getsettable.identifier, tp_str = tp_str, tp_repr = tp_repr, tp_methods = methods.identifier, ) if tp_as_number: pytype.tp_as_number = '&%s' % tp_as_number cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() cu.add_defn('\n/* Map from GCC tree codes to PyGccWrapperTypeObject* */\n') cu.add_defn('PyGccWrapperTypeObject *pytype_for_tree_code[] = {\n') for tree_type in tree_types: cu.add_defn(' &PyGcc%s_TypeObj, /* %s */\n' % (tree_type.camel_cased_string(), tree_type.SYM)) cu.add_defn('};\n\n') cu.add_defn('\n/* Map from PyGccWrapperTypeObject* to GCC tree codes*/\n') cu.add_defn('int \n') cu.add_defn('PyGcc_tree_type_object_as_tree_code(PyObject *cls, enum tree_code *out)\n') cu.add_defn('{\n') for tree_type in tree_types: cu.add_defn(' if (cls == (PyObject*)&PyGcc%s_TypeObj) {\n' ' *out = %s; return 0;\n' ' }\n' % (tree_type.camel_cased_string(), tree_type.SYM)) cu.add_defn(' return -1;\n') cu.add_defn('}\n') cu.add_defn(""" PyGccWrapperTypeObject* PyGcc_autogenerated_tree_type_for_tree_code(enum tree_code code, int borrow_ref) { PyGccWrapperTypeObject *result; assert(code >= 0); assert(code < MAX_TREE_CODES); result = pytype_for_tree_code[code]; if (!borrow_ref) { Py_INCREF(result); } return result; } PyGccWrapperTypeObject* PyGcc_autogenerated_tree_type_for_tree(gcc_tree t, int borrow_ref) { enum tree_code code = TREE_CODE(t.inner); /* printf("code:%i\\n", code); */ return PyGcc_autogenerated_tree_type_for_tree_code(code, borrow_ref); } """) generate_tree_code_classes() cu.add_defn(""" int autogenerated_tree_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_tree_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/generate-variable-c.py000066400000000000000000000053531342215241600211650ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . from cpybuilder import * from wrapperbuilder import PyGccWrapperTypeObject cu = CompilationUnit() cu.add_include('gcc-python.h') cu.add_include('gcc-python-wrappers.h') cu.add_include('gcc-plugin.h') cu.add_include("gcc-c-api/gcc-variable.h") modinit_preinit = '' modinit_postinit = '' def generate_variable(): global modinit_preinit global modinit_postinit getsettable = PyGetSetDefTable('PyGccVariable_getset_table', []) def add_simple_getter(name, c_expression, doc): getsettable.add_gsdef(name, cu.add_simple_getter('PyGccVariable_get_%s' % name, 'PyGccVariable', c_expression), None, doc) add_simple_getter('decl', 'PyGccTree_New(gcc_variable_get_decl(self->var))', 'The declaration of this variable, as a gcc.Tree') cu.add_defn(getsettable.c_defn()) pytype = PyGccWrapperTypeObject(identifier = 'PyGccVariable_TypeObj', localname = 'Variable', tp_name = 'gcc.Variable', tp_dealloc = 'PyGccWrapper_Dealloc', struct_name = 'PyGccVariable', tp_new = 'PyType_GenericNew', tp_getset = getsettable.identifier, #tp_repr = '(reprfunc)PyGccVariable_repr', #tp_str = '(reprfunc)PyGccVariable_repr', ) cu.add_defn(pytype.c_defn()) modinit_preinit += pytype.c_invoke_type_ready() modinit_postinit += pytype.c_invoke_add_to_module() generate_variable() cu.add_defn(""" int autogenerated_variable_init_types(void) { """ + modinit_preinit + """ return 1; error: return 0; } """) cu.add_defn(""" void autogenerated_variable_add_types(PyObject *m) { """ + modinit_postinit + """ } """) print(cu.as_str()) gcc-python-plugin-0.17/gimple-types.txt.in000066400000000000000000000005161342215241600205770ustar00rootroot00000000000000#define DEFGSCODE(GIMPLE_symbol, printable_name, GSS_symbol) \ GIMPLE_symbol, printable_name, GSS_symbol #include "gimple.def" #undef DEFGSCODE #define DEFGSSTRUCT(GSS_enumeration_value, structure_name, has_tree_operands) \ GSS_enumeration_value, structure_name, has_tree_operands #include "gsstruct.def" #undef DEFGSSTRUCT gcc-python-plugin-0.17/libcpychecker/000077500000000000000000000000001342215241600176175ustar00rootroot00000000000000gcc-python-plugin-0.17/libcpychecker/PyArg_ParseTuple.py000066400000000000000000000345501342215241600233660ustar00rootroot00000000000000# Copyright 2011, 2013 David Malcolm # Copyright 2011, 2013 Red Hat, Inc. # # This 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 # . # Domain-specific warning: # Detecting errors in usage of the PyArg_ParseTuple API # # See http://docs.python.org/c-api/arg.html # # Note that all of the "#" codes are affected by the presence of the # macro PY_SSIZE_T_CLEAN. If the macro was defined before including Python.h, # the various lengths for these format codes are of C type "Py_ssize_t" rather # than "int". # # This behavior was clarified in the Python 3 version of the C API # documentation[1], though the Python 2 version of the API docs leave which # codes are affected somewhat ambiguoues. # # Nevertheless, the API _does_ work this way in Python 2: all format codes # with a "#" do work this way. # # You can see the implementation of the API in CPython's Python/getargs.c # # [1] The relevant commit to the CPython docs was: # http://hg.python.org/cpython/rev/5d4a5655575f/ import gcc from libcpychecker.formatstrings import * from libcpychecker.types import * from libcpychecker.utils import log def _type_of_simple_arg(arg): # Convert 1-character argument code to a gcc.Type, covering the easy cases # # Analogous to Python/getargs.c:convertsimple, this is the same order as # that function's "switch" statement: simple = {'b': gcc.Type.unsigned_char, 'B': gcc.Type.unsigned_char, 'h': gcc.Type.short, 'H': gcc.Type.unsigned_short, 'i': gcc.Type.int, 'I': gcc.Type.unsigned_int, 'l': gcc.Type.long, 'k': gcc.Type.unsigned_long, 'f': gcc.Type.float, 'd': gcc.Type.double, 'c': gcc.Type.char, } if arg in simple: # FIXME: ideally this shouldn't need calling; it should just be an # attribute: return simple[arg]() if arg == 'n': return get_Py_ssize_t() if arg == 'L': return get_PY_LONG_LONG() if arg == 'K': return get_PY_LONG_LONG().unsigned_equivalent if arg == 'D': return get_Py_complex() class TypeCheck(FormatUnit): """ Handler for the "O!" format code """ def __init__(self, code): FormatUnit.__init__(self, code) self.checker = TypeCheckCheckerType(self) self.result = TypeCheckResultType(self) # The gcc.VarDecl for the PyTypeObject, if we know it (or None): self.typeobject = None def get_expected_types(self): # We will discover the actual types as we go, using "self" to bind # together the two arguments return [self.checker, self.result] def get_other_type(self): if not self.typeobject: return None return get_type_for_typeobject(self.typeobject) class TypeCheckCheckerType(AwkwardType): def __init__(self, typecheck): self.typecheck = typecheck def is_compatible(self, actual_type, actual_arg): # We should be encountering a pointer to a PyTypeObject # The result type (next argument) should be either a PyObject* or # an pointer to the corresponding type. # # For example, "O!" with # &PyCode_Type, &code, # then "code" should be either a PyObject* or a PyCodeObject* # (and &code gets the usual extra level of indirection) if str(actual_type) != 'struct PyTypeObject *': return False # OK, the type for this argument is good. # Try to record the actual type object, assuming it's a pointer to a # global: if not isinstance(actual_arg, gcc.AddrExpr): return True if not isinstance(actual_arg.operand, gcc.VarDecl): return True # OK, we have a ptr to a global; record it: self.typecheck.typeobject = actual_arg.operand return True def describe(self): return '"struct PyTypeObject *"' class TypeCheckResultType(AwkwardType): def __init__(self, typecheck): self.typecheck = typecheck self.base_type = get_PyObject().pointer.pointer def is_compatible(self, actual_type, actual_arg): if not isinstance(actual_type, gcc.PointerType): return False # If something went wrong with figuring out the type, we can only check # against PyObject*: # (PyObject **) is good: if compatible_type(actual_type, self.base_type): return True other_type = self.typecheck.get_other_type() if other_type: if compatible_type(actual_type, other_type.pointer.pointer): return True return False def describe(self): other_type = self.typecheck.get_other_type() if other_type: return ('%s (based on PyTypeObject: %r) or %s' % (describe_type(other_type.pointer.pointer), self.typecheck.typeobject.name, describe_type(self.base_type))) else: if self.typecheck.typeobject: return ('"%s" (unfamiliar with PyTypeObject: %r)' % (describe_type(self.base_type), self.typecheck.typeobject.name)) else: return '"%s" (unable to determine relevant PyTypeObject)' % describe_type(self.base_type) class Conversion(FormatUnit): """ Handler for the "O&" format code """ def __init__(self, code): FormatUnit.__init__(self, code) self.callback = ConverterCallbackType(self) self.result = ConverterResultType(self) def get_expected_types(self): # We will discover the actual types as we go, using "self" to bind # together the two arguments return [self.callback, self.result] class ConverterCallbackType(AwkwardType): def __init__(self, conv): self.conv = conv self.actual_type = None def is_compatible(self, actual_type, actual_arg): # We should be encountering a function pointer of type: # int (fn)(PyObject *, T*) # The result type (next argument) should be a T* if not isinstance(actual_type, gcc.PointerType): return False signature = actual_type.dereference if not isinstance(signature, gcc.FunctionType): return False # Check return type: if signature.type != gcc.Type.int(): return False # Check argument types: if len(signature.argument_types) != 2: return False if not compatible_type(signature.argument_types[0], get_PyObject().pointer): return False if not isinstance(signature.argument_types[1], gcc.PointerType): return False # Write back to the ConverterResultType with the second arg: log('2nd argument of converter should be of type %s', signature.argument_types[1]) self.conv.result.type = signature.argument_types[1] self.actual_type = actual_type return True def describe(self): return '"int (converter)(PyObject *, T*)" for some type T' class ConverterResultType(AwkwardType): def __init__(self, conv): self.conv = conv self.type = None def is_compatible(self, actual_type, actual_arg): if not isinstance(actual_type, gcc.PointerType): return False # If something went wrong with figuring out the type, we can't check # it: if self.type is None: return True return compatible_type(self.type, actual_type) def describe(self): if self.type: return ('%s (from second argument of %s)' % (describe_type(self.type), describe_type(self.conv.callback.actual_type))) else: return '"T*" for some type T' class PyArgParseFmt(ParsedFormatString): """ Python class representing the string arg to PyArg_ParseTuple and friends """ def add_argument(self, code, expected_types): self.args.append(ConcreteUnit(code, expected_types)) def num_expected(self): return len(list(self.iter_exp_types())) def iter_exp_types(self): """ Yield a sequence of (FormatUnit, gcc.Type) pairs, representing the expected types of the varargs """ for arg in self.args: for exp_type in arg.get_expected_types(): yield (arg, exp_type) @classmethod def from_string(cls, fmt_string, with_size_t): """ Parse fmt_string, generating a PyArgParseFmt instance Compare to Python/getargs.c:vgetargs1 FIXME: only implements a subset of the various cases; no tuples yet etc """ result = PyArgParseFmt(fmt_string) i = 0 paren_nesting = 0 while i < len(fmt_string): c = fmt_string[i] i += 1 if i < len(fmt_string): next = fmt_string[i] else: next = None if c == '(': paren_nesting += 1 continue if c == ')': if paren_nesting > 0: paren_nesting -= 1 continue else: raise MismatchedParentheses(fmt_string) if c in [':', ';']: break if c =='|': continue simple_type = _type_of_simple_arg(c) if simple_type: result.add_argument(c, [simple_type.pointer]) elif c in ['s', 'z']: # string, possibly NULL/None if next == '#': result.add_argument(c + '#', [get_const_char_ptr_ptr(), get_hash_size_type(with_size_t).pointer]) i += 1 elif next == '*': result.add_argument(c + '*', [get_Py_buffer().pointer]) i += 1 else: result.add_argument(c, [get_const_char_ptr_ptr()]) elif c == 'e': if next in ['s', 't']: arg = ConcreteUnit('e' + next, [(get_const_char_ptr(), get_char_ptr(), NullPointer()), gcc.Type.char().pointer.pointer]) i += 1 if i < len(fmt_string): if fmt_string[i] == '#': arg.code += '#' # es# and et# within getargs.c use FETCH_SIZE and # STORE_SIZE and are thus affected by the size # macro: arg.expected_types.append(get_hash_size_type(with_size_t).pointer) i+=1 result.args.append(arg) elif c == 'u': if next == '#': result.add_argument('u#', [Py_UNICODE().pointer.pointer, get_hash_size_type(with_size_t).pointer]) i += 1 else: result.add_argument('u', [Py_UNICODE().pointer.pointer]) elif c == 'S': if is_py3k(): # S (bytes) [PyBytesObject *] (or PyObject *) result.add_argument('S', [(get_PyBytesObject().pointer.pointer, get_PyObject().pointer.pointer)]) else: # S (string) [PyStringObject *] (or PyObject *) result.add_argument('S', [(get_PyStringObject().pointer.pointer, get_PyObject().pointer.pointer)]) elif c == 'U': result.add_argument('U', [(get_PyUnicodeObject().pointer.pointer, get_PyObject().pointer.pointer)]) elif c == 'O': # object if next == '!': result.args.append(TypeCheck('O!')) i += 1 elif next == '?': raise UnhandledCode(richloc, fmt_string, c + next) # FIXME elif next == '&': result.args.append(Conversion('O&')) i += 1 else: result.add_argument('O', [get_PyObject().pointer.pointer]) elif c == 'w': if next == '#': result.add_argument('w#', [gcc.Type.char().pointer.pointer, get_hash_size_type(with_size_t).pointer]) # Note: reading CPython sources indicates it's a FETCH_SIZE # type, not an Py_ssize_t, as the docs current suggest i += 1 elif next == '*': result.add_argument('w*', [get_Py_buffer().pointer]) i += 1 else: result.add_argument('w', [gcc.Type.char().pointer.pointer]) elif c == 't': if next == '#': result.add_argument('t#', [gcc.Type.char().pointer.pointer, get_hash_size_type(with_size_t).pointer]) # Note: reading CPython sources indicates it's a FETCH_SIZE # type, not an int, as the docs current suggest i += 1 else: raise UnknownFormatChar(fmt_string, c) if paren_nesting > 0: raise MismatchedParentheses(fmt_string) return result gcc-python-plugin-0.17/libcpychecker/Py_BuildValue.py000066400000000000000000000223631342215241600227030ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Domain-specific warning: # Detecting errors in usage of the Py_BuildValue API # # See http://docs.python.org/c-api/arg.html#Py_BuildValue # # FIXME: # Note that all of the "#" codes are affected by the presence of the # macro PY_SSIZE_T_CLEAN. If the macro was defined before including Python.h, # the various lengths for these format codes are of C type "Py_ssize_t" rather # than "int". # # This behavior was clarified in the Python 3 version of the C API # documentation[1], though the Python 2 version of the API docs leave which # codes are affected somewhat ambiguoues. # # Nevertheless, the API _does_ work this way in Python 2: all format codes # with a "#" do work this way. # # You can see the implementation of the API in CPython's Python/getargs.c # # [1] The relevant commit to the CPython docs was: # http://hg.python.org/cpython/rev/5d4a5655575f/ import gcc import sys from libcpychecker.formatstrings import * from libcpychecker.types import * from libcpychecker.utils import log def _type_of_simple_arg(arg): # Convert 1-character argument code to a gcc.Type, covering the easy cases # # Analogous to Python/modsupport.c:do_mkvalue, this is the same order as # that function's "switch" statement: simple = { # all of these actually just use "int": 'b': gcc.Type.char, 'B': gcc.Type.unsigned_char, 'h': gcc.Type.short, 'i': gcc.Type.int, 'H': gcc.Type.unsigned_short, 'I': gcc.Type.unsigned_int, # 'n' covered below 'l': gcc.Type.long, 'k': gcc.Type.unsigned_long, # 'L' covered below # 'K' covered below # 'u': covered in from_string() below 'f': gcc.Type.double, # (although documented as "[float]", 'f' accepts a "va_double" in # modsupport.c) 'd': gcc.Type.double, # 'D' covered below 'c': gcc.Type.int, # (although documented as "[char]", 'c' accepts an "int" in # modsupport.c) # 's': covered in from_string() below # 'z': covered in from_string() below # 'N': covered in from_string() below # 'S': covered in from_string() below # 'O': covered in from_string() below # ':': covered in from_string() below # ',': covered in from_string() below # ' ': covered in from_string() below # '\t': covered in from_string() below } if arg in simple: # FIXME: ideally this shouldn't need calling; it should just be an # attribute: return simple[arg]() if arg == 'n': return get_Py_ssize_t() elif arg == 'L': return get_PY_LONG_LONG() elif arg == 'K': return get_PY_LONG_LONG().unsigned_equivalent elif arg == 'D': return get_Py_complex().pointer class AnyPyObjectPtr(AwkwardType): """ For use when we expect PyObject*, or any subclass """ def is_compatible(self, actual_type, actual_arg): # We expect a pointer to a PyObject*, or any subclass: from libcpychecker.refcounts import type_is_pyobjptr_subclass return type_is_pyobjptr_subclass(actual_type) class ObjectFormatUnit(FormatUnit): """ Base class for Py_BuildValue format codes that expect a PyObject* """ def get_expected_types(self): return [AnyPyObjectPtr()] class CodeSO(ObjectFormatUnit): """ Corresponds to Py_BuildValue format codes "S" and "O" """ pass class CodeN(ObjectFormatUnit): """ Corresponds to Py_BuildValue format code "N" """ pass class CompoundFmt: def __init__(self, opench, closech): self.opench = opench self.closech = closech self.args = [] def __repr__(self): return 'CompoundFmt(%r, %r, %r)' % (self.opench, self.args, self.closech) def append(self, item): self.args.append(item) def iter_exp_types(self): for arg in self.args: if isinstance(arg, CompoundFmt): for inner in arg.iter_exp_types(): yield inner else: for exp_type in arg.get_expected_types(): yield (arg, exp_type) class PyBuildValueFmt(ParsedFormatString): def __init__(self, fmt_string): ParsedFormatString.__init__(self, fmt_string) self.arg_stack = [self.args] def num_expected(self): from pprint import pformat #sys.stderr.write('%s\n' % pformat(list(self.iter_exp_types()))) return len(list(self.iter_exp_types())) def iter_exp_types(self): """ Yield a sequence of (FormatUnit, gcc.Type) pairs, representing the expected types of the varargs """ for arg in self.args: if isinstance(arg, CompoundFmt): for inner in arg.iter_exp_types(): yield inner else: for exp_type in arg.get_expected_types(): yield (arg, exp_type) def add_argument(self, code, expected_types): self.arg_stack[-1].append(ConcreteUnit(code, expected_types)) def add_complex_argument(self, arg): self.arg_stack[-1].append(arg) def _do_open_compound_fmt(self, opench, closech): """ Analogous to Python/modsupport.c:do_mktuple """ # Store tuples using lists, so that they are mutable during # construction: new = CompoundFmt(opench, closech) self.arg_stack[-1].append(new) self.arg_stack.append(new) def _do_close_compound_fmt(self, closech): if len(self.arg_stack) < 1: raise MismatchedParentheses(self.fmt_string) cf = self.arg_stack.pop() if cf.closech != closech: raise MismatchedParentheses(self.fmt_string) @classmethod def from_string(cls, fmt_string, with_size_t): """ Parse fmt_string, generating a PyBuildValue instance Compare to Python/modsupport.c:va_build_value FIXME: only implements a subset of the various cases """ result = PyBuildValueFmt(fmt_string) i = 0 while i < len(fmt_string): c = fmt_string[i] i += 1 if i < len(fmt_string): next = fmt_string[i] else: next = None #sys.stderr.write('(%r, %r)\n' % (c, None)) # Analogous to Python/modsupport.c:do_mkvalue, this is in the same # order as that function's "switch" statement: simple_type = _type_of_simple_arg(c) if simple_type: result.add_argument(c, [simple_type]) continue if c == '(': result._do_open_compound_fmt('(', ')') continue if c == '[': result._do_open_compound_fmt('[', ']') continue if c == '{': result._do_open_compound_fmt('{', '}') continue if c in ')]}': result._do_close_compound_fmt(c) continue if c in 'sz': if next == '#': result.add_argument(c + '#', [get_char_ptr(), get_hash_size_type(with_size_t)]) i += 1 else: result.add_argument(c, [get_char_ptr()]) continue if c == 'u': if next == '#': result.add_argument(c + '#', [Py_UNICODE().pointer, get_hash_size_type(with_size_t)]) i += 1 else: result.add_argument(c, [Py_UNICODE().pointer]) continue if c in 'NSO': if next == '&': result.add_complex_argument() i += 1 else: if c == 'N': result.add_complex_argument(CodeN(c)) else: result.add_complex_argument(CodeSO(c)) continue if c in ":, \t": continue raise UnknownFormatChar(fmt_string, c) if len(result.arg_stack) > 1: # Not all parentheses were closed: raise MismatchedParentheses(self.fmt_string) #print result #from pprint import pprint #pprint(result.args) return result gcc-python-plugin-0.17/libcpychecker/__init__.py000066400000000000000000000123161342215241600217330ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . from __future__ import print_function import sys import gcc from libcpychecker.formatstrings import check_pyargs from libcpychecker.utils import log from libcpychecker.refcounts import check_refcounts, get_traces from libcpychecker.attributes import register_our_attributes from libcpychecker.initializers import check_initializers from libcpychecker.types import get_PyObject if hasattr(gcc, 'PLUGIN_FINISH_DECL'): from libcpychecker.compat import on_finish_decl class CpyCheckerGimplePass(gcc.GimplePass): """ The custom pass that implements the per-function part of our extra compile-time checks """ def __init__(self, dump_traces=False, show_traces=False, verify_pyargs=True, verify_refcounting=False, show_possible_null_derefs=False, only_on_python_code=True, maxtrans=256, dump_json=False, verbose=False): gcc.GimplePass.__init__(self, 'cpychecker-gimple') self.dump_traces = dump_traces self.show_traces = show_traces self.verify_pyargs = verify_pyargs # Refcounting verification is run before rewriting gimple into ssa form, # and as such is not expected to handle ssa. In gcc 7 and later, gcc # introduces ssa names before the ssa rewrite (for call arguments that # are calls themselves), and this causes Python exceptions in # refcounting verification. So, disable refcounting for gcc 7 and # later, until refcounting verification can handle ssa names. if verify_refcounting and gcc.GCC_VERSION >= 7000: if verbose: print("cpychecker: warning: " + "Detected gcc 7 or later, disabling verify_refcounting", file=sys.stderr) self.verify_refcounting = False else: self.verify_refcounting = verify_refcounting self.show_possible_null_derefs = show_possible_null_derefs self.only_on_python_code = only_on_python_code self.maxtrans = maxtrans self.dump_json = dump_json def execute(self, fun): if fun: log('%s', fun) if self.verify_pyargs: check_pyargs(fun) if self.only_on_python_code: # Only run the refcount checker on code that # includes : if not get_PyObject(): return # The refcount code is too buggy for now to be on by default: if self.verify_refcounting: if 0: # Profiled version: import cProfile prof_filename = '%s.%s.refcount-profile' % (gcc.get_dump_base_name(), fun.decl.name) cProfile.runctx('self._check_refcounts(fun)', globals(), locals(), filename=prof_filename) import pstats prof = pstats.Stats(prof_filename) prof.sort_stats('cumulative').print_stats(20) else: # Normal mode (without profiler): self._check_refcounts(fun) def _check_refcounts(self, fun): check_refcounts(fun, self.dump_traces, self.show_traces, self.show_possible_null_derefs, maxtrans=self.maxtrans, dump_json=self.dump_json) class CpyCheckerIpaPass(gcc.SimpleIpaPass): """ The custom pass that implements the whole-program part of our extra compile-time checks """ def __init__(self): gcc.SimpleIpaPass.__init__(self, 'cpychecker-ipa') def execute(self): check_initializers() def main(**kwargs): # Register our custom attributes: gcc.register_callback(gcc.PLUGIN_ATTRIBUTES, register_our_attributes) # Hook for GCC 4.7 and later: if hasattr(gcc, 'PLUGIN_FINISH_DECL'): gcc.register_callback(gcc.PLUGIN_FINISH_DECL, on_finish_decl) # Register our GCC passes: gimple_ps = CpyCheckerGimplePass(**kwargs) if 1: # non-SSA version: gimple_ps.register_before('*warn_function_return') else: # SSA version: gimple_ps.register_after('ssa') ipa_ps = CpyCheckerIpaPass() ipa_ps.register_before('*free_lang_data') gcc-python-plugin-0.17/libcpychecker/absinterp.py000066400000000000000000003631101342215241600221640ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . import gcc import gccutils import re import sys from six import StringIO, integer_types from gccutils import get_src_for_loc, get_nonnull_arguments, check_isinstance from gccutils.graph.stmtgraph import StmtGraph, StmtNode from collections import OrderedDict from libcpychecker.utils import log, logging_enabled from libcpychecker.types import * from libcpychecker.diagnostics import location_as_json, type_as_json debug_comparisons = 0 numeric_types = integer_types + (float, ) # I found myself regularly getting State and Transition instances confused. To # ameliorate that, here are some naming conventions and abbreviations: # # Within method names: # "mktrans_" means "make a Transition" # "mkstate_" means "make a State" # # Within variable names # the prefix "t_" means a Transition # the prefix "s_" means a State # the prefix "v_" means an AbstractValue # the prefix "r_" means a Region # the prefix "f_" means a Facet # Valid 'opname' parameters to eval_comparison hooks: opnames = frozenset(['eq', 'ge', 'gt', 'le', 'lt']) def raw_comparison(a, opname, b): assert opname in opnames if opname == 'eq': return a == b elif opname == 'ge': return a >= b elif opname == 'gt': return a > b elif opname == 'le': return a <= b elif opname == 'lt': return a < b else: raise ValueError() def flip_opname(opname): """ Given: A op B get the op' for: B op' A that has the same results """ assert opname in opnames if opname == 'eq': return 'eq' # symmetric elif opname == 'ge': return 'le' elif opname == 'gt': return 'lt' elif opname == 'le': return 'ge' elif opname == 'lt': return 'gt' else: raise ValueError() if debug_comparisons: debug_indent = 0 # Decorator for adding debug tracking to the various comparison operators def dump_comparison(f): def impl_fn(self, *args): global debug_indent print('%s%s.%s:' % (' ' * debug_indent, self.__class__.__name__, f.__name__)) for arg in [self] + list(args): print(' %s%s' % (' ' * debug_indent, arg)) debug_indent += 1 r = f(self, *args) debug_indent -= 1 print('%sreturned: %s' % (' ' * debug_indent, r)) return r return impl_fn def debug_comparison(msg): print('%s%s' % (' ' * debug_indent, msg)) else: # empty decorator def dump_comparison(f): return f ######################################################################## class FnMeta(object): """ Metadata describing an API function """ __slots__ = ('name', # the name of the function 'docurl', # URL of the API documentation, on docs.python.org 'declared_in', # name of the header file in which this is declared 'prototype', # fragment of C giving the prototype (for documentation purposes) 'defined_in', # where is this function defined (in CPython) 'notes', # fragment of text, giving notes on the function ) def __init__(self, **kwargs): for key in self.__slots__: setattr(self, key, None) for key, value in kwargs.items(): setattr(self, key, value) def desc_when_call_returns_value(self, valuedesc): """ Generate descriptive text for a Transition involving a call to this function that returns some value (described in string form) e.g. "when PyTuple_Size() returns ob_size" """ return 'when %s() returns %s' % (self.name, valuedesc) def desc_when_call_succeeds(self): """ Generate descriptive text for a Transition involving a call to this function that succeeds. e.g. "when PyTuple_SetItem() succeeds" """ return 'when %s() succeeds' % self.name def desc_when_call_fails(self, why=None): """ Generate descriptive text for a Transition involving a call to this function that fails, optionally with a textual description of the kind of failure e.g. "when PyTuple_SetItem() fails (index out of range)" """ if why: return 'when %s() fails (%s)' % (self.name, why) else: return 'when %s() fails' % self.name def desc_special(self, event): """ Generate descriptive text for a Transition involving a call to this function that does somthing unusual e.g. "when PyString_Concat() does nothing due to NULL *lhs" """ return 'when %s() %s' % (self.name, event) ############################################################################ # Various kinds of r-value: ############################################################################ class AbstractValue(object): """ Base class, representing some subset of possible values out of the full set of values that this r-value could hold. """ __slots__ = ('gcctype', 'loc', 'fromsplit') def __init__(self, gcctype, loc): if gcctype: check_isinstance(gcctype, gcc.Type) if loc: check_isinstance(loc, gcc.Location) self.gcctype = gcctype self.loc = loc def __str__(self): if self.gcctype: result = '%s' % self.gcctype else: result = 'unknown type' if self.loc: result += ' from %s' % self.loc return result def __repr__(self): return ('%s(gcctype=%r, loc=%r)' % (self.__class__.__name__, str(self.gcctype), self.loc)) def as_json(self, state): result = dict(kind=self.__class__.__name__, gcctype=type_as_json(self.gcctype), value_comes_from=location_as_json(self.loc)) # Get extra per-class JSON fields: result.update(self.json_fields(state)) return result def json_fields(self, state): # Hook for getting extra per-class fields for JSON serialization # Empty for the base class return dict() def is_null_ptr(self): """ Is this AbstractValue *definitely* a NULL pointer? """ # Overridden by ConcreteValue return False def get_transitions_for_function_call(self, state, stmt): """ For use for handling function pointers. Return a list of Transition instances giving the outcome of calling this function ptr value """ check_isinstance(state, State) check_isinstance(stmt, gcc.GimpleCall) returntype = stmt.fn.type.dereference.type from libcpychecker.refcounts import type_is_pyobjptr_subclass if type_is_pyobjptr_subclass(returntype): log('Invocation of function pointer returning PyObject * (or subclass)') # Assume that all such functions either: # - return a new reference, or # - return NULL and set an exception (e.g. MemoryError) return state.cpython.make_transitions_for_new_ref_or_fail(stmt, None, 'new ref from call through function pointer') return state.apply_fncall_side_effects( [state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), 'calling %s' % self)], stmt) def eval_unary_op(self, exprcode, gcctype, loc): if exprcode == gcc.ConvertExpr: raise NotImplementedError("Don't know how to cope with type conversion of: %r (%s) at %s to type %s" % (self, self, loc, gcctype)) else: raise NotImplementedError("Don't know how to cope with exprcode: %r (%s) on %s at %s" % (exprcode, exprcode, self, loc)) def eval_binop(self, exprcode, rhs, rhsdesc, gcctype, loc): raise NotImplementedError @dump_comparison def eval_comparison(self, opname, rhs, rhsdesc): """ opname is a string in opnames Return a boolean, or None (meaning we don't know) """ raise NotImplementedError("eval_comparison for %s (%s)" % (self, opname)) def extract_from_parent(self, region, gcctype, loc): """ Called on a parent when inheriting a value from it for a child region, for example, when a whole struct has "UnknownValue", we can extract a particular field, giving an UnknownValue of the appropriate type """ raise NotImplementedError('%s.extract_from_parent(%s, %s, %s)' % (self.__class__.__name__, region, gcctype, loc)) def as_string_constant(self): """ If this is a pointer to a string constant, return the underlying string, otherwise return None """ if isinstance(self, PointerToRegion): if isinstance(self.region, RegionForStringConstant): return self.region.text # We could be dealing with e.g. char *ptr = "hello world"; # where "hello world" is a 'char[12]', and thus ptr has been # assigned a char* pointing to '"hello world"[0]' if isinstance(self.region, ArrayElementRegion): if isinstance(self.region.parent, RegionForStringConstant): return self.region.parent.text[self.region.index:] # Otherwise, not a string constant, return None def union(self, v_other): check_isinstance(v_other, AbstractValue) raise NotImplementedError('%s.union(%s, %s)' % (self.__class__.__name__, v_other)) class EmptySet(AbstractValue): """ The empty set: there are no possible values for this variable (yet). """ def union(self, v_other): check_isinstance(v_other, AbstractValue) return v_other class UnknownValue(AbstractValue): """ A value that we know nothing about: it could be any of the possible values """ @classmethod def make(cls, gcctype, loc): """ For some types, we may be able to supply more information """ if gcctype: check_isinstance(gcctype, gcc.Type) if loc: check_isinstance(loc, gcc.Location) if gcctype: if isinstance(gcctype, gcc.IntegerType): # Supply range limits for integer types, from the type itself: return WithinRange(gcctype, loc, gcctype.min_value.constant, gcctype.max_value.constant) return UnknownValue(gcctype, loc) def __str__(self): if self.gcctype: return 'unknown %s from %s' % (self.gcctype, self.loc) else: if self.loc: return 'unknown value from %s' % self.loc else: return 'unknown value' def __repr__(self): return 'UnknownValue(gcctype=%r, loc=%r)' % (self.gcctype, self.loc) def eval_unary_op(self, exprcode, gcctype, loc): return UnknownValue.make(gcctype, loc) def eval_binop(self, exprcode, rhs, rhsdesc, gcctype, loc): return UnknownValue.make(gcctype, loc) @dump_comparison def eval_comparison(self, opname, rhs, rhsdesc): if opname == 'eq': # If it's the *same* value, it's equal to itself: if self is rhs: return True return None def extract_from_parent(self, region, gcctype, loc): return UnknownValue.make(gcctype, loc) def union(self, v_other): check_isinstance(v_other, AbstractValue) return self def eval_binop(exprcode, a, b, rhsvalue): """ Evaluate a gcc exprcode on a pair of Python values (as opposed to AbstractValue instances) """ log('eval_binop(%s, %s, %s)', exprcode, a, b) assert isinstance(a, numeric_types) assert isinstance(b, numeric_types) assert isinstance(rhsvalue, AbstractValue) def inner(): if exprcode == gcc.PlusExpr: return a + b elif exprcode == gcc.MinusExpr: return a - b elif exprcode == gcc.MultExpr: return a * b elif exprcode == gcc.TruncDivExpr: return a // b elif exprcode == gcc.ExactDivExpr: return a / b elif exprcode == gcc.TruncModExpr: return a % b elif exprcode == gcc.MaxExpr: return max(a, b) elif exprcode == gcc.MinExpr: return min(a, b) elif exprcode == gcc.BitIorExpr: return a | b elif exprcode == gcc.BitAndExpr: return a & b elif exprcode == gcc.BitXorExpr: return a ^ b elif exprcode == gcc.LshiftExpr: return a << b elif exprcode == gcc.RshiftExpr: return a >> b elif exprcode == gcc.TruthAndExpr: return a and b elif exprcode == gcc.TruthOrExpr: return a or b # (an implicit return of None means "did not know how to handle this # expression") try: result = inner() except (ArithmeticError, ValueError): err = sys.exc_info()[1] isdefinite = not hasattr(rhsvalue, 'fromsplit') raise PredictedArithmeticError(err, rhsvalue, isdefinite) log('result: %s', result) assert isinstance(result, numeric_types) return result class ConcreteValue(AbstractValue): """ A known, specific value (e.g. 0) """ __slots__ = ('value', ) def __init__(self, gcctype, loc, value): check_isinstance(gcctype, gcc.Type) if loc: check_isinstance(loc, gcc.Location) check_isinstance(value, numeric_types) self.gcctype = gcctype self.loc = loc self.value = value @classmethod def from_int(self, value): return ConcreteValue(gcc.Type.int(), None, value) def __ne__(self, other): if isinstance(other, ConcreteValue): return self.value != other.value return NotImplemented def __str__(self): if self.loc: return ('(%s)%s from %s' % (self.gcctype, value_to_str(self.value), self.loc)) else: return ('(%s)%s' % (self.gcctype, value_to_str(self.value))) def __repr__(self): return ('ConcreteValue(gcctype=%r, loc=%r, value=%s)' % (str(self.gcctype), self.loc, value_to_str(self.value))) def json_fields(self, state): return dict(value=self.value) def is_null_ptr(self): if isinstance(self.gcctype, gcc.PointerType): return self.value == 0 def get_transitions_for_function_call(self, state, stmt): check_isinstance(state, State) check_isinstance(stmt, gcc.GimpleCall) class CallOfNullFunctionPtr(PredictedError): def __init__(self, stmt, value): check_isinstance(stmt, gcc.Gimple) check_isinstance(value, AbstractValue) self.stmt = stmt self.value = value def __str__(self): return ('call of NULL function pointer at %s: %s' % (self.stmt.loc, self.value)) if self.is_null_ptr(): raise CallOfNullFunctionPtr(stmt, self) return AbstractValue.get_transitions_for_function_call(self, state, stmt) def eval_unary_op(self, exprcode, gcctype, loc): if exprcode == gcc.AbsExpr: return ConcreteValue(gcctype, loc, abs(self.value)) elif exprcode == gcc.BitNotExpr: # FIXME: bitwise-complement, with the correct width # self.gcctype.precision return ConcreteValue(gcctype, loc, ~self.value) elif exprcode == gcc.NegateExpr: return ConcreteValue(gcctype, loc, -self.value) elif exprcode == gcc.ConvertExpr: # Is this value expressible within the new type? # If not, we might lose information if isinstance(self.gcctype, gcc.IntegerType) \ and isinstance(gcctype, gcc.IntegerType): if (self.value >= gcctype.min_value.constant and self.value <= gcctype.max_value.constant): # The old range will convert OK to the new type: return ConcreteValue(gcctype, loc, self.value) # We might lose information e.g. truncation; be pessimistic for now: return UnknownValue.make(gcctype, loc) elif exprcode == gcc.FixTruncExpr: return ConcreteValue(gcctype, loc, int(self.value)) elif exprcode == gcc.FloatExpr: return ConcreteValue(gcctype, loc, float(self.value)) else: raise NotImplementedError("Don't know how to cope with exprcode: %r (%s) on %s at %s" % (exprcode, exprcode, self, loc)) def eval_binop(self, exprcode, rhs, rhsdesc, gcctype, loc): if isinstance(rhs, ConcreteValue): newvalue = eval_binop(exprcode, self.value, rhs.value, rhs) if newvalue is not None: return ConcreteValue(gcctype, loc, newvalue) return UnknownValue.make(gcctype, loc) @dump_comparison def eval_comparison(self, opname, rhs, rhsdesc): log('ConcreteValue.eval_comparison(%s, %s%s)', self, opname, rhs) if isinstance(rhs, ConcreteValue): return raw_comparison(self.value, opname, rhs.value) elif isinstance(rhs, WithinRange): # Specialcase for equality: if opname == 'eq': if not rhs.contains(self.value): return False # Split into 2 or 3 parts: ranges = [] if rhs.minvalue < self.value: # subrange that's < ranges.append(WithinRange.make(rhs.gcctype, rhs.loc, rhs.minvalue, self.value-1)) ranges.append(WithinRange.make(rhs.gcctype, rhs.loc, self.value)) if self.value < rhs.maxvalue: # subrange that's > ranges.append(WithinRange.make(rhs.gcctype, rhs.loc, self.value+1, rhs.maxvalue)) rhs.raise_split(rhsdesc, *ranges) # For everything else (inequalities), consider ranges: self_vs_min = raw_comparison(self.value, opname, rhs.minvalue) self_vs_max = raw_comparison(self.value, opname, rhs.maxvalue) if self_vs_min == self_vs_max: return self_vs_min else: # Prepare a split, autogenerating the appropriate # boundaries: class RangeOfComparison: """ A range over which the comparison against the ConcreteValue has a constant value """ __slots__ = ('rng', 'result') def __init__(self, rng, result): check_isinstance(rng, (ConcreteValue, WithinRange)) check_isinstance(result, (bool, None)) self.rng = rng self.result = result def __repr__(self): return ('RangeOfComparison(%r, %r)' % (self.rng, self.result)) # Where are the boundary values? raw_boundaries = sorted(list(set([self.value - 1, self.value, self.value + 1, rhs.minvalue, rhs.maxvalue]))) # Filter them to be within the existing range: raw_boundaries = [v for v in raw_boundaries if rhs.contains(v)] if debug_comparisons: debug_comparison([value_to_str(v) for v in raw_boundaries]) # Calculate a minimal list of RangeOfComparison instances # Within each one, the comparison against the ConcreteValue has # a consistent result: ranges = [] num_boundary_ranges = len(raw_boundaries) if debug_comparisons: debug_comparison('num_boundary_ranges: %r' % num_boundary_ranges) for i in range(num_boundary_ranges): minvalue = raw_boundaries[i] if minvalue < rhs.gcctype.min_value.constant: minvalue = rhs.gcctype.min_value.constant if i < num_boundary_ranges - 1: # Extend up to but not including the next range: maxvalue = raw_boundaries[i + 1] - 1 else: # Final range: use full range: maxvalue = rhs.maxvalue if maxvalue > rhs.gcctype.max_value.constant: maxvalue = rhs.gcctype.max_value.constant if debug_comparisons: debug_comparison('%i [%s..%s]' % (i, value_to_str(minvalue), value_to_str(maxvalue))) check_isinstance(minvalue, numeric_types) check_isinstance(maxvalue, numeric_types) # Only "proper" ranges: if minvalue <= maxvalue: self_vs_min = raw_comparison(self.value, opname, minvalue) self_vs_max = raw_comparison(self.value, opname, maxvalue) # All ranges should have identical value when compared # against the concrete value: assert self_vs_min == self_vs_max if debug_comparisons: debug_comparison(' [%s..%s] %s %s ?: %s' % (value_to_str(minvalue), value_to_str(maxvalue), opname, self.value, self_vs_min)) if ranges and ranges[-1].result == self_vs_min: # These ranges are adjacent and have the same result; # merge them: oldrange = ranges[-1].rng if isinstance(oldrange, ConcreteValue): newrange = WithinRange(oldrange.gcctype, oldrange.loc, oldrange.value, maxvalue) else: check_isinstance(oldrange, WithinRange) newrange = WithinRange(oldrange.gcctype, oldrange.loc, oldrange.minvalue, maxvalue) ranges[-1].rng = newrange else: # We have a range with a different value: roc = RangeOfComparison(WithinRange.make(rhs.gcctype, rhs.loc, minvalue, maxvalue), self_vs_min) ranges.append(roc) if debug_comparisons: from pprint import pprint pprint(ranges) rhs.raise_split(rhsdesc, *[roc.rng for roc in ranges]) return None def extract_from_parent(self, region, gcctype, loc): return ConcreteValue(gcctype, loc, self.value) def union(self, v_other): check_isinstance(v_other, AbstractValue) if isinstance(v_other, ConcreteValue): if self.value == v_other.value: return self return WithinRange.make(self.gcctype, self.loc, self.value, v_other.value) if isinstance(v_other, WithinRange): return WithinRange.make(self.gcctype, self.loc, *(self.value, v_other.minvalue, v_other.maxvalue)) raise NotImplementedError('%s.union(%s)' % (self.__class__.__name__, v_other)) def value_to_str(value): """ Display large integers/longs in hexadecimal, since it's easier to decipher -0x8000000000000000 than -9223372036854775808 """ check_isinstance(value, numeric_types) if isinstance(value, integer_types): if abs(value) > 0x100000: return hex(value) return str(value) class WithinRange(AbstractValue): """ A value known to be within a given range e.g. -3 <= val <= +4 """ __slots__ = ('minvalue', 'maxvalue', ) def __init__(self, gcctype, loc, *values): """ The constructor can take one or more values; the resulting set is the minimal range covering all of the input values, For example, WithinRange(gcctype, loc, 7, 4, -4, -2) will give the range -2 <= val < 7 """ check_isinstance(gcctype, gcc.Type) if loc: check_isinstance(loc, gcc.Location) assert len(values) >= 1 for value in values: check_isinstance(value, numeric_types) self.gcctype = gcctype self.loc = loc self.minvalue = min(values) self.maxvalue = max(values) # Clamp to be within the type's expressible range: if self.minvalue < gcctype.min_value.constant: self.minvalue = gcctype.min_value.constant if self.maxvalue > gcctype.max_value.constant: self.maxvalue = gcctype.max_value.constant @classmethod def make(cls, gcctype, loc, *values): """ Generate a WithinRange instance, unless the range uniqely identifies a value, in which case generate a ConcreteValue instance """ minvalue = min(values) maxvalue = max(values) if minvalue == maxvalue: return ConcreteValue(gcctype, loc, minvalue) else: return WithinRange(gcctype, loc, minvalue, maxvalue) @classmethod def ge_zero(cls, gcctype, loc): """ Make a WithinRange for the given type, assuming a value >= 0, up to the maximum value representable by the type """ return WithinRange(gcctype, loc, 0, gcctype.max_value.constant) def __str__(self): if self.loc: return ('(%s)val [%s <= val <= %s] from %s' % (self.gcctype, value_to_str(self.minvalue), value_to_str(self.maxvalue), self.loc)) else: return ('(%s)val [%s <= val <= %s]' % (self.gcctype, value_to_str(self.minvalue), value_to_str(self.maxvalue))) def __repr__(self): return ('WithinRange(gcctype=%r, loc=%r, minvalue=%s, maxvalue=%s)' % (str(self.gcctype), self.loc, value_to_str(self.minvalue), value_to_str(self.maxvalue))) def json_fields(self, state): return dict(minvalue=self.minvalue, maxvalue=self.maxvalue) def eval_unary_op(self, exprcode, gcctype, loc): if exprcode == gcc.AbsExpr: values = [abs(val) for val in (self.minvalue, self.maxvalue)] return WithinRange.make(gcctype, loc, min(values), max(values)) elif exprcode == gcc.BitNotExpr: return UnknownValue.make(gcctype, loc) elif exprcode == gcc.NegateExpr: return WithinRange.make(gcctype, loc, -self.maxvalue, -self.minvalue) elif exprcode == gcc.ConvertExpr: # Is the whole of this range fully expressible within the new type? # If not, we might lose information if isinstance(self.gcctype, gcc.IntegerType) \ and isinstance(gcctype, gcc.IntegerType): if (self.minvalue >= gcctype.min_value.constant and self.maxvalue <= gcctype.max_value.constant): # The old range will convert OK to the new type: return WithinRange.make(gcctype, loc, self.minvalue, self.maxvalue) # We might lose information e.g. truncation; be pessimistic for now: return UnknownValue.make(gcctype, loc) elif exprcode == gcc.FloatExpr: return UnknownValue.make(gcctype, loc) else: raise NotImplementedError("Don't know how to cope with exprcode: %r (%s) on %s at %s" % (exprcode, exprcode, self, loc)) def eval_binop(self, exprcode, rhs, rhsdesc, gcctype, loc): if isinstance(rhs, ConcreteValue): values = [eval_binop(exprcode, val, rhs.value, rhs) for val in (self.minvalue, self.maxvalue)] return WithinRange.make(gcctype, loc, min(values), max(values)) elif isinstance(rhs, WithinRange): # Assume that the operations are "concave" in that the resulting # range is within that found by trying all four corners: # Avoid division by zero: # (see https://fedorahosted.org/gcc-python-plugin/ticket/25 ) if exprcode == gcc.TruncDivExpr or exprcode == gcc.TruncModExpr: if rhs.minvalue == 0 and rhs.maxvalue > 0: zero_range = WithinRange.make(rhs.gcctype, rhs.loc, 0) gt_zero_range = WithinRange.make(rhs.gcctype, rhs.loc, 1, rhs.maxvalue) rhs.raise_split(rhsdesc, zero_range, gt_zero_range) # Avoid negative shifts: # (see https://fedorahosted.org/gcc-python-plugin/ticket/14 ) if exprcode == gcc.LshiftExpr or exprcode == gcc.RshiftExpr: if rhs.minvalue < 0 and rhs.maxvalue >= 0: neg_range = WithinRange.make(rhs.gcctype, rhs.loc, rhs.minvalue, -1) ge_zero_range = WithinRange.make(rhs.gcctype, rhs.loc, 0, rhs.maxvalue) rhs.raise_split(rhsdesc, neg_range, ge_zero_range) values = (eval_binop(exprcode, self.minvalue, rhs.minvalue, rhs), eval_binop(exprcode, self.minvalue, rhs.maxvalue, rhs), eval_binop(exprcode, self.maxvalue, rhs.minvalue, rhs), eval_binop(exprcode, self.maxvalue, rhs.maxvalue, rhs)) return WithinRange.make(gcctype, loc, min(values), max(values)) return UnknownValue.make(gcctype, loc) def contains(self, rawvalue): check_isinstance(rawvalue, numeric_types) return self.minvalue <= rawvalue and rawvalue <= self.maxvalue @dump_comparison def eval_comparison(self, opname, rhs, rhsdesc): log('WithinRange.eval_comparison(%s, %s%s)', self, opname, rhs) # If it's the *same* value, it's equal to itself: if opname == 'eq': if self is rhs: return True if isinstance(rhs, WithinRange): # They can only be equal if there's an overlap: if self.contains(rhs.minvalue) or self.contains(rhs.maxvalue): # Maybe equal: return None else: # No overlap: definitely non-equal: return False if isinstance(rhs, ConcreteValue): # to implement WithinRange op ConcreteValue, use: # ConcreteValue flip(op) WithinRange return rhs.eval_comparison(flip_opname(opname), self, None) return None def raise_split(self, valuedesc, *new_ranges): """ Raise a SplitValue exception to subdivide this range into subranges """ descriptions = [] if valuedesc is None: valuedesc = 'value' for r in new_ranges: if isinstance(r, WithinRange): descriptions.append('when considering range: %s <= %s <= %s' % (value_to_str(r.minvalue), valuedesc, value_to_str(r.maxvalue))) elif isinstance(r, ConcreteValue): descriptions.append('when considering %s == %s' % (valuedesc, r)) else: raise TypeError('unrecognized type: %r' % r) raise SplitValue(self, new_ranges, descriptions) def raise_as_concrete(self, loc, value, desc): """ Raise a SplitValue exception to reinterpret this range as a specific ConcreteValue from now on. This is slightly abusing the SplitValue mechanism, as it's just one new value, but it should at least add the descriptive text into the trace. """ if loc: check_isinstance(loc, gcc.Location) check_isinstance(value, numeric_types) check_isinstance(desc, str) v_new = ConcreteValue(self.gcctype, loc, value) raise SplitValue(self, [v_new], [desc]) def extract_from_parent(self, region, gcctype, loc): return WithinRange.make(gcctype, self.loc, self.minvalue, self.maxvalue) def union(self, v_other): check_isinstance(v_other, AbstractValue) if isinstance(v_other, ConcreteValue): return WithinRange.make(self.gcctype, self.loc, *(self.minvalue, self.maxvalue, v_other.value)) if isinstance(v_other, WithinRange): return WithinRange.make(self.gcctype, self.loc, *(self.minvalue, self.maxvalue, v_other.minvalue, v_other.maxvalue)) raise NotImplementedError('%s.union(%s)' % (self.__class__.__name__, v_other)) class PointerToRegion(AbstractValue): """A non-NULL pointer value, pointing at a specific Region""" __slots__ = ('region', ) def __init__(self, gcctype, loc, region): AbstractValue.__init__(self, gcctype, loc) check_isinstance(region, Region) self.region = region def __str__(self): if self.loc: return '(%s)&%r from %s' % (self.gcctype, self.region, self.loc) else: return '(%s)&%r' % (self.gcctype, self.region) def __repr__(self): return 'PointerToRegion(gcctype=%r, loc=%r, region=%r)' % (str(self.gcctype), self.loc, self.region) def json_fields(self, state): return dict(target=self.region.as_json()) def eval_comparison(self, opname, rhs, rhsdesc): log('PointerToRegion.eval_comparison:(%s, %s%s)', self, opname, rhs) if opname == 'eq': if isinstance(rhs, ConcreteValue) and rhs.value == 0: log('ptr to region vs 0: %s is definitely not equal to %s', self, rhs) return False if isinstance(rhs, PointerToRegion): log('comparing regions: %s %s', self, rhs) return self.region == rhs.region # We don't know: return None def eval_unary_op(self, exprcode, gcctype, loc): if exprcode == gcc.ConvertExpr: # Casting of this non-NULL pointer to another type: return UnknownValue.make(gcctype, loc) # Defer to base class: AbstractValue.eval_unary_op(self, exprcode, gcctype, loc) class DeallocatedMemory(AbstractValue): """ A 'poisoned' r-value: this memory has been deallocated, so the r-value is meaningless. """ def __str__(self): if self.loc: return 'memory deallocated at %s' % self.loc else: return 'deallocated memory' def extract_from_parent(self, region, gcctype, loc): return DeallocatedMemory(gcctype, self.loc) class UninitializedData(AbstractValue): """ A 'poisoned' r-value: this memory has not yet been written to, so the r-value is meaningless. """ def __str__(self): if self.loc: return 'uninitialized data at %s' % self.loc else: return 'uninitialized data' def get_transitions_for_function_call(self, state, stmt): check_isinstance(state, State) check_isinstance(stmt, gcc.GimpleCall) class CallOfUninitializedFunctionPtr(PredictedError): def __init__(self, stmt, value): check_isinstance(stmt, gcc.Gimple) check_isinstance(value, AbstractValue) self.stmt = stmt self.value = value def __str__(self): return ('call of uninitialized function pointer at %s: %s' % (self.stmt.loc, self.value)) raise CallOfUninitializedFunctionPtr(stmt, self) def extract_from_parent(self, region, gcctype, loc): return UninitializedData(gcctype, self.loc) def make_null_ptr(gcctype, loc): return ConcreteValue(gcctype, loc, 0) ############################################################################ # Various kinds of predicted error: ############################################################################ class PredictedError(Exception): pass class InvalidlyNullParameter(PredictedError): # Use this when we can predict that a function is called with NULL as an # argument for an argument that must not be NULL def __init__(self, fnname, paramidx, nullvalue): self.fnname = fnname self.paramidx = paramidx # starts at 1 self.nullvalue = nullvalue def __str__(self): return ('%s can be called with NULL as parameter %i; %s' % (self.fnname, self.paramidx, self.nullvalue)) class PredictedValueError(PredictedError): def __init__(self, state, expr, value, isdefinite): check_isinstance(state, State) check_isinstance(expr, gcc.Tree) check_isinstance(value, AbstractValue) self.state = state self.expr = expr self.value = value self.isdefinite = isdefinite class PredictedArithmeticError(PredictedError): def __init__(self, err, rhsvalue, isdefinite): check_isinstance(err, (ArithmeticError, ValueError)) self.err = err self.rhsvalue = rhsvalue self.isdefinite = isdefinite def __str__(self): if self.isdefinite: return '%s with right-hand-side %s' % (self.err, self.rhsvalue) else: return 'possible %s with right-hand-side %s' % (self.err, self.rhsvalue) class UsageOfUninitializedData(PredictedValueError): def __init__(self, state, expr, value, desc): check_isinstance(state, State) check_isinstance(expr, gcc.Tree) check_isinstance(value, AbstractValue) PredictedValueError.__init__(self, state, expr, value, True) check_isinstance(desc, str) self.desc = desc def __str__(self): return ('%s at %s' % (self.desc, self.state.stmtnode.get_stmt().loc)) class NullPtrDereference(PredictedValueError): def __init__(self, state, expr, ptr, isdefinite): check_isinstance(state, State) check_isinstance(expr, gcc.Tree) check_isinstance(expr, (gcc.ComponentRef, gcc.MemRef)) PredictedValueError.__init__(self, state, expr, ptr, isdefinite) def __str__(self): if self.isdefinite: return ('dereferencing NULL (%s) at %s' % (self.expr, self.state.stmtnode.get_stmt().loc)) else: return ('possibly dereferencing NULL (%s) at %s' % (self.expr, self.state.stmtnode.get_stmt().loc)) class NullPtrArgument(PredictedValueError): def __init__(self, state, stmt, idx, ptr, isdefinite, why): check_isinstance(state, State) check_isinstance(stmt, gcc.Gimple) check_isinstance(idx, int) check_isinstance(ptr, AbstractValue) if why is not None: check_isinstance(why, str) PredictedValueError.__init__(self, state, stmt.args[idx], ptr, isdefinite) self.stmt = stmt self.idx = idx # this is a 0-based index; it is changed to a 1-based index when # printed self.why = why def __str__(self): if self.isdefinite: return ('calling %s with NULL as argument %i (%s) at %s' % (self.stmt.fn, self.idx + 1, self.expr, self.state.stmtnode.get_stmt().loc)) else: return ('possibly calling %s with NULL as argument %i (%s) at %s' % (self.stmt.fn, self.idx + 1, self.expr, self.state.stmtnode.get_stmt().loc)) class ReadFromDeallocatedMemory(PredictedError): def __init__(self, stmt, value): check_isinstance(stmt, gcc.Gimple) check_isinstance(value, DeallocatedMemory) self.stmt = stmt self.value = value def __str__(self): return ('reading from deallocated memory at %s: %s' % (self.stmt.loc, self.value)) class PassingPointerToDeallocatedMemory(PredictedError): def __init__(self, argidx, fnname, stmt, value): check_isinstance(stmt, gcc.Gimple) check_isinstance(value, DeallocatedMemory) self.argidx = argidx self.fnname = fnname self.stmt = stmt self.value = value def __str__(self): return ('passing pointer to deallocated memory as argument %i of %s at %s: %s' % (self.argidx + 1, self.fnname, self.stmt.loc, self.value)) def describe_stmt(stmt): if isinstance(stmt, gcc.GimpleCall): if isinstance(stmt.fn.operand, gcc.FunctionDecl): fnname = stmt.fn.operand.name return 'call to %s at line %i' % (fnname, stmt.loc.line) else: return str(stmt.loc) class Region(object): __slots__ = ('name', 'parent', 'children', 'fields', ) def __init__(self, name, parent): self.name = name self.parent = parent self.children = [] self.fields = {} if parent: parent.children.append(self) def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.name) def as_json(self): m = re.match(r"region for gcc.ParmDecl\('(\S+)'\)\.(\S+)", self.name) if m: return '%s->%s' % (m.group(1), m.group(2)) return self.name def is_on_stack(self): if isinstance(self, RegionOnStack): return True if self.parent: return self.parent.is_on_stack() return False class RegionForGlobal(Region): """ Represents the area of memory (e.g. in .data or .bss section) used to store a particular globa """ __slots__ = ('vardecl', ) def __init__(self, vardecl): check_isinstance(vardecl, (gcc.VarDecl, gcc.FunctionDecl)) Region.__init__(self, vardecl.name, None) self.vardecl = vardecl def __repr__(self): return 'RegionForGlobal(%r)' % self.vardecl def as_json(self): return str(self.vardecl) class RegionOnStack(Region): def __repr__(self): return 'RegionOnStack(%r)' % self.name def __str__(self): return '%s on stack' % self.name class RegionForLocal(RegionOnStack): __slots__ = ('vardecl', ) def __init__(self, vardecl, stack): RegionOnStack.__init__(self, 'region for %r' % vardecl, stack) self.vardecl = vardecl def as_json(self): return str(self.vardecl) class RegionForStaticLocal(RegionForGlobal): # "static" locals work more like globals. In particular, they're not on # the stack pass class RegionOnHeap(Region): """ Represents an area of memory allocated on the heap """ __slots__ = ('alloc_stmt', ) def __init__(self, name, alloc_stmt): check_isinstance(alloc_stmt, gcc.Gimple) Region.__init__(self, name, None) self.alloc_stmt = alloc_stmt def __repr__(self): return 'RegionOnHeap(%r, %r)' % (self.name, self.alloc_stmt.loc) def __str__(self): return '%s allocated at %s' % (self.name, self.alloc_stmt.loc) class RegionForStringConstant(Region): """ Represents an area of memory used for string constants typically allocated in the .data segment """ __slots__ = ('text', ) def __init__(self, text): Region.__init__(self, text, None) self.text = text def as_json(self): return str(repr(self.text)) class ArrayElementRegion(Region): __slots__ = ('index', ) def __init__(self, name, parent, index): Region.__init__(self, name, parent) self.index = index class MissingValue(Exception): """ The value tracking system couldn't figure out any information about the given region """ def __init__(self, region): self.region = region def __str__(self): return 'Missing value for %s' % self.region class SplitValue(Exception): """ We encountered an value (e.g. UnknownValue), but we'd like to know more about it. Backtrack the analysis, splitting it into multiple possible worlds with alternate abstract values for said value """ def __init__(self, value, altvalues, descriptions): self.value = value self.altvalues = altvalues self.descriptions = descriptions def __str__(self): return ('Splitting:\n%r\ninto\n%s' % (self.value, '\n'.join([repr(alt) for alt in self.altvalues]))) def split(self, state): log('creating states for split of %s into %s', self.value, self.altvalues) result = [] for altvalue, desc in zip(self.altvalues, self.descriptions): log(' creating state for split where %s is %s', self.value, altvalue) altvalue.fromsplit = True newstate = state.copy() newstate.fromsplit = True for r in newstate.value_for_region: # Replace instances of the value itself: if newstate.value_for_region[r] is self.value: log(' replacing value for region %s with %s', r, altvalue) newstate.value_for_region[r] = altvalue result.append(Transition(state, newstate, desc)) return result class Facet(object): """ A facet of state, relating to a particular API (e.g. libc, cpython, etc) Each facet knows which State instance it relates to, and knows how to copy itself to a new State. Potentially it can also supply "impl_" methods, which implement named functions within the API, describing all possible transitions from the current state to new states (e.g. success, failure, etc), creating appropriate new States with appropriate new Facet subclass instances. """ __slots__ = ('state', ) def __init__(self, state): check_isinstance(state, State) self.state = state def copy(self, newstate): # Concrete subclasses should implement this. raise NotImplementedError class State(object): """ A Location with memory state, and zero or more additional "facets" of state, one per API that we care about. 'facets' is a dict, mapping attribute names to Facet subclass. For example, it might be: {'cpython': CPython, 'libc': Libc, 'glib': GLib} indicating that we expect all State instances to have a s.cpython field, with a CPython instance, and a s.libc field (a Libc instance), etc. Every State "knows" what all its facets are, and each Facet has a "state" attribute recording which State instance it is part of. For example, a CPython facet can keep track of the thread-local exception status, and a Libc facet can keep track of file-descriptors, malloc buffers, etc. Hopefully this will allow checking of additional APIs to be slotted into the checker, whilst keeping each API's special-case rules isolated. """ # We can't use the __slots__ optimization here, as we're adding additional # per-facet attributes def __init__(self, stmtgraph, stmtnode, lastgccloc, facets, region_for_var=None, value_for_region=None, return_rvalue=None, has_returned=False, not_returning=False): check_isinstance(stmtgraph, StmtGraph) check_isinstance(stmtnode, StmtNode) check_isinstance(facets, dict) self.stmtgraph = stmtgraph self.fun = stmtgraph.fun self.stmtnode = stmtnode self.lastgccloc = lastgccloc self.facets = facets # Mapping from VarDecl.name to Region: if region_for_var: check_isinstance(region_for_var, OrderedDict) self.region_for_var = region_for_var else: self.region_for_var = OrderedDict() # Mapping from Region to AbstractValue: if value_for_region: check_isinstance(value_for_region, OrderedDict) self.value_for_region = value_for_region else: self.value_for_region = OrderedDict() self.return_rvalue = return_rvalue self.has_returned = has_returned self.not_returning = not_returning def __str__(self): return ('loc: %s region_for_var:%s value_for_region:%s' % (self.stmtnode, self.region_for_var, self.value_for_region)) def __repr__(self): return ('loc: %r region_for_var:%r value_for_region:%r' % (self.stmtnode, self.region_for_var, self.value_for_region)) def as_str_table(self): # Generate a string, displaying the data in tabular form: from gccutils import Table t = Table(['Expression', 'Region', 'Value']) for k in self.region_for_var: region = self.region_for_var[k] value = self.value_for_region.get(region, None) t.add_row((k, region, value),) s = StringIO() t.write(s) return s.getvalue() def as_json(self, desc): variables = OrderedDict() for k in self.region_for_var: region = self.region_for_var[k] value = self.value_for_region.get(region, None) if value: variables[region.as_json()] = value.as_json(self) result = dict(location=location_as_json(self.stmtnode.get_gcc_loc()), message=desc, variables=variables) return result def log(self, logger): if not logging_enabled: return # Display data in tabular form: logger('%s', self.as_str_table()) #logger('extra: %s' % (self._extra(), ), indent) # FIXME: derived class/extra: #self.resources.log(logger, indent) logger('loc: %s', self.stmtnode) if self.stmtnode.get_stmt(): logger('%s', self.stmtnode.get_stmt().loc) def copy(self): s_new = State(self.stmtgraph, self.stmtnode, self.lastgccloc, self.facets, self.region_for_var.copy(), self.value_for_region.copy(), self.return_rvalue, self.has_returned, self.not_returning) # Make a copy of each facet into the new state: for key in self.facets: facetcls = self.facets[key] f_old = getattr(self, key) f_new = f_old.copy(s_new) setattr(s_new, key, f_new) return s_new def verify(self): """ Perform self-tests to ensure sanity of this State """ for k in self.value_for_region: check_isinstance(k, Region) if not isinstance(self.value_for_region[k], AbstractValue): raise TypeError('value for region %r is not an AbstractValue: %r' % (k, self.value_for_region[k])) def eval_lvalue(self, expr, loc): """ Return the Region for the given expression """ log('eval_lvalue: %r %s', expr, expr) if loc: check_isinstance(loc, gcc.Location) if isinstance(expr, gcc.SsaName): region = self.var_region(expr.var) check_isinstance(region, Region) return region if isinstance(expr, (gcc.VarDecl, gcc.ParmDecl, gcc.ResultDecl, gcc.FunctionDecl)): region = self.var_region(expr) check_isinstance(region, Region) return region elif isinstance(expr, gcc.ArrayRef): region = self.element_region(expr, loc) check_isinstance(region, Region) return region elif isinstance(expr, gcc.ComponentRef): check_isinstance(expr.field, gcc.FieldDecl) return self.get_field_region(expr, loc) elif isinstance(expr, gcc.StringCst): region = self.string_constant_region(expr, loc) check_isinstance(region, Region) return region elif isinstance(expr, gcc.MemRef): # Write through a pointer: dest_ptr = self.eval_rvalue(expr.operand, loc) log('dest_ptr: %r', dest_ptr) self.raise_any_null_ptr_deref(expr, dest_ptr) if isinstance(dest_ptr, UnknownValue): # Split into null/non-null pointers: self.raise_split_value(dest_ptr) check_isinstance(dest_ptr, PointerToRegion) dest_region = dest_ptr.region log('dest_region: %r', dest_region) return dest_region raise NotImplementedError('eval_lvalue: %r %s' % (expr, expr)) def eval_rvalue(self, expr, loc): """ Return the value for the given expression, as an AbstractValue FIXME: also as a Region? """ log('eval_rvalue: %r %s', expr, expr) if loc: check_isinstance(loc, gcc.Location) if isinstance(expr, AbstractValue): return expr if isinstance(expr, Region): return expr if isinstance(expr, gcc.IntegerCst): return ConcreteValue(expr.type, loc, expr.constant) if isinstance(expr, gcc.RealCst): return ConcreteValue(expr.type, loc, expr.constant) if isinstance(expr, gcc.StringCst): return AbstractValue(expr.type, loc) if isinstance(expr, gcc.SsaName): region = self.var_region(expr.var) check_isinstance(region, Region) value = self.get_store(region, expr.type, loc) check_isinstance(value, AbstractValue) return value if isinstance(expr, (gcc.VarDecl, gcc.ParmDecl, gcc.ResultDecl)): region = self.var_region(expr) check_isinstance(region, Region) value = self.get_store(region, expr.type, loc) check_isinstance(value, AbstractValue) return value #return UnknownValue.make(expr.type, str(expr)) if isinstance(expr, gcc.ComponentRef): #check_isinstance(expr.field, gcc.FieldDecl) region = self.get_field_region(expr, loc)#.target, expr.field.name) check_isinstance(region, Region) log('got field region for %s: %r', expr, region) try: value = self.get_store(region, expr.type, loc) log('got value: %r', value) except MissingValue: value = UnknownValue.make(expr.type, loc) log('no value; using: %r', value) check_isinstance(value, AbstractValue) return value if isinstance(expr, gcc.AddrExpr): log('expr.operand: %r', expr.operand) lvalue = self.eval_lvalue(expr.operand, loc) check_isinstance(lvalue, Region) if isinstance(expr.operand.type, gcc.ArrayType): index0_lvalue = self._array_region(lvalue, 0) return PointerToRegion(expr.type, loc, index0_lvalue) else: return PointerToRegion(expr.type, loc, lvalue) if isinstance(expr, gcc.ArrayRef): log('expr.array: %r', expr.array) log('expr.index: %r', expr.index) lvalue = self.eval_lvalue(expr, loc) check_isinstance(lvalue, Region) rvalue = self.get_store(lvalue, expr.type, loc) check_isinstance(rvalue, AbstractValue) return rvalue if isinstance(expr, gcc.MemRef): log('expr.operand: %r', expr.operand) opvalue = self.eval_rvalue(expr.operand, loc) check_isinstance(opvalue, AbstractValue) log('opvalue: %r', opvalue) self.raise_any_null_ptr_deref(expr, opvalue) if isinstance(opvalue, UnknownValue): # Split into null/non-null pointers: self.raise_split_value(opvalue) check_isinstance(opvalue, PointerToRegion) # FIXME rvalue = self.get_store(opvalue.region, expr.type, loc) check_isinstance(rvalue, AbstractValue) return rvalue if isinstance(expr, gcc.BitFieldRef): # e.g. in 'D.2694 = BIT_FIELD_REF <*foo, 8, 0>;' # for now, pessimistically assume nothing: return UnknownValue.make(expr.type, loc) raise NotImplementedError('eval_rvalue: %r %s' % (expr, expr)) return UnknownValue.make(expr.type, loc) # FIXME def assign(self, lhs, rhs, loc): log('assign(%r, %r)', lhs, rhs) log('assign(%s, %s)', lhs, rhs) if loc: check_isinstance(loc, gcc.Location) dest_region = self.eval_lvalue(lhs, loc) log('dest_region: %s %r', dest_region, dest_region) value = self.eval_rvalue(rhs, loc) log('value: %s %r', value, value) check_isinstance(value, AbstractValue) check_isinstance(dest_region, Region) self.value_for_region[dest_region] = value def var_region(self, var): check_isinstance(var, (gcc.VarDecl, gcc.ParmDecl, gcc.ResultDecl, gcc.FunctionDecl)) if var not in self.region_for_var: # Presumably a reference to a global variable: log('adding region for global var: %r', var) region = RegionForGlobal(var) # it is its own region: self.region_for_var[var] = region # Initialize the refcount of global PyObject instances # e.g. _Py_NoneStruct to 0 i.e. we don't own any references to them if str(var.type) == 'struct PyObject': from libcpychecker.refcounts import RefcountValue ob_refcnt = self.make_field_region(region, 'ob_refcnt') # FIXME: this should be a memref and fieldref self.value_for_region[ob_refcnt] = RefcountValue.borrowed_ref(None, region) return self.region_for_var[var] def element_region(self, ar, loc): log('element_region: %s', ar) check_isinstance(ar, gcc.ArrayRef) if loc: check_isinstance(loc, gcc.Location) log(' ar.array: %r', ar.array) log(' ar.index: %r', ar.index) parent = self.eval_lvalue(ar.array, loc) check_isinstance(parent, Region) log(' parent: %r', parent) index = self.eval_rvalue(ar.index, loc) check_isinstance(index, AbstractValue) log(' index: %r', index) if isinstance(index, ConcreteValue): index = index.value return self._array_region(parent, index) def pointer_plus_region(self, stmt): # Cope with treating pointers as arrays. # The constant appears to be in bytes, rather than as units of the type log('pointer_add_region') assert stmt.exprcode == gcc.PointerPlusExpr rhs = stmt.rhs a = self.eval_rvalue(rhs[0], stmt.loc) b = self.eval_rvalue(rhs[1], stmt.loc) log('a: %r', a) log('b: %r', b) if isinstance(a, PointerToRegion) and isinstance(b, ConcreteValue): parent = a.region log('%s', rhs[0].type) log('%s', rhs[0].type.dereference) t = rhs[0].type.dereference if isinstance(t, gcc.VoidType): index = b.value else: sizeof = t.sizeof log('%s', sizeof) index = b.value // sizeof # Offset of zero? just reuse the existing pointer's region: if index == 0: return a.region # Are we offsetting within an array? if isinstance(parent, ArrayElementRegion): return self._array_region(parent.parent, parent.index + index) return self._array_region(parent, index) else: raise NotImplementedError("Don't know how to cope with pointer addition of\n %r\nand\n %rat %s" % (a, b, stmt.loc)) def _array_region(self, parent, index): # Used by element_region, and pointer_add_region log('_array_region(%s, %s)', parent, index) check_isinstance(parent, Region) check_isinstance(index, (integer_types, UnknownValue, ConcreteValue, WithinRange)) if isinstance(index, ConcreteValue): index = index.value if index in parent.fields: log('reusing') return parent.fields[index] log('not reusing') region = ArrayElementRegion('%s[%s]' % (parent.name, index), parent, index) parent.fields[index] = region # it is its own region: self.region_for_var[region] = region return region def get_field_region(self, cr, loc): #target, field): check_isinstance(cr, gcc.ComponentRef) if loc: check_isinstance(loc, gcc.Location) #cr.debug() log('target: %r %s ', cr.target, cr.target) log('field: %r', cr.field) if isinstance(cr.target, gcc.MemRef): ptr = self.eval_rvalue(cr.target.operand, loc) # FIXME log('ptr: %r', ptr) self.raise_any_null_ptr_deref(cr, ptr) if isinstance(ptr, UnknownValue): # It could be NULL; it could be non-NULL # Split the analysis # Non-NULL pointer: log('splitting %s into non-NULL/NULL pointers', cr) self.raise_split_value(ptr) check_isinstance(ptr, PointerToRegion) return self.make_field_region(ptr.region, cr.field.name) target_region = self.eval_lvalue(cr.target, loc) return self.make_field_region(target_region, cr.field.name) def string_constant_region(self, expr, loc): log('string_constant_region: %s', expr) check_isinstance(expr, gcc.StringCst) if loc: check_isinstance(loc, gcc.Location) region = RegionForStringConstant(expr.constant) return region def get_store(self, region, gcctype, loc): if gcctype: check_isinstance(gcctype, gcc.Type) if loc: check_isinstance(loc, gcc.Location) try: val = self._get_store_recursive(region, gcctype, loc) return val except MissingValue: # The first time we look up the value of a global, assign it a new # "unknown" value: if isinstance(region, RegionForGlobal): newval = UnknownValue.make(region.vardecl.type, region.vardecl.location) log('setting up %s for %s', newval, region.vardecl) self.value_for_region[region] = newval return newval # OK: no value known: return UnknownValue.make(gcctype, loc) def summarize_array(self, r_array, v_range, gcctype, loc): """ Determine if the region r_array is fully populated with values in the range of indices covered by v_range If it is, return a representative value """ check_isinstance(r_array, Region) check_isinstance(v_range, WithinRange) v_result = EmptySet(gcctype, loc) # (This loop should rapidly fail when the range is large and/or outside # the bounds of the array) for index in range(v_range.minvalue, v_range.maxvalue + 1): # print 'index: %i' % index if index not in r_array.fields: # We have an uninitialized element: return None r_at_index = r_array.fields[index] # print 'r_at_index: %s' % r_at_index check_isinstance(r_at_index, Region) v_at_index = self.value_for_region[r_at_index] # print 'v_at_index: %s' % v_at_index check_isinstance(v_at_index, AbstractValue) v_result = v_result.union(v_at_index) # print 'v_result: %s' % v_result # Every subregion within the given range is initialized: return v_result def _get_store_recursive(self, region, gcctype, loc): check_isinstance(region, Region) log('_get_store_recursive(%s, %s, %s)', region, gcctype, loc) if region in self.value_for_region: return self.value_for_region[region] # Not found; try default value from parent region: if region.parent: try: parent_value = self._get_store_recursive(region.parent, gcctype, loc) # If we're looking up within an array on the stack that has # been fully initialized, then the default value from the # parent is UninitializedData(), but every actual value that # could be looked up is some sane value. If we're indexing # to an unknown location, don't falsely say it's unitialized: if isinstance(parent_value, UninitializedData): if isinstance(region, ArrayElementRegion): if isinstance(region.index, WithinRange): v_lookup = self.summarize_array(region.parent, region.index, gcctype, loc) if v_lookup: return v_lookup return parent_value.extract_from_parent(region, gcctype, loc) except MissingValue: raise MissingValue(region) raise MissingValue(region) def make_heap_region(self, name, stmt): region = RegionOnHeap(name, stmt) # it is its own region: self.region_for_var[region] = region return region def make_field_region(self, target, field): check_isinstance(target, Region) if field: # (field can be None for C++ destructors) check_isinstance(field, str) log('make_field_region(%r, %r)', target, field) if field in target.fields: log('reusing') return target.fields[field] log('not reusing') region = Region('%s.%s' % (target.name, field), target) target.fields[field] = region # it is its own region: self.region_for_var[region] = region return region def get_value_of_field_by_varname(self, varname, field): # Lookup varname.field # For use in writing selftests log('get_value_of_field_by_varname(%r, %r)', varname, field) check_isinstance(varname, str) check_isinstance(field, str) for k in self.region_for_var: if isinstance(k, gcc.VarDecl): if k.name == varname: region = self.region_for_var[k] region = self.make_field_region(region, field) value = self.value_for_region.get(region, None) return value def get_value_of_field_by_region(self, region, field): """ Lookup region->field, getting its AbstractValue, if any, or None For use in writing selftests and diagnostics, as it has no side-effects. You may want to use read_field_by_name() instead """ log('get_value_of_field_by_region(%r, %r)', region, field) check_isinstance(region, Region) check_isinstance(field, str) if field in region.fields: field_region = region.fields[field] return self.value_for_region.get(field_region, None) return None def read_field_by_name(self, stmt, gcctype, region, fieldname): """ Lookup region->field, getting its AbstractValue. If the field doesn't have a value yet, if will be set to a new UnknownValue so that subsequent reads of the field receive the *same* unknown value """ log('read_field_by_name(%r, %r)', region, fieldname) check_isinstance(stmt, gcc.Gimple) if gcctype: check_isinstance(gcctype, gcc.Type) check_isinstance(region, Region) check_isinstance(fieldname, str) v_field = self.get_value_of_field_by_region(region, fieldname) if v_field is None: v_field = UnknownValue.make(gcctype, stmt.loc) r_field = self.make_field_region(region, fieldname) self.value_for_region[r_field] = v_field return v_field def set_field_by_name(self, r_struct, fieldname, v_field): r_field = self.make_field_region(r_struct, fieldname) self.value_for_region[r_field] = v_field def dereference(self, expr, v_ptr, loc): check_isinstance(v_ptr, AbstractValue) if isinstance(v_ptr, UnknownValue): self.raise_split_value(v_ptr, loc) self.raise_any_null_ptr_deref(expr, v_ptr) check_isinstance(v_ptr, PointerToRegion) if v_ptr.region not in self.value_for_region: # Add a new UnknownValue: if v_ptr.gcctype: gcctype = v_ptr.gcctype.dereference else: gcctype = None self.value_for_region[v_ptr.region] = UnknownValue.make(gcctype, loc) return self.value_for_region[v_ptr.region] def init_for_function(self, fun): log('State.init_for_function(%r)', fun) self.fun = fun root_region = Region('root', None) stack = RegionOnStack('stack for %s' % fun.decl.name, root_region) nonnull_args = get_nonnull_arguments(fun.decl.type) for idx, parm in enumerate(fun.decl.arguments): def parm_is_this(): if idx == 0 and parm.is_artificial and parm.name == 'this': return True region = RegionForLocal(parm, stack) self.region_for_var[parm] = region if idx in nonnull_args or parm_is_this() \ or isinstance(parm.type, gcc.ReferenceType): # Make a non-NULL ptr: other = Region('region-for-arg-%r' % parm, None) self.region_for_var[other] = other self.value_for_region[region] = PointerToRegion(parm.type, parm.location, other) else: self.value_for_region[region] = UnknownValue.make(parm.type, parm.location) for local in fun.local_decls: if local.static: # Statically-allocated locals are zero-initialized before the # function is called for the first time, and then preserve # state between function calls region = RegionForStaticLocal(local) # For now, don't try to track all possible values a static var # can take; simply treat it as an UnknownValue v_local = UnknownValue.make(local.type, fun.start) else: region = RegionForLocal(local, stack) v_local = UninitializedData(local.type, fun.start) self.region_for_var[local] = region self.value_for_region[region] = v_local # Region for the gcc.ResultDecl, if any: if fun.decl.result: result = fun.decl.result region = RegionForLocal(result, stack) self.region_for_var[result] = region self.value_for_region[region] = UninitializedData(result.type, fun.start) self.verify() def mktrans_assignment(self, lhs, rhs, desc): """ Return a Transition to a state at the next location, with the RHS assigned to the LHS, if LHS is not None """ log('mktrans_assignment(%r, %r, %r)', lhs, rhs, desc) if desc: check_isinstance(desc, str) new = self.use_next_stmt_node() if lhs: new.assign(lhs, rhs, self.stmtnode.get_gcc_loc()) return Transition(self, new, desc) def update_stmt_node(self, new_stmt_node): new = self.copy() new.stmtnode = new_stmt_node if new.stmtnode.stmt and new.stmtnode.stmt.loc: new.lastgccloc = new.stmtnode.stmt.loc else: new.lastgccloc = self.lastgccloc return new def use_next_stmt_node(self): def next_stmt_node(stmtnode): if len(stmtnode.succs) != 1: raise ValueError('len(stmtnode.succs) == %i at %s' % (len(stmtnode.succs), stmtnode)) edge = list(stmtnode.succs)[0] assert edge.srcnode == stmtnode assert edge.dstnode != stmtnode return edge.dstnode new_stmt_node = next_stmt_node(self.stmtnode) return self.update_stmt_node(new_stmt_node) def get_gcc_loc_or_none(self): # Return the gcc.Location for this state, which could be None stmt = self.stmtnode.get_stmt() if stmt: return stmt.loc def get_gcc_loc(self, fun): # Return a non-None gcc.Location for this state # Some statements have None for their location, but gcc.error() etc # don't allow this. Use the end of the function for this case. stmt = self.stmtnode.get_stmt() if stmt: log('%s' % self.stmtnode.get_stmt().loc) # grrr... not all statements have a non-NULL location gccloc = self.stmtnode.get_stmt().loc if gccloc is None: assert self.lastgccloc return self.lastgccloc return gccloc else: return fun.end def raise_any_null_ptr_deref(self, expr, ptr): check_isinstance(expr, gcc.Tree) check_isinstance(ptr, AbstractValue) if isinstance(ptr, UninitializedData): raise UsageOfUninitializedData(self, expr, ptr, 'dereferencing uninitialized pointer (%s)' % expr) if ptr.is_null_ptr(): # Read through NULL # If we earlier split the analysis into NULL/non-NULL # cases, then we're only considering the possibility # that this pointer was NULL; we don't know for sure # that it was. isdefinite = not hasattr(ptr, 'fromsplit') raise NullPtrDereference(self, expr, ptr, isdefinite) def raise_any_null_ptr_func_arg(self, stmt, idx, ptr, why=None): # idx is the 0-based index of the argument check_isinstance(stmt, gcc.Gimple) check_isinstance(idx, int) check_isinstance(ptr, AbstractValue) if why: check_isinstance(why, str) if isinstance(ptr, UnknownValue): self.raise_split_value(ptr, stmt.loc) if ptr.is_null_ptr(): # NULL argument to a function that requires non-NULL # If we earlier split the analysis into NULL/non-NULL # cases, then we're only considering the possibility # that this pointer was NULL; we don't know for sure # that it was. isdefinite = not hasattr(ptr, 'fromsplit') raise NullPtrArgument(self, stmt, idx, ptr, isdefinite, why) def raise_split_value(self, ptr_rvalue, loc=None): """ Raise a SplitValue exception on the given rvalue, so that we can backtrack and split the current state into a version with an explicit NULL value and a version with a non-NULL value FIXME: we should split into multiple non-NULL values, covering the various aliasing possibilities """ check_isinstance(ptr_rvalue, AbstractValue) check_isinstance(ptr_rvalue, UnknownValue) check_isinstance(ptr_rvalue.gcctype, gcc.PointerType) global region_id region = Region('heap-region-%i' % region_id, None) region_id += 1 self.region_for_var[region] = region non_null_ptr = PointerToRegion(ptr_rvalue.gcctype, loc, region) null_ptr = ConcreteValue(ptr_rvalue.gcctype, loc, 0) raise SplitValue(ptr_rvalue, [non_null_ptr, null_ptr], [("when treating %s as non-NULL" % ptr_rvalue), ("when treating %s as NULL" % ptr_rvalue)]) def deallocate_region(self, stmt, region): # Mark the region as deallocated # Since regions are shared with other states, we have to set this up # for this state by assigning it with a special "DeallocatedMemory" # value # Clear the value for any fields within the region: for k, v in region.fields.items(): if v in self.value_for_region: del self.value_for_region[v] # Set the default value for the whole region to be "DeallocatedMemory" self.region_for_var[region] = region self.value_for_region[region] = DeallocatedMemory(None, stmt.loc) def get_transitions(self): # Return a list of Transition instances, based on input State stmt = self.stmtnode.get_stmt() if stmt: return self._get_transitions_for_stmt(stmt) else: result = [] for succedge in sorted(self.stmtnode.succs): newstate = self.copy() newstate.stmtnode = succedge.dstnode result.append(Transition(self, newstate, '')) log('result: %s', result) return result def _get_transitions_for_stmt(self, stmt): log('_get_transitions_for_stmt: %r %s', stmt, stmt) log('dir(stmt): %s', dir(stmt)) if stmt.loc: gcc.set_location(stmt.loc) if isinstance(stmt, gcc.GimpleCall): return self._get_transitions_for_GimpleCall(stmt) elif isinstance(stmt, (gcc.GimpleDebug, gcc.GimpleLabel, gcc.GimplePredict, gcc.GimpleNop)): return [Transition(self, self.use_next_stmt_node(), None)] elif isinstance(stmt, gcc.GimpleCond): return self._get_transitions_for_GimpleCond(stmt) elif isinstance(stmt, gcc.GimpleReturn): return self._get_transitions_for_GimpleReturn(stmt) elif isinstance(stmt, gcc.GimpleAssign): return self._get_transitions_for_GimpleAssign(stmt) elif isinstance(stmt, gcc.GimpleSwitch): return self._get_transitions_for_GimpleSwitch(stmt) elif isinstance(stmt, gcc.GimpleAsm): return self._get_transitions_for_GimpleAsm(stmt) else: raise NotImplementedError("Don't know how to cope with %r (%s) at %s" % (stmt, stmt, stmt.loc)) def mkstate_nop(self, stmt): """ Clone this state (at a function call), updating the location, for functions with "void" return type """ newstate = self.use_next_stmt_node() return newstate def mkstate_return_of(self, stmt, v_return): """ Clone this state (at a function call), updating the location, and setting the result of the call to the given AbstractValue """ check_isinstance(v_return, AbstractValue) newstate = self.use_next_stmt_node() if stmt.lhs: newstate.assign(stmt.lhs, v_return, stmt.loc) return newstate def mkstate_concrete_return_of(self, stmt, value): """ Clone this state (at a function call), updating the location, and setting the result of the call to the given concrete value """ check_isinstance(value, numeric_types) newstate = self.use_next_stmt_node() if stmt.lhs: newstate.assign(stmt.lhs, ConcreteValue(stmt.lhs.type, stmt.loc, value), stmt.loc) return newstate def mktrans_nop(self, stmt, fnname): """ Make a Transition for handling a function call that has no "visible" effect within our simulation (beyond advancing to the next location). [We might subsequently modify the destination state, though] """ newstate = self.use_next_stmt_node() return Transition(self, newstate, 'calling %s()' % fnname) def mktrans_not_returning(self, desc): # The function being called does not return e.g. "exit(0);" # Transition to a special noreturn state: s_new = self.copy() s_new.not_returning = True return Transition(self, s_new, desc) def mktrans_from_fncall_state(self, stmt, state, partialdesc, has_siblings): """ Given a function call here, convert a State instance into a Transition instance, marking it. """ check_isinstance(stmt, gcc.GimpleCall) check_isinstance(state, State) check_isinstance(partialdesc, str) fnname = stmt.fn.operand.name if has_siblings: desc = 'when %s() %s' % (fnname, partialdesc) else: desc = '%s() %s' % (fnname, partialdesc) return Transition(self, state, desc) def make_transitions_for_fncall(self, stmt, fnmeta, s_success, s_failure): """ Given a function call, convert a pair of State instances into a pair of Transition instances, marking one as a successful call, the other as a failed call. """ check_isinstance(stmt, gcc.GimpleCall) if fnmeta: check_isinstance(fnmeta, FnMeta) check_isinstance(s_success, State) check_isinstance(s_failure, State) if fnmeta: return [Transition(self, s_success, fnmeta.desc_when_call_succeeds()), Transition(self, s_failure, fnmeta.desc_when_call_fails())] else: return [Transition(self, s_success, 'when call succeeds'), Transition(self, s_failure, 'when call fails')] def eval_stmt_args(self, stmt): check_isinstance(stmt, gcc.GimpleCall) return [self.eval_rvalue(arg, stmt.loc) for arg in stmt.args] def _get_transitions_for_GimpleCall(self, stmt): log('stmt.lhs: %s %r', stmt.lhs, stmt.lhs) log('stmt.fn: %s %r', stmt.fn, stmt.fn) log('dir(stmt.fn): %s', dir(stmt.fn)) if hasattr(stmt.fn, 'operand'): log('stmt.fn.operand: %s', stmt.fn.operand) returntype = stmt.fn.type.dereference.type log('returntype: %s', returntype) if stmt.noreturn: # The function being called does not return e.g. "exit(0);" # Transition to a special noreturn state: return [self.mktrans_not_returning('not returning from %s' % stmt.fn)] if isinstance(stmt.fn, (gcc.VarDecl, gcc.ParmDecl, gcc.SsaName)): # Calling through a function pointer: val = self.eval_rvalue(stmt.fn, stmt.loc) log('val: %s', val) check_isinstance(val, AbstractValue) return val.get_transitions_for_function_call(self, stmt) # Evaluate the arguments: args = self.eval_stmt_args(stmt) # Check for uninitialized and deallocated data: for i, arg in enumerate(args): if isinstance(arg, UninitializedData): raise UsageOfUninitializedData(self, stmt.args[i], arg, 'passing uninitialized data (%s) as argument %i to function' % (stmt.args[i], i + 1)) if isinstance(arg, PointerToRegion): rvalue = self.value_for_region.get(arg.region, None) if isinstance(rvalue, DeallocatedMemory): raise PassingPointerToDeallocatedMemory(i, 'function', stmt, rvalue) if isinstance(stmt.fn.operand, gcc.FunctionDecl): log('dir(stmt.fn.operand): %s', dir(stmt.fn.operand)) log('stmt.fn.operand.name: %r', stmt.fn.operand.name) fnname = stmt.fn.operand.name # Hand off to impl_* methods of facets, where these methods exist # In each case, the method should have the form: # def impl_foo(self, stmt, v_arg0, v_arg1, *args): # for a C function named "foo" i.e. it takes "self", plus the # gcc.GimpleCall statement, followed by the AbstractValue instances # for the evaluated arguments (which for some functions will # involve varargs, like above). # They should return a list of Transition instances. methname = 'impl_%s' % fnname for key in self.facets: facet = getattr(self, key) if hasattr(facet, methname): meth = getattr(facet, 'impl_%s' % fnname) # Call the facet's method: return meth(stmt, *args) #from libcpychecker.c_stdio import c_stdio_functions, handle_c_stdio_function #if fnname in c_stdio_functions: # return handle_c_stdio_function(self, fnname, stmt) if 0: # For extending coverage of the Python API: # Detect and complain about Python API entrypoints that # weren't explicitly handled if fnname.startswith('_Py') or fnname.startswith('Py'): raise NotImplementedError('not yet implemented: %s' % fnname) # Unknown function returning (PyObject*): from libcpychecker.refcounts import type_is_pyobjptr_subclass if type_is_pyobjptr_subclass(stmt.fn.operand.type.type): log('Invocation of unknown function returning PyObject * (or subclass): %r' % fnname) fnmeta = FnMeta(name=fnname) # Assume that all such functions either: # - return a new reference, or # - return NULL and set an exception (e.g. MemoryError) from libcpychecker.attributes import fnnames_returning_borrowed_refs if fnname in fnnames_returning_borrowed_refs: # The function being called was marked as returning a # borrowed ref, rather than a new ref: return self.apply_fncall_side_effects( self.cpython.make_transitions_for_borrowed_ref_or_fail(stmt, fnmeta), stmt) return self.apply_fncall_side_effects( self.cpython.make_transitions_for_new_ref_or_fail(stmt, fnmeta, 'new ref from (unknown) %s' % fnname), stmt) # GCC builtins: if fnname == '__builtin_expect': # http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html # The return value of: # __builtin_expect(long exp, long c) # is "exp" (the 0-th argument): return [self.mktrans_assignment(stmt.lhs, stmt.args[0], None)] # Unknown function of other type: log('Invocation of unknown function: %r', fnname) return self.apply_fncall_side_effects( [self.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), None)], stmt) log('stmt.args: %s %r', stmt.args, stmt.args) for i, arg in enumerate(stmt.args): log('args[%i]: %s %r', i, arg, arg) def get_function_name(self, stmt): """ Try to get the function name for a gcc.GimpleCall statement as a string, or None if we're unable to determine it. For a simple function invocation this is easy, but if we're calling through a function pointer we may or may not know. """ check_isinstance(stmt, gcc.GimpleCall) v_fn = self.eval_rvalue(stmt.fn, stmt.loc) if isinstance(v_fn, PointerToRegion): if isinstance(v_fn.region, RegionForGlobal): if isinstance(v_fn.region.vardecl, gcc.FunctionDecl): return v_fn.region.vardecl.name # Unable to determine it: return None def apply_fncall_side_effects(self, transitions, stmt): """ Given a list of Transition instances for a call to a function with unknown side-effects, modify all of the destination states. Specifically: any pointer arguments to the function are modified in the destination states to be an UnknownValue, given that the function could have written an arbitrary r-value back into the input """ check_isinstance(transitions, list) check_isinstance(stmt, gcc.GimpleCall) args = self.eval_stmt_args(stmt) fnname = self.get_function_name(stmt) # cpython: handle functions marked as stealing references to their # arguments: from libcpychecker.attributes import stolen_refs_by_fnname if fnname in stolen_refs_by_fnname: for t_iter in transitions: check_isinstance(t_iter, Transition) for argindex in stolen_refs_by_fnname[stmt.fn.operand.name]: v_arg = args[argindex-1] if isinstance(v_arg, PointerToRegion): t_iter.dest.cpython.steal_reference(v_arg, stmt.loc) # cpython: handle functions that have been marked as setting the # exception state: from libcpychecker.attributes import fnnames_setting_exception if fnname in fnnames_setting_exception: for t_iter in transitions: # Mark the global exception state (with an arbitrary # error): t_iter.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) # cpython: handle functions that have been marked as setting the # exception state when they return a negative value: from libcpychecker.attributes import fnnames_setting_exception_on_negative_result if fnname in fnnames_setting_exception_on_negative_result: def handle_negative_return(t_iter): check_isinstance(t_iter, Transition) check_isinstance(t_iter.src, State) check_isinstance(stmt, gcc.GimpleCall) if stmt.lhs: v_returnval = t_iter.dest.eval_rvalue(stmt.lhs, stmt.loc) # This could raise a SplitValue exception: # the split value affects State instances that are already # within the trace, whereas we're splitting on a new value # that only exists within a new State. # Hence we have to do this within # process_splittable_transitions # so that we can split the new state: eqzero = v_returnval.eval_comparison( 'lt', ConcreteValue.from_int(0), None) if eqzero is True: # Mark the global exception state (with an arbitrary # error): t_iter.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) transitions = process_splittable_transitions(transitions, handle_negative_return) for t_iter in transitions: check_isinstance(t_iter, Transition) for v_arg in args: if isinstance(v_arg, PointerToRegion): v_newval = UnknownValue.make(v_arg.gcctype, stmt.loc) t_iter.dest.value_for_region[v_arg.region] = v_newval return transitions def _get_transitions_for_GimpleCond(self, stmt): def make_transition_for_true(stmt, has_siblings): e = true_edge(self.stmtnode) assert e nextstate = self.update_stmt_node(e.dstnode) nextstate.prior_bool = True if has_siblings: desc = 'when taking True path' else: desc = 'taking True path' return Transition(self, nextstate, desc) def make_transition_for_false(stmt, has_siblings): e = false_edge(self.stmtnode) assert e nextstate = self.update_stmt_node(e.dstnode) nextstate.prior_bool = False if has_siblings: desc = 'when taking False path' else: desc = 'taking False path' return Transition(self, nextstate, desc) log('stmt.exprcode: %s', stmt.exprcode) log('stmt.exprtype: %s', stmt.exprtype) log('stmt.lhs: %r %s', stmt.lhs, stmt.lhs) log('stmt.rhs: %r %s', stmt.rhs, stmt.rhs) boolval = self.eval_condition(stmt, stmt.lhs, stmt.exprcode, stmt.rhs) if boolval is True: log('taking True edge') nextstate = make_transition_for_true(stmt, False) return [nextstate] elif boolval is False: log('taking False edge') nextstate = make_transition_for_false(stmt, False) return [nextstate] else: check_isinstance(boolval, UnknownValue) # We don't have enough information; both branches are possible: return [make_transition_for_true(stmt, True), make_transition_for_false(stmt, True)] def eval_condition(self, stmt, expr_lhs, exprcode, expr_rhs): """ Evaluate a comparison, returning one of True, False, or None """ log('eval_condition: %s %s %s ', expr_lhs, exprcode, expr_rhs) check_isinstance(expr_lhs, gcc.Tree) check_isinstance(exprcode, type) # it's a type, rather than an instance check_isinstance(expr_rhs, gcc.Tree) lhs = self.eval_rvalue(expr_lhs, stmt.loc) rhs = self.eval_rvalue(expr_rhs, stmt.loc) check_isinstance(lhs, AbstractValue) check_isinstance(rhs, AbstractValue) # Detect usage of uninitialized data: if isinstance(lhs, UninitializedData): raise UsageOfUninitializedData(self, expr_lhs, lhs, 'comparison against uninitialized data (%s)' % expr_lhs) if isinstance(rhs, UninitializedData): raise UsageOfUninitializedData(self, expr_rhs, rhs, 'comparison against uninitialized data (%s)' % expr_rhs) if exprcode == gcc.EqExpr: result = lhs.eval_comparison('eq', rhs, expr_rhs) if result is not None: return result elif exprcode == gcc.NeExpr: result = lhs.eval_comparison('eq', rhs, expr_rhs) if result is not None: return not result elif exprcode == gcc.LtExpr: result = lhs.eval_comparison('lt', rhs, expr_rhs) if result is not None: return result elif exprcode == gcc.LeExpr: result = lhs.eval_comparison('le', rhs, expr_rhs) if result is not None: return result elif exprcode == gcc.GeExpr: result = lhs.eval_comparison('ge', rhs, expr_rhs) if result is not None: return result elif exprcode == gcc.GtExpr: result = lhs.eval_comparison('gt', rhs, expr_rhs) if result is not None: return result # Specialcasing: comparison of unknown ptr with NULL: if (isinstance(expr_lhs, gcc.VarDecl) and isinstance(expr_rhs, gcc.IntegerCst) and isinstance(expr_lhs.type, gcc.PointerType)): # Split the ptr variable immediately into NULL and non-NULL # versions, so that we can evaluate the true and false branch with # explicitly data log('splitting %s into non-NULL/NULL pointers', expr_lhs) self.raise_split_value(lhs, stmt.loc) log('unable to compare %r with %r', lhs, rhs) #raise NotImplementedError("Don't know how to do %s comparison of %s with %s" # % (exprcode, lhs, rhs)) return UnknownValue(stmt.lhs.type, stmt.loc) def eval_binop_args(self, stmt): rhs = stmt.rhs a = self.eval_rvalue(rhs[0], stmt.loc) b = self.eval_rvalue(rhs[1], stmt.loc) log('a: %r', a) log('b: %r', b) return a, b def eval_rhs(self, stmt): log('eval_rhs(%s): %s', stmt, stmt.rhs) rhs = stmt.rhs # Handle arithmetic and boolean expressions: if stmt.exprcode in (gcc.PlusExpr, gcc.MinusExpr, gcc.MultExpr, gcc.TruncDivExpr, gcc.TruncModExpr, gcc.RdivExpr, gcc.ExactDivExpr, gcc.MaxExpr, gcc.MinExpr, gcc.BitIorExpr, gcc.BitAndExpr, gcc.BitXorExpr, gcc.LshiftExpr, gcc.RshiftExpr, gcc.TruthAndExpr, gcc.TruthOrExpr ): a, b = self.eval_binop_args(stmt) if isinstance(a, UninitializedData): raise UsageOfUninitializedData(self, stmt.rhs[0], a, 'usage of uninitialized data (%s) on left-hand side of %s' % (stmt.rhs[0], stmt.exprcode.get_symbol())) if isinstance(b, UninitializedData): raise UsageOfUninitializedData(self, stmt.rhs[1], b, 'usage of uninitialized data (%s) on right-hand side of %s' % (stmt.rhs[0], stmt.exprcode.get_symbol())) try: c = a.eval_binop(stmt.exprcode, b, rhs[1], stmt.lhs.type, stmt.loc) check_isinstance(c, AbstractValue) return c except NotImplementedError: return UnknownValue.make(stmt.lhs.type, stmt.loc) elif stmt.exprcode == gcc.ComponentRef: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.VarDecl: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.ParmDecl: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.IntegerCst: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.RealCst: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.StringCst: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.AddrExpr: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.NopExpr: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.ArrayRef: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.MemRef: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.PointerPlusExpr: try: region = self.pointer_plus_region(stmt) return PointerToRegion(stmt.lhs.type, stmt.loc, region) except NotImplementedError: return UnknownValue.make(stmt.lhs.type, stmt.loc) elif stmt.exprcode in (gcc.EqExpr, gcc.NeExpr, gcc.LtExpr, gcc.LeExpr, gcc.GeExpr, gcc.GtExpr): # Comparisons result = self.eval_condition(stmt, rhs[0], stmt.exprcode, rhs[1]) if result is not None: return ConcreteValue(stmt.lhs.type, stmt.loc, 1 if result else 0) else: return UnknownValue.make(stmt.lhs.type, stmt.loc) # Unary expressions: elif stmt.exprcode in (gcc.AbsExpr, gcc.BitNotExpr, gcc.ConvertExpr, gcc.NegateExpr, gcc.FixTruncExpr, gcc.FloatExpr): v_rhs = self.eval_rvalue(stmt.rhs[0], stmt.loc) return v_rhs.eval_unary_op(stmt.exprcode, stmt.lhs.type, stmt.loc) elif stmt.exprcode == gcc.BitFieldRef: return self.eval_rvalue(rhs[0], stmt.loc) elif stmt.exprcode == gcc.Constructor: # Default value for whole array becomes 0: return ConcreteValue(stmt.lhs.type, stmt.loc, 0) else: raise NotImplementedError("Don't know how to cope with exprcode: %r (%s) at %s" % (stmt.exprcode, stmt.exprcode, stmt.loc)) def _get_transitions_for_GimpleAssign(self, stmt): log('stmt.lhs: %r %s', stmt.lhs, stmt.lhs) log('stmt.rhs: %r %s', stmt.rhs, stmt.rhs) log('stmt: %r %s', stmt, stmt) log('stmt.exprcode: %r', stmt.exprcode) value = self.eval_rhs(stmt) log('value from eval_rhs: %r', value) check_isinstance(value, AbstractValue) if isinstance(value, DeallocatedMemory): raise ReadFromDeallocatedMemory(stmt, value) nextstate = self.use_next_stmt_node() return [self.mktrans_assignment(stmt.lhs, value, None)] def _get_transitions_for_GimpleReturn(self, stmt): #log('stmt.lhs: %r %s', stmt.lhs, stmt.lhs) #log('stmt.rhs: %r %s', stmt.rhs, stmt.rhs) log('stmt: %r %s', stmt, stmt) log('stmt.retval: %r', stmt.retval) nextstate = self.copy() if stmt.retval: rvalue = self.eval_rvalue(stmt.retval, stmt.loc) log('rvalue from eval_rvalue: %r', rvalue) nextstate.return_rvalue = rvalue nextstate.has_returned = True return [Transition(self, nextstate, 'returning')] def _get_transitions_for_GimpleSwitch(self, stmt): def get_labels_for_rvalue(self, stmt, rvalue): # Gather all possible labels for the given rvalue result = [] for label in stmt.labels: # FIXME: for now, treat all labels as possible: result.append(label) return result log('stmt.indexvar: %r', stmt.indexvar) log('stmt.labels: %r', stmt.labels) indexval = self.eval_rvalue(stmt.indexvar, stmt.loc) log('indexval: %r', indexval) labels = get_labels_for_rvalue(self, stmt, indexval) log('labels: %r', labels) result = [] for label in labels: newstate = self.copy() bb = self.stmtgraph.fun.cfg.get_block_for_label(label.target) newstate.stmtnode = self.stmtgraph.entry_of_bb[bb] if label.low: check_isinstance(label.low, gcc.IntegerCst) if label.high: check_isinstance(label.high, gcc.IntegerCst) desc = 'when following cases %i...%i' % (label.low.constant, label.high.constant) else: desc = 'when following case %i' % label.low.constant else: desc = 'when following default' result.append(Transition(self, newstate, desc)) return result def _get_transitions_for_GimpleAsm(self, stmt): log('stmt: %r %s', stmt, stmt) if stmt.string == '': # Empty fragment of inline assembler: s_next = self.use_next_stmt_node() return [Transition(self, s_next, None)] raise NotImplementedError('Unable to handle inline assembler: %s' % stmt.string) def get_persistent_refs_for_region(self, dst_region): # Locate all regions containing pointers that point at the given region # that are either on the heap or are globals (not locals) check_isinstance(dst_region, Region) result = [] for src_region in self.get_all_refs_for_region(dst_region): if src_region.is_on_stack(): continue result.append(src_region) return result def get_all_refs_for_region(self, dst_region): # Locate all regions containing pointers that point at the given region check_isinstance(dst_region, Region) result = [] for src_region in self.value_for_region: v = self.value_for_region[src_region] if isinstance(v, PointerToRegion): if v.region == dst_region: result.append(src_region) return result region_id = 0 class Transition(object): __slots__ = ('src', # State 'dest', # State 'desc', # str ) def __init__(self, src, dest, desc): check_isinstance(src, State) check_isinstance(dest, State) if desc: check_isinstance(desc, str) self.src = src self.dest = dest self.desc = desc def __repr__(self): return 'Transition(%r, %r)' % (self.dest, self.desc) def log(self, logger): logger('desc: %r' % self.desc) logger('dest:') self.dest.log(logger) class Trace(object): __slots__ = ('states', 'transitions', 'err', 'paths_taken') """A sequence of States and Transitions""" def __init__(self): self.states = [] self.transitions = [] self.err = None # A list of (src gcc.StmtNode, dest gcc.StmtNode) pairs self.paths_taken = [] def add(self, transition): check_isinstance(transition, Transition) self.states.append(transition.dest) self.transitions.append(transition) if transition.src.stmtnode.bb != transition.dest.stmtnode.bb: self.paths_taken.append( (transition.src.stmtnode.bb, transition.dest.stmtnode.bb) ) return self def add_error(self, err): self.err = err def copy(self): t = Trace() t.states = self.states[:] t.transitions = self.transitions[:] t.err = self.err # FIXME: should this be a copy? t.paths_taken = self.paths_taken[:] return t def log(self, logger, name): logger('%s:' % name) for i, state in enumerate(self.states): logger('%i:' % i) state.log(logger) if self.err: logger(' Trace ended with error: %s' % self.err) def get_last_stmt(self): return self.states[-1].stmtnode.get_stmt() def return_value(self): return self.states[-1].return_rvalue def has_looped(self): """ Is the tail transition a path we've followed before? """ endstate = self.states[-1] if hasattr(endstate, 'fromsplit'): # We have a state that was created from a SplitValue. It will have # the same location as the state before it (before the split). # Don't treat it as a loop: return False if endstate.not_returning: # The handler not "exit" etc leads to a transition that has a # repeated location: return False endtransition = self.transitions[-1] if 0: gcc.inform(endstate.get_gcc_loc(endstate.fun), ('paths_taken: %s' % (self.paths_taken,))) gcc.inform(endstate.get_gcc_loc(endstate.fun), 'src, loc: %s' % ((endtransition.src.loc, endtransition.dest.loc),)) # Is this a path we've followed before? src_bb = endtransition.src.stmtnode.bb dest_bb = endtransition.dest.stmtnode.bb if src_bb != dest_bb: if (src_bb, dest_bb) in self.paths_taken[0:-1]: return True def get_all_var_region_pairs(self): """ Get the set of all (LHS,region) pairs in region_for_var within all of the states in this trace, without duplicates """ result = set() for s_iter in self.states: for var_iter, r_iter in s_iter.region_for_var.items(): pair = (var_iter, r_iter) result.add(pair) return result def var_points_unambiguously_to(self, r_srcptr, r_dstptr): """ Does the source region (a pointer variable) always point to the destination region (or be NULL, or uninitialized) throughout all of the states in this trace? """ ever_had_value = False #print('r_srcptr, r_dstptr: %r, %r' % (r_srcptr, r_dstptr)) for s_iter in self.states: if r_srcptr not in s_iter.value_for_region: continue v_srcptr = s_iter.value_for_region[r_srcptr] #print ('v_srcptr: %s' % v_srcptr) # It doesn't matter if it's uninitialized, or NULL: if isinstance(v_srcptr, UninitializedData): continue if v_srcptr.is_null_ptr(): continue if isinstance(v_srcptr, PointerToRegion): if v_srcptr.region == r_dstptr: ever_had_value = True continue else: # This variable is pointing at another region at # this point within the trace: return False # Some kind of value we weren't expecting: return False # If we get here, there was no state in which the var pointed to # anything else. # # If it ever pointed to the region in question, then it's a good way # of referring to the region: return ever_had_value def get_description_for_region(self, r_in): """ Try to come up with a human-readable description of the input region """ check_isinstance(r_in, Region) # If a local pointer variable has just the given region as a value (as # well as its initial "uninitialized" or NULL states), then that's a # good name for this region: for var_iter, r_iter in self.get_all_var_region_pairs(): if self.var_points_unambiguously_to(r_iter, r_in): if isinstance(r_iter, (RegionForLocal, RegionForGlobal)): # Only do it for variables with names, not for temporaries: if r_iter.vardecl.name: return "'*%s'" % r_iter.vardecl.name # Otherwise, just use the name of the region return r_in.name def true_edge(stmtnode): for e in stmtnode.succs: if e.true_value: return e def false_edge(stmtnode): for e in stmtnode.succs: if e.false_value: return e def process_splittable_transitions(transitions, callback): """ Apply a processing function to each Transition in transitions, handling the case where a SplitValue exception is raised by splitting the destination states. Return a new list of Transition instances: which will be the old Transition objects, potentially with additional Transition instances if any have been split """ newtransitions = [] for t_iter in transitions: try: callback(t_iter) newtransitions.append(t_iter) except SplitValue: err = sys.exc_info()[1] splittransitions = err.split(t_iter.dest) check_isinstance(splittransitions, list) # Recurse: newtransitions += process_splittable_transitions(splittransitions, callback) return newtransitions class Resources: # Resource tracking for a state def __init__(self): # Resources that we've acquired: self._acquisitions = [] # Resources that we've released: self._releases = [] def copy(self): new = Resources() new._acquisitions = self._acquisitions[:] new._releases = self._releases[:] return new def acquire(self, resource): self._acquisitions.append(resource) def release(self, resource): self._releases.append(resource) def log(self, logger): logger('resources:') logger('acquisitions: %s' % self._acquisitions) logger('releases: %s' % self._releases) class TooComplicated(Exception): """ The function is too complicated for the checker to analyze. We have a list of Trace instances, each of which is "complete" in the sense that it fully captures one path through the function. However, we know that the list itself is incomplete: it's not the full list of all possible traces. """ def __init__(self, complete_traces): check_isinstance(complete_traces, list) self.complete_traces = complete_traces class Limits: """ Resource limits, to avoid an analysis going out of control """ def __init__(self, maxtrans): self.maxtrans = maxtrans self.trans_seen = 0 def on_transition(self, transition, result): """ result is a list of all *complete* traces so far """ if 0: print('%s -> %s' % (transition.src.stmtnode, transition.dest.stmtnode)) self.trans_seen += 1 if self.trans_seen > self.maxtrans: raise TooComplicated(result) def iter_traces(stmtgraph, facets, prefix=None, limits=None, depth=0): """ Traverse the tree of traces of program state, returning a list of Trace instances. For now, don't include any traces that contain loops, as a primitive way of ensuring termination of the analysis This is recursive, setting up a depth-first traversal of the state tree. If it's interrupted by a TooComplicated exception, we should at least capture an incomplete list of paths down to some of the bottoms of the tree. """ fun = stmtgraph.fun log('iter_traces(%r, %r, %r)', fun, facets, prefix) if prefix is None: prefix = Trace() curstate = State(stmtgraph, stmtgraph.get_entry_nodes()[0], None, facets, None, None, None) #Resources()) curstate.init_for_function(fun) for key in facets: facet_cls = facets[key] f_new = facet_cls(curstate, fun=fun) setattr(curstate, key, f_new) f_new.init_for_function(fun) else: check_isinstance(prefix, Trace) curstate = prefix.states[-1] if curstate.has_returned: # This state has returned a value (and hence terminated): return [prefix] if curstate.not_returning: # This state has called "exit" or similar, and thus this # trace should terminate: return [prefix] # Stop interpreting when you see a loop, to ensure termination: if prefix.has_looped(): log('loop detected; stopping iteration') if 0: gcc.inform(curstate.get_gcc_loc(fun), 'loop detected; stopping iteration') # Don't return the prefix so far: it is not a complete trace return [] # We need the prevstate in order to handle Phi nodes if len(prefix.states) > 1: prevstate = prefix.states[-2] else: prevstate = None prefix.log(log, 'PREFIX') log(' %s:%s', fun.decl.name, curstate.stmtnode) try: transitions = curstate.get_transitions() check_isinstance(transitions, list) except PredictedError: # We're at a terminating state: err = sys.exc_info()[1] err.loc = prefix.get_last_stmt().loc trace_with_err = prefix.copy() trace_with_err.add_error(err) trace_with_err.log(log, 'FINISHED TRACE WITH ERROR: %s' % err) return [trace_with_err] except SplitValue: # Split the state up, splitting into parallel worlds with different # values for the given value # FIXME: this doesn't work; it thinks it's a loop :( err = sys.exc_info()[1] transitions = err.split(curstate) check_isinstance(transitions, list) log('transitions: %s', transitions) if len(transitions) > 0: result = [] for transition in transitions: check_isinstance(transition, Transition) transition.dest.verify() # Potentially raise a TooComplicated exception: if limits: limits.on_transition(transition, result) newprefix = prefix.copy().add(transition) # Recurse # This gives us a depth-first traversal of the state tree try: for trace in iter_traces(stmtgraph, facets, newprefix, limits, depth + 1): result.append(trace) except TooComplicated: err = sys.exc_info()[1] traces = err.complete_traces traces += result raise TooComplicated(traces) return result else: # We're at a terminating state: prefix.log(log, 'FINISHED TRACE') return [prefix] class StateGraph: """ A graph of states, representing the various routes through a function, tracking state. For now, we give up when we encounter a loop, as an easy way to ensure termination of the analysis """ def __init__(self, fun, logger, stateclass): check_isinstance(fun, gcc.Function) self.fun = fun self.states = [] self.transitions = [] self.stateclass = stateclass logger('StateGraph.__init__(%r)' % fun) # Recursively gather states: initial = stateclass(Location.get_block_start(fun.cfg.entry), None, None, None, Resources(), ConcreteValue(get_PyObjectPtr(), fun.start, 0)) initial.init_for_function(fun) self.states.append(initial) self._gather_states(initial, logger) def _gather_states(self, curstate, logger): logger(' %s:%s' % (self.fun.decl.name, curstate.stmtnode)) try: transitions = curstate.get_transitions() #print transitions check_isinstance(transitions, list) except PredictedError: # We're at a terminating state: err = sys.exc_info()[1] errstate = curstate.copy() transition = Transition(curstate, errstate, str(err)) self.states.append(transition.dest) self.transitions.append(transition) return logger('transitions:', 2) for t in transitions: t.log(logger, 3) if len(transitions) > 0: for transition in transitions: # FIXME: what about loops??? check_isinstance(transition, Transition) self.states.append(transition.dest) self.transitions.append(transition) if transition.dest.has_returned(): # This state has returned a value (and hence terminated) continue if transition.dest.not_returning(): # This state has called "exit" or similar, and thus this # trace should terminate: continue # Recurse: self._gather_states(transition.dest, logger) else: # We're at a terminating state: logger('FINISHED TRACE') def get_prev_state(self, state): assert state in self.states for t in self.transitions: if t.dest == state: return t.src # Not found: return None def extra_text(msg, indent): sys.stderr.write('%s%s\n' % (' ' * indent, msg)) gcc-python-plugin-0.17/libcpychecker/attributes.py000066400000000000000000000122411342215241600223570ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . import gcc from gccutils import check_isinstance from libcpychecker.types import register_type_object # Recorded attribute data, primed with some special-case knowledge about # the code that Cython and SWIG generate: fnnames_returning_borrowed_refs = set([ '__Pyx_GetStdout', 'SWIG_Python_ErrorType', # returns a borrowed ref to one of the global exception objects ]) fnnames_setting_exception = set() fnnames_setting_exception_on_negative_result = set() # A dictionary mapping from fnname to set of argument indices: stolen_refs_by_fnname = {} def register_our_attributes(): # Callback, called by the gcc.PLUGIN_ATTRIBUTES event # Handler for __attribute__((cpychecker_returns_borrowed_ref)) # and #ifdef WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE def attribute_callback_for_returns_borrowed_ref(*args): if 0: print('attribute_callback_for_returns_borrowed_ref(%r)' % args) check_isinstance(args[0], gcc.FunctionDecl) fnname = args[0].name fnnames_returning_borrowed_refs.add(fnname) gcc.register_attribute('cpychecker_returns_borrowed_ref', 0, 0, False, False, False, attribute_callback_for_returns_borrowed_ref) gcc.define_macro('WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE') # Handler for __attribute__((cpychecker_steals_reference_to_arg(n))) # and #ifdef WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE def attribute_callback_for_steals_reference_to_arg(*args): if 0: print('attribute_callback_for_steals_reference_to_arg(%r)' % (args, )) check_isinstance(args[0], gcc.FunctionDecl) check_isinstance(args[1], gcc.IntegerCst) fnname = args[0].name argindex = int(args[1].constant) if fnname in stolen_refs_by_fnname: stolen_refs_by_fnname[fnname].add(argindex) else: stolen_refs_by_fnname[fnname] = set([argindex]) gcc.register_attribute('cpychecker_steals_reference_to_arg', 1, 1, False, False, False, attribute_callback_for_steals_reference_to_arg) gcc.define_macro('WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE') # Handler for __attribute__((cpychecker_type_object_for_struct(type))) # and #ifdef WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE def attribute_callback_type_object_for_typedef(*args): if 0: print('attribute_callback_type_object_for_typedef(%r)' % (args, )) check_isinstance(args[0], gcc.VarDecl) check_isinstance(args[1], gcc.StringCst) typedef_name = args[1].constant register_type_object(args[0], typedef_name) gcc.register_attribute('cpychecker_type_object_for_typedef', 1, 1, False, False, False, attribute_callback_type_object_for_typedef) gcc.define_macro('WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE') # Handler for __attribute__((cpychecker_sets_exception)) # and #ifdef WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE def attribute_callback_for_sets_exception(*args): if 0: print('attribute_callback_for_sets_exception(%r)' % args) check_isinstance(args[0], gcc.FunctionDecl) fnname = args[0].name fnnames_setting_exception.add(fnname) gcc.register_attribute('cpychecker_sets_exception', 0, 0, False, False, False, attribute_callback_for_sets_exception) gcc.define_macro('WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE') # Handler for __attribute__((cpychecker_negative_result_sets_exception)) # and #ifdef WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE def attribute_callback_for_negative_result_sets_exception(*args): if 0: print('attribute_callback_for_negative_result_sets_exception(%r)' % args) check_isinstance(args[0], gcc.FunctionDecl) fnname = args[0].name fnnames_setting_exception_on_negative_result.add(fnname) gcc.register_attribute('cpychecker_negative_result_sets_exception', 0, 0, False, False, False, attribute_callback_for_negative_result_sets_exception) gcc.define_macro('WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE') gcc-python-plugin-0.17/libcpychecker/c_stdio.py000066400000000000000000000056171342215241600216260ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Checking of C's stdio functions # Only partial coverage so var import gcc from absinterp import AbstractValue, ConcreteValue, InvalidlyNullParameter class InternalCheckerError(Exception): pass class UnrecognizedFunction(InternalCheckerError): def __init__(self, fnname): self.fnname = fnname def __str__(self): return 'Unrecognized function: %r' % self.fnname c_stdio_functions = [ 'fopen', 'fclose', ] class NonNullFilePtr(AbstractValue): def __init__(self, stmt): self.stmt = stmt def __str__(self): return 'non-NULL (FILE*) acquired at %s' % self.stmt def __repr__(self): return 'NonNullFilePtr(%r)' % self.stmt def handle_c_stdio_function(state, fnname, stmt): if fnname == 'fopen': # The "success" case: file_ptr = NonNullFilePtr(stmt) success = state.make_assignment(stmt.lhs, file_ptr, '%s() succeeded' % fnname) success.dest.acquire(file_ptr) # The "failure" case: failure = state.make_assignment(stmt.lhs, NullPtrValue(stmt), '%s() failed' % fnname) return [success, failure] elif fnname == 'fclose': expr = state.eval_expr(stmt.args[0]) if isinstance(expr, NonNullFilePtr): result = state.make_assignment(stmt.lhs, AbstractValue(gcc.Type.int(), stmt), '%s() succeeded' % fnname) # FIXME errno handling! result.release(expr) return [result] elif isinstance(expr, NullPtrValue): raise InvalidlyNullParameter(fnname, 1, expr) else: result = state.make_assignment(stmt.lhs, AbstractValue(gcc.Type.int(), stmt), '%s() succeeded' % fnname) # FIXME errno handling! result.dest.release(expr) return [result] else: # We claimed to handle this function, but didn't: raise UnrecognizedFunction(fnname) gcc-python-plugin-0.17/libcpychecker/compat.py000066400000000000000000000064431342215241600214630ustar00rootroot00000000000000# Copyright 2012 David Malcolm # Copyright 2012 Red Hat, Inc. # # This 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 # . # Code for handling compatibility between different GCC versions import gcc import gccutils # The checker need to be able to access global declarations for exception # objects, such as: # PyAPI_DATA(PyObject *) PyExc_MemoryError; # and of type objects, such as: # PyAPI_DATA(PyTypeObject) PyList_Type; # # Originally we did this using gccutils.get_global_vardecl_by_name(), which # walks the translation units' top-level blocks looking for gcc.VarDecl # Unfortunately as of GCC PR debug/51410 (in 4.7 onwards), those that aren't # directly referenced by the code being compiled get stripped (to condense # the debug data), and so we can't see them anymore. # However, GCC 4.7 gained a PLUGIN_FINISH_DECL event, so we can use that # instead to gather the decls for later use. # # See https://fedorahosted.org/gcc-python-plugin/ticket/21 class CouldNotFindVarDecl(RuntimeError): def __init__(self, varname): self.varname = varname def __str__(self): return ('could not find expected global variable %r' % self.varname) if hasattr(gcc, 'PLUGIN_FINISH_DECL'): # GCC 4.7 and later global_exceptions = {} global_typeobjs = {} def on_finish_decl(*args): # GCC 4.7 and later: callback to the PLUGIN_FINISH_DECL event # print(args) # FIXME: why two args? global global_exceptions global global_typeobjs decl = args[0] if isinstance(decl, gcc.VarDecl): if decl.name: if decl.name.startswith('PyExc_'): global_exceptions[decl.name] = decl if decl.name.endswith('_Type'): global_typeobjs[decl.name] = decl def _get_exception_decl_by_name(exc_name): return global_exceptions[exc_name] def _get_typeobject_decl_by_name(typeobjname): return global_typeobjs[typeobjname] else: # GCC 4.6 doesn't have PLUGIN_FINISH_DECL, but # gccutils.get_global_vardecl_by_name() finds the declarations we need def _get_exception_decl_by_name(exc_name): return gccutils.get_global_vardecl_by_name(exc_name) def _get_typeobject_decl_by_name(typeobjname): return gccutils.get_global_vardecl_by_name(typeobjname) def get_exception_decl_by_name(exc_name): exc_decl = _get_exception_decl_by_name(exc_name) if not exc_decl: raise CouldNotFindVarDecl(exc_name) return exc_decl def get_typeobject_decl_by_name(typeobjname): typeobjdecl = _get_typeobject_decl_by_name(typeobjname) if not typeobjdecl: raise CouldNotFindVarDecl(typeobjname) return typeobjdecl gcc-python-plugin-0.17/libcpychecker/diagnostics.py000066400000000000000000000244001342215241600225000ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . """ Error reporting interface, supporting regular GCC messages plus higher-level HTML visualizations, with de-duplication. GCC diagnostic messages are buffered up within Report instances and eventually flushed, allowing us to de-duplicate error reports. """ import gcc from gccutils import get_src_for_loc, check_isinstance from libcpychecker.visualizations import HtmlRenderer from libcpychecker.utils import log class Annotator: """ A collection of hooks for use when describing a trace (either as text, or as an HTML report) This allows us to add the annotations to the correct place in the textual stream when reporting on flow through a function """ def get_notes(self, transition): """ Return a list of Note instances giving extra information about the transition """ raise NotImplementedError class TestAnnotator(Annotator): """ A sample annotator that adds information to the trace on movement between gimple statements """ def get_notes(self, transition): result = [] srcloc = transition.src.get_gcc_loc_or_none() if srcloc: if transition.src.loc != transition.dest.loc: result.append(Note(srcloc, ('transition from "%s" to "%s"' % (transition.src.loc.get_stmt(), transition.dest.loc.get_stmt())))) return result def describe_trace(trace, report, annotator): """ Buffer up more details about the path through the function that leads to the error, using report.add_inform() """ awaiting_target = None for t in trace.transitions: log('transition: %s', t) srcloc = t.src.get_gcc_loc_or_none() if t.desc: if srcloc: report.add_inform(t.src.get_gcc_loc(report.fun), ('%s at: %s' % (t.desc, get_src_for_loc(srcloc)))) else: report.add_inform(t.src.get_gcc_loc(report.fun), '%s' % t.desc) if t.src.stmtnode.bb != t.dest.stmtnode.bb: # Tell the user where conditionals reach: destloc = t.dest.get_gcc_loc_or_none() if destloc: report.add_inform(destloc, 'reaching: %s' % get_src_for_loc(destloc)) if annotator: notes = annotator.get_notes(t) for note in notes: if note.loc and note.loc == srcloc: report.add_inform(note.loc, note.msg) class Reporter: """ Error-reporting interface. Gathers information, sending it to GCC's regular diagnostic interface, but also storing it for e.g. HTML dumps Error reports can be de-duplicated by finding sufficiently similar Report instances, and only fully flushing one of them within each equivalence class """ def __init__(self): self.reports = [] self._got_warnings = False def make_warning(self, fun, loc, msg): assert isinstance(fun, gcc.Function) assert isinstance(loc, gcc.Location) self._got_warnings = True w = Report(fun, loc, msg) self.reports.append(w) w.add_warning(loc, msg) return w def make_debug_dump(self, fun, loc, msg): assert isinstance(fun, gcc.Function) assert isinstance(loc, gcc.Location) r = Report(fun, loc, msg) self.reports.append(r) return r def got_warnings(self): return self._got_warnings def to_json(self, fun): result = dict(filename=fun.start.file, function=dict(name=fun.decl.name, # line number range: lines=(fun.decl.location.line - 1, fun.end.line + 1)), reports=[]) for report in self.reports: result['reports'].append(report.to_json(fun)) return result def dump_json(self, fun, filename): js = self.to_json(fun) from json import dump, dumps with open(filename, 'w') as f: dump(js, f, sort_keys=True, indent=4) if 0: print(dumps(js, sort_keys=True, indent=4)) def to_html(self, fun): # (FIXME: eliminate self.fun from HtmlRenderer and the above arg) r = HtmlRenderer(fun) html = r.make_header() for report in self.reports: html += r.make_report(report) html += r.make_footer() return html def dump_html(self, fun, filename): html = self.to_html(fun) with open(filename, 'w') as f: f.write(html) def remove_duplicates(self): """ Try to organize Report instances into equivalence classes, and only keep the first Report within each class """ for report in self.reports[:]: # The report might have been removed during the iteration: if report.is_duplicate: continue for candidate in self.reports[:]: if report != candidate and not report.is_duplicate: if candidate.is_duplicate_of(report): report.add_duplicate(candidate) self.reports.remove(candidate) # Add a note to each report that survived about any duplicates: for report in self.reports: if report.duplicates: report.add_note(report.loc, ('found %i similar trace(s) to this' % len(report.duplicates))) def flush(self): for r in self.reports: r.flush() class SavedDiagnostic: """ A saved GCC diagnostic, which we can choose to emit or suppress at a later date """ def __init__(self, loc, msg): assert isinstance(loc, gcc.Location) assert isinstance(msg, str) self.loc = loc self.msg = msg class SavedWarning(SavedDiagnostic): def flush(self): gcc.warning(self.loc, self.msg) class SavedInform(SavedDiagnostic): def flush(self): gcc.inform(self.loc, self.msg) class Report: """ Data about a particular bug found by the checker """ def __init__(self, fun, loc, msg): self.fun = fun self.loc = loc self.msg = msg self.trace = None self._annotators = {} self.notes = [] self._saved_diagnostics = [] # list of SavedDiagnostic # De-duplication handling: self.is_duplicate = False self.duplicates = [] # list of Report def add_warning(self, loc, msg): # Add a gcc.warning() to the buffer of GCC diagnostics self._saved_diagnostics.append(SavedWarning(loc, msg)) def add_inform(self, loc, msg): # Add a gcc.inform() to the buffer of GCC diagnostics self._saved_diagnostics.append(SavedInform(loc, msg)) def flush(self): # Flush the buffer of GCC diagnostics for d in self._saved_diagnostics: d.flush() def add_trace(self, trace, annotator=None): self.trace = trace self._annotators[trace] = annotator describe_trace(trace, self, annotator) def add_note(self, loc, msg): """ Add a note at the given location. This is added both to the buffer of GCC diagnostics, and also to a saved list that's available to the HTML renderer. """ self.add_inform(loc, msg) note = Note(loc, msg) self.notes.append(note) return note def get_annotator_for_trace(self, trace): return self._annotators.get(trace) def is_duplicate_of(self, other): check_isinstance(other, Report) # Simplistic equivalence classes for now: # the same function, source location, and message; everything # else can be different if self.fun != other.fun: return False if self.loc != other.loc: return False if self.msg != other.msg: return False return True def add_duplicate(self, other): assert not self.is_duplicate self.duplicates.append(other) other.is_duplicate = True def to_json(self, fun): assert self.trace result = dict(message=self.msg, severity='warning', # FIXME states=[]) # Generate a list of (state, desc) pairs, putting the desc from the # transition into source state; the final state will have an empty # string pairs = [] for t_iter in self.trace.transitions: pairs.append( (t_iter.src, t_iter.desc) ) pairs.append( (self.trace.transitions[-1].dest, None) ) for i, (s_iter, desc) in enumerate(pairs): result['states'].append(s_iter.as_json(desc)) result['notes'] = [dict(location=location_as_json(note.loc), message=note.msg) for note in self.notes] return result class Note: """ A note within a self """ def __init__(self, loc, msg): self.loc = loc self.msg = msg def location_as_json(loc): if loc: return (dict(line=loc.line, column=loc.column), dict(line=loc.line, column=loc.column)) else: return None def type_as_json(t): if t: return str(t) else: return None gcc-python-plugin-0.17/libcpychecker/formatstrings.py000066400000000000000000000445401342215241600231020ustar00rootroot00000000000000# Copyright 2011, 2016 David Malcolm # Copyright 2011, 2016 Red Hat, Inc. # # This 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 # . import sys from gccutils import get_src_for_loc, get_global_typedef from libcpychecker.types import * from libcpychecker.utils import log const_correctness = True def get_char_ptr(): return gcc.Type.char().pointer def get_const_char_ptr(): return gcc.Type.char().const_equivalent.pointer def get_const_char_ptr_ptr(): if const_correctness: return gcc.Type.char().const_equivalent.pointer.pointer else: # Allow people to be sloppy about const-correctness here: return (gcc.Type.char().const_equivalent.pointer.pointer, gcc.Type.char().pointer.pointer) def get_hash_size_type(with_size_t): # Was PY_SSIZE_T_CLEAN defined? if with_size_t: return get_Py_ssize_t() else: return gcc.Type.int() class NullPointer: # Dummy value, for pointer arguments that can legitimately be NULL def describe(self): return 'NULL' class CExtensionWarning(Exception): # Base class for warnings discovered by static analysis in C extension code pass class FormatStringWarning(CExtensionWarning): def __init__(self, fmt_string): self.fmt_string = fmt_string def emit_as_warning(self, loc): if gcc.warning(loc, str(self)): sys.stderr.write(self.extra_info()) def extra_info(self): return "" class UnknownFormatChar(FormatStringWarning): def __init__(self, fmt_string, ch): FormatStringWarning.__init__(self, fmt_string) self.ch = ch def __str__(self): return "unknown format char in \"%s\": '%s'" % (self.fmt_string, self.ch) class UnhandledCode(UnknownFormatChar): def __str__(self): return "unhandled format code in \"%s\": '%s' (FIXME)" % (self.fmt_string, self.ch) class MismatchedParentheses(FormatStringWarning): def __str__(self): return "mismatched parentheses in format string \"%s\"" % (self.fmt_string, ) class ParsedFormatStringWarning(FormatStringWarning): def __init__(self, funcname, fmt): FormatStringWarning.__init__(self, fmt.fmt_string) self.funcname = funcname self.fmt = fmt class FormatUnit: """ One fragment of the string arg to PyArg_ParseTuple and friends """ def __init__(self, code): self.code = code def get_expected_types(self): # Return a list of the types expected for this format unit, # either as gcc.Type instances, or as instances of AwkwardType raise NotImplementedError class ConcreteUnit(FormatUnit): """ The common case: a fragment of a format string that corresponds to a pre-determined list of types (often just a single element) """ def __init__(self, code, expected_types): FormatUnit.__init__(self, code) self.expected_types = expected_types def get_expected_types(self): return self.expected_types def __repr__(self): return 'ConcreteUnit(%r,%r)' % (self.code, self.expected_types) class AwkwardType: """ Base class for expected types within a format unit that need special handling (e.g. for "O!" and "O&") """ def is_compatible(self, actual_type, actual_arg): raise NotImplementedError def describe(self): raise NotImplementedError class ParsedFormatString: """ Python class representing the result of parsing a format string """ def __init__(self, fmt_string): self.fmt_string = fmt_string self.args = [] def __repr__(self): return ('%s(fmt_string=%r, args=%r)' % (self.__class__.__name__, self.fmt_string, self.args)) class WrongNumberOfVars(ParsedFormatStringWarning): def __init__(self, funcname, fmt, varargs): ParsedFormatStringWarning.__init__(self, funcname, fmt) self.varargs = varargs def __str__(self): return ('%s in call to %s with format string "%s"' % (self._get_desc_prefix(), self.funcname, self.fmt.fmt_string)) def extra_info(self): result = (' expected %i extra arguments:\n' % self.fmt.num_expected()) for (arg, exp_type) in self.fmt.iter_exp_types(): result += ' %s\n' % describe_type(exp_type) if len(self.varargs) == 0: result += ' but got none\n' else: result += ' but got %i:\n' % len(self.varargs) for arg in self.varargs: result += ' %s\n' % describe_type(arg.type) return result def _get_desc_prefix(self): raise NotImplementedError class NotEnoughVars(WrongNumberOfVars): def _get_desc_prefix(self): return 'Not enough arguments' class TooManyVars(WrongNumberOfVars): def _get_desc_prefix(self): return 'Too many arguments' class MismatchingType(ParsedFormatStringWarning): def __init__(self, funcname, fmt, arg_num, arg_fmt_string, exp_type, vararg): super(self.__class__, self).__init__(funcname, fmt) self.arg_num = arg_num self.arg_fmt_string = arg_fmt_string self.exp_type = exp_type self.vararg = vararg def extra_info(self): def _describe_vararg(va): result = '"%s"' % va.type if hasattr(va, 'operand'): result += describe_precision(va.operand.type) return result return (' argument %i ("%s") had type\n' ' %s\n' ' but was expecting\n' ' %s\n' ' for format code "%s"\n' % (self.arg_num, self.vararg, describe_type(self.vararg.type), describe_type(self.exp_type), self.arg_fmt_string)) def __str__(self): return ('Mismatching type in call to %s with format code "%s"' % (self.funcname, self.fmt.fmt_string)) def describe_precision(t): if hasattr(t, 'precision'): return ' (pointing to %i bits)' % t.precision else: return '' def describe_type(t): if isinstance(t, AwkwardType): return t.describe() if isinstance(t, NullPointer): return t.describe() if isinstance(t, tuple): result = 'one of ' + ' or '.join([describe_type(tp) for tp in t]) else: # Special-case handling of function types, to avoid embedding the ID: # c.f. void (*) (void) if isinstance(t, gcc.PointerType): if isinstance(t.dereference, gcc.FunctionType): signature = t.dereference return ('"%s (*fn) (%s)"' % (signature.type, ', '.join([str(argtype) for argtype in signature.argument_types]))) result = '"%s"' % t if hasattr(t, 'dereference'): result += describe_precision(t.dereference) return result def compatible_type(exp_type, actual_type, actualarg=None): log('comparing exp_type: %s (%r) with actual_type: %s (%r)', exp_type, exp_type, actual_type, actual_type) log('type(exp_type): %r %s', type(exp_type), type(exp_type)) log('actualarg: %s (%r)', actualarg, actualarg) # Support exp_type being actually a tuple of expected types (we need this # for "S" and "U"): if isinstance(exp_type, tuple): for exp in exp_type: if compatible_type(exp, actual_type, actualarg): return True # Didn't match any of them: return False # Support the "O!" and "O&" converter codes: if isinstance(exp_type, AwkwardType): return exp_type.is_compatible(actual_type, actualarg) # Support the codes that can accept NULL: if isinstance(exp_type, NullPointer): if isinstance(actual_type, gcc.PointerType): if isinstance(actual_type.dereference, gcc.VoidType): # We have a (void*), double-check that it's actually NULL: if actualarg: if isinstance(actualarg, gcc.IntegerCst): if actualarg.constant == 0: # We have NULL: return True return False assert isinstance(exp_type, gcc.Type) or isinstance(exp_type, gcc.TypeDecl) assert isinstance(actual_type, gcc.Type) or isinstance(actual_type, gcc.TypeDecl) # Try direct comparison: if actual_type == exp_type: return True # Sometimes we get the typedef rather than the type, for both exp and # actual. Compare using the actual types, but report using the typedefs # so that we can report that e.g. # PyObject * * # was expected, rather than: # struct PyObject * * if isinstance(exp_type, gcc.TypeDecl): if compatible_type(exp_type.type, actual_type): return True if isinstance(actual_type, gcc.TypeDecl): if compatible_type(exp_type, actual_type.type): return True # Dereference for pointers (and ptrs to ptrs etc): if isinstance(actual_type, gcc.PointerType) and isinstance(exp_type, gcc.PointerType): if compatible_type(exp_type.dereference, actual_type.dereference): return True # Support (const char*) vs (char*) # Somewhat counter-intuitively, the APIs that expect a char* are those that # read the string data (Py_BuildValue); those that expect a const char* are # those that write back a const char* value (PyArg_ParseTuple) # # Hence it's OK to accept a (const char*) when a (char*) was expected: if str(exp_type) == 'char *': if str(actual_type) == 'const char *': return True # Don't be too fussy about typedefs to integer types # For instance: # typedef unsigned PY_LONG_LONG gdb_py_ulongest; # gives a different IntegerType instance to that of # gcc.Type.long_long().unsigned_equivalent # As long as the size, signedness etc are the same, let it go if isinstance(actual_type, gcc.IntegerType) and isinstance(exp_type, gcc.IntegerType): def compare_int_types(): for attr in ('precision', 'unsigned', 'const', 'volatile', 'restrict'): if getattr(actual_type, attr) != getattr(exp_type, attr): return False return True if compare_int_types(): return True # Support character arrays vs char*: if str(exp_type) == 'char *': if isinstance(actual_type, gcc.PointerType): if isinstance(actual_type.dereference, gcc.ArrayType): if actual_type.dereference.dereference == gcc.Type.char(): return True return False def check_pyargs(fun): from libcpychecker.PyArg_ParseTuple import PyArgParseFmt from libcpychecker.Py_BuildValue import PyBuildValueFmt def get_format_string(stmt, format_idx): fmt_code = stmt.args[format_idx] # We can only cope with the easy case, when it's a AddrExpr(StringCst()) # i.e. a reference to a string constant, i.e. a string literal in the C # source: if isinstance(fmt_code, gcc.AddrExpr): operand = fmt_code.operand if isinstance(operand, gcc.StringCst): return operand.constant def check_keyword_array(stmt, idx): keywords = stmt.args[idx] if isinstance(keywords, gcc.AddrExpr): operand = keywords.operand if isinstance(operand, gcc.VarDecl): # Caveat: "initial" will only be set up on the VarDecl of a # global variable, or a "static" variable in function scope; # for other local variables we appear to need to track the # gimple statements to get the value at the callsite initial = operand.initial if isinstance(initial, gcc.Constructor): elements = [None] * len(initial.elements) for elt in initial.elements: (num, contents) = elt elt_idx = num.constant if isinstance(contents, gcc.NopExpr): contents = contents.operand if isinstance(contents, gcc.AddrExpr): contents = contents.operand if isinstance(contents, gcc.StringCst): elements[elt_idx] = contents.constant elif isinstance(contents, gcc.IntegerCst): elements[elt_idx] = contents.constant if elements[-1] != 0: gcc.warning(stmt.loc, 'keywords to PyArg_ParseTupleAndKeywords are not NULL-terminated') i = 0 for elt in elements[0:-1]: if not elt: gcc.warning(stmt.loc, 'keyword argument %d missing in PyArg_ParseTupleAndKeywords call' % i) i = i + 1 def check_callsite(stmt, parser, funcname, format_idx, varargs_idx, with_size_t): log('got call at %s', stmt.loc) log(get_src_for_loc(stmt.loc)) # log('stmt: %r %s', (stmt, stmt)) # log('args: %r', stmt.args) # for arg in stmt.args: # # log(' arg: %s %r', (arg, arg)) # We expect the following args: # args[0]: PyObject *input_tuple # args[1]: char * format # args[2...]: output pointers if len(stmt.args) >= format_idx: fmt_string = get_format_string(stmt, format_idx) if fmt_string: log('fmt_string: %r', fmt_string) loc = stmt.loc # Figure out expected types, based on the format string... try: fmt = parser.from_string(fmt_string, with_size_t) except FormatStringWarning: err = sys.exc_info()[1] err.emit_as_warning(stmt.loc) return log('fmt: %r', fmt.args) exp_types = list(fmt.iter_exp_types()) log('exp_types: %r', exp_types) # ...then compare them against the actual types: varargs = stmt.args[varargs_idx:] # log('varargs: %r', varargs) if len(varargs) < len(exp_types): NotEnoughVars(funcname, fmt, varargs).emit_as_warning(loc) return if len(varargs) > len(exp_types): TooManyVars(funcname, fmt, varargs).emit_as_warning(loc) return for index, ((exp_arg, exp_type), vararg) in enumerate(zip(exp_types, varargs)): if not compatible_type(exp_type, vararg.type, actualarg=vararg): err = MismatchingType(funcname, fmt, index + varargs_idx + 1, exp_arg.code, exp_type, vararg) if hasattr(vararg, 'location'): loc = vararg.location else: loc = stmt.loc err.emit_as_warning(loc) def maybe_check_callsite(stmt): if stmt.fndecl: # If "PY_SSIZE_T_CLEAN" is defined before #include , then # the preprocessor is actually turning these into "_SizeT"-suffixed # variants, which handle some format codes differently # FIXME: should we report the name as seen by the compiler? # It doesn't appear in the CPython API docs if stmt.fndecl.name == 'PyArg_ParseTuple': check_callsite(stmt, PyArgParseFmt, 'PyArg_ParseTuple', 1, 2, False) elif stmt.fndecl.name == '_PyArg_ParseTuple_SizeT': check_callsite(stmt, PyArgParseFmt, 'PyArg_ParseTuple', 1, 2, True) elif stmt.fndecl.name == 'PyArg_Parse': check_callsite(stmt, PyArgParseFmt, 'PyArg_Parse', 1, 2, False) elif stmt.fndecl.name == '_PyArg_Parse_SizeT': check_callsite(stmt, PyArgParseFmt, 'PyArg_Parse', 1, 2, True) elif stmt.fndecl.name == 'PyArg_ParseTupleAndKeywords': check_keyword_array(stmt, 3) check_callsite(stmt, PyArgParseFmt, 'PyArg_ParseTupleAndKeywords', 2, 4, False) elif stmt.fndecl.name == '_PyArg_ParseTupleAndKeywords_SizeT': check_keyword_array(stmt, 3) check_callsite(stmt, PyArgParseFmt, 'PyArg_ParseTupleAndKeywords', 2, 4, True) elif stmt.fndecl.name == 'Py_BuildValue': check_callsite(stmt, PyBuildValueFmt, 'Py_BuildValue', 0, 1, False) elif stmt.fndecl.name == 'Py_BuildValue_SizeT': check_callsite(stmt, PyBuildValueFmt, 'Py_BuildValue', 0, 1, True) if fun.cfg: for bb in fun.cfg.basic_blocks: if isinstance(bb.gimple, list): for stmt in bb.gimple: if stmt.loc: gcc.set_location(stmt.loc) if isinstance(stmt, gcc.GimpleCall): maybe_check_callsite(stmt) gcc-python-plugin-0.17/libcpychecker/initializers.py000066400000000000000000000160601342215241600227020ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Verification of data initializers (e.g. PyMethodDef tables) import gcc from gccutils import check_isinstance from libcpychecker.utils import log def check_initializers(): # Invoked by the "cpychecker-ipa" pass, once per compilation unit verify_any_PyMethodDef_flags() from collections import OrderedDict class StructInitializer(object): def __init__(self, ctor): check_isinstance(ctor, gcc.Constructor) self.ctor = ctor # Mapping from string fieldname to gcc.Tree value: self.fielddict = OrderedDict() for key, tree in ctor.elements: check_isinstance(key, gcc.FieldDecl) self.fielddict[key.name] = tree def __repr__(self): attrs = ','.join(['%s=%s' % (k, v) for k, v in self.fielddict.items()]) return '%s(%s)' % (self.__class__.__name__, attrs) def int_field(self, fieldname): """ Extract the initializer for the given field, as an int """ if fieldname not in self.fielddict: return 0 # implicit 0 tree = self.fielddict[fieldname] check_isinstance(tree, gcc.IntegerCst) return tree.constant def char_ptr_field(self, fieldname): if fieldname not in self.fielddict: return None # implicit NULL tree = self.fielddict[fieldname] if isinstance(tree, gcc.IntegerCst): if tree.constant == 0: return None # NULL # go past casts: if isinstance(tree, gcc.NopExpr): tree = tree.operand check_isinstance(tree, gcc.AddrExpr) check_isinstance(tree.operand, gcc.StringCst) return tree.operand.constant def function_ptr_field(self, fieldname): """ Extract the initializer for the given field, as a gcc.FunctionDecl, or None for NULL. """ if fieldname not in self.fielddict: return None # implicit NULL tree = self.fielddict[fieldname] # go past casts: if isinstance(tree, gcc.NopExpr): tree = tree.operand if isinstance(tree, gcc.IntegerCst): if tree.constant == 0: return None # NULL check_isinstance(tree, gcc.AddrExpr) return tree.operand class PyMethodDefInitializer(StructInitializer): def get_location(self): return self.fielddict['ml_meth'].location # Adapted from Include/methodobject.h: METH_OLDARGS = 0x0000 METH_VARARGS = 0x0001 METH_KEYWORDS = 0x0002 METH_NOARGS = 0x0004 METH_O = 0x0008 METH_CLASS = 0x0010 METH_STATIC = 0x0020 METH_COEXIST = 0x0040 def verify_any_PyMethodDef_flags(): """ Check all initializers for PyMethodDef arrays. Verify that the flags used match the real signature of the callback function (albeit usually cast to a PyCFunction): http://docs.python.org/c-api/structures.html#PyMethodDef """ methods = get_all_PyMethodDef_initializers() #from pprint import pprint #pprint(methods) for si in methods: if 0: print(si) ml_meth = si.function_ptr_field('ml_meth') ml_flags = si.int_field('ml_flags') if 0: print(' ml_meth: %r' % ml_meth) print(' ml_flags: %r' % ml_flags) check_isinstance(ml_flags, int) if ml_meth is not None: check_isinstance(ml_meth, gcc.FunctionDecl) if ml_flags & METH_KEYWORDS: expargs = 3 exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag' else: expargs = 2 exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)"' actualargs = len(ml_meth.type.argument_types) if expargs != actualargs: gcc.warning(si.get_location(), 'flags do not match callback signature for %r' ' within PyMethodDef table' % ml_meth.name) gcc.inform(si.get_location(), exptypemsg + ' (%s arguments)' % expargs) gcc.inform(si.get_location(), 'actual type of underlying callback: %s' % ml_meth.type + ' (%s arguments)' % actualargs) gcc.inform(si.get_location(), 'see http://docs.python.org/c-api/structures.html#PyMethodDef') def get_all_PyMethodDef_initializers(): """ Locate all initializers for PyMethodDef, returning a list of StructInitializer instances """ log('get_all_PyMethodDef_initializers') result = [] vars = gcc.get_variables() for var in vars: if isinstance(var.decl, gcc.VarDecl): if isinstance(var.decl.type, gcc.ArrayType): if str(var.decl.type.type) == 'struct PyMethodDef': if var.decl.initial: table = [] for idx, ctor in var.decl.initial.elements: #print idx, ctor si = PyMethodDefInitializer(ctor) table.append(si) # Warn about missing sentinel entry with # ml->ml_name == NULL ml_name = table[-1].char_ptr_field('ml_name') if 0: print('final ml_name: %r' % ml_name) if ml_name is not None: gcc.warning(table[-1].get_location(), 'missing NULL sentinel value at end of PyMethodDef table') result += table return result class PyTypeObjectInitializer(StructInitializer): pass def get_all_PyTypeObject_initializers(): """ Locate all initializers for PyTypeObject, returning a list of PyTypeObjectInitializer instances """ log('get_all_PyTypeObject_initializers') result = [] vars = gcc.get_variables() for var in vars: if isinstance(var.decl, gcc.VarDecl): if str(var.decl.type) == 'struct PyTypeObject': ctor = var.decl.initial if ctor: si = PyTypeObjectInitializer(ctor) result.append(si) return result gcc-python-plugin-0.17/libcpychecker/refcounts.py000066400000000000000000006346171342215241600222220ustar00rootroot00000000000000# Copyright 2011, 2012 David Malcolm # Copyright 2011, 2012 Red Hat, Inc. # # This 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 # . # Attempt to check that C code is implementing CPython's reference-counting # rules. See: # http://docs.python.org/c-api/intro.html#reference-counts # for a description of how such code is meant to be written import sys import gcc from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc, check_isinstance from libcpychecker.absinterp import * from libcpychecker.attributes import fnnames_returning_borrowed_refs, \ stolen_refs_by_fnname, fnnames_setting_exception, \ fnnames_setting_exception_on_negative_result from libcpychecker.diagnostics import Reporter, Annotator, Note from libcpychecker.PyArg_ParseTuple import PyArgParseFmt, FormatStringWarning,\ TypeCheckCheckerType, TypeCheckResultType, \ ConverterCallbackType, ConverterResultType from libcpychecker.Py_BuildValue import PyBuildValueFmt, ObjectFormatUnit, \ CodeSO, CodeN from libcpychecker.types import is_py3k, is_debug_build, get_PyObjectPtr, \ get_Py_ssize_t from libcpychecker.utils import log from libcpychecker import compat def stmt_is_assignment_to_count(stmt): if hasattr(stmt, 'lhs'): if stmt.lhs: if isinstance(stmt.lhs, gcc.ComponentRef): # print 'stmt.lhs.target: %s' % stmt.lhs.target # print 'stmt.lhs.target.type: %s' % stmt.lhs.target.type # (presumably we need to filter these to structs that are # PyObject, or subclasses) if stmt.lhs.field.name == 'ob_refcnt': return True def type_is_pyobjptr(t): assert t is None or isinstance(t, gcc.Type) if str(t) == 'struct PyObject *': return True def type_is_pyobjptr_subclass(t): assert t is None or isinstance(t, gcc.Type) # It must be a pointer: if not isinstance(t, gcc.PointerType): return False # ...to a struct: if not isinstance(t.dereference, gcc.RecordType): return False # Obtain the fields of the struct/class # For C++ "fields" will also contain a gcc.TypeDecl for the # type itself, and for any nested types (e.g. typedefs), so filter them # out. This avoids an infinite recursion for classes with no data, where # the initial decl of the type otherwise would make it appear that there's # a nested copy of the struct inside itself. fields = [field for field in t.dereference.fields if isinstance(field, gcc.FieldDecl)] if len(fields) == 0: # Opaque struct: there's nothing we can do. # Assume it's *not* a PyObject subclass: return False # if first field is a PyObject subclass, then we're good: if type_is_pyobjptr_subclass(fields[0].type.pointer): return True fieldnames = [f.name for f in fields] if is_py3k(): # For Python 3, the first field must be "ob_base", or it must be "PyObject": if str(t) == 'struct PyObject *': return True if fieldnames[0] != 'ob_base': return False else: # For Python 2, the first two fields must be "ob_refcnt" and "ob_type". # (In a debug build, these are preceded by _ob_next and _ob_prev) # FIXME: debug builds! if is_debug_build(): if fieldnames[:4] != ['_ob_next', '_ob_prev', 'ob_refcnt', 'ob_type']: return False else: if fieldnames[:2] != ['ob_refcnt', 'ob_type']: return False # Passed all tests: return True def stmt_is_assignment_to_objptr(stmt): if hasattr(stmt, 'lhs'): if stmt.lhs: if type_is_pyobjptr(stmt.lhs.type): return True def stmt_is_return_of_objptr(stmt): if isinstance(stmt, gcc.GimpleReturn): if stmt.retval: if type_is_pyobjptr(stmt.retval.type): return True def make_null_pyobject_ptr(stmt): return make_null_ptr(get_PyObjectPtr(), stmt.loc) ############################################################################ # API for describing the effect of a function call, attempting to abstract # away the internals of the checker ############################################################################ class FunctionCall: # A way to describe the behaviors of a function def __init__(self, s_src, stmt, fnmeta, args=None, varargs=None): check_isinstance(s_src, State) self.s_src = s_src self.stmt = stmt self.fnmeta = fnmeta self.args = args self.varargs = varargs self._crashes_on_null_arg = [] self.outcomes = [] self._never_returns = False def crashes_on_null_arg(self, argidx, why): self._crashes_on_null_arg.append( (argidx, why) ) def never_returns(self): assert self.outcomes == [] self._never_returns = True def add_outcome(self, desc, s_new=None): assert not self._never_returns if s_new is None: s_new = self.s_src.use_next_stmt_node() oc_new = Outcome(self, desc, s_new) self.outcomes.append(oc_new) return oc_new def always(self): # For functions with a single outcome return self.add_outcome('calling %s()' % self.fnmeta.name) def can_succeed(self): return self.add_outcome(self.fnmeta.desc_when_call_succeeds()) def can_fail(self): return self.add_outcome(self.fnmeta.desc_when_call_fails()) def can_succeed_new_ref(self, name=None, typeobjregion=None): if name is None: name = 'new ref from %s()' % self.fnmeta.name oc = self.add_outcome(self.fnmeta.desc_when_call_succeeds()) s_new, r_nonnull = self.s_src.cpython.mkstate_new_ref(self.stmt, name, typeobjregion) oc.state = s_new oc.returns_ptr(r_nonnull) return oc def new_ref_or_fail(self, objname=None): # Return (on_success, on_failure) pair of Outcome instances if objname is None: objname = 'new ref from call to %s' % self.fnmeta.name on_success = self.can_succeed_new_ref(objname) on_failure = self.can_fail() on_failure.sets_exception('PyExc_MemoryError') on_failure.returns_NULL() return on_success, on_failure def get_transitions(self): # Sanity-check: if self.stmt.lhs: for oc in self.outcomes: if not oc.v_return: class OutcomeHasNoReturnValue(Exception): def __init__(self, oc): self.oc = oc def __str__(self): return '%s does not define a return value' % oc raise OutcomeHasNoReturnValue(oc) # Check for null args: for argidx, why in self._crashes_on_null_arg: assert self.args self.s_src.raise_any_null_ptr_func_arg(self.stmt, argidx, self.args[argidx], why) if self._never_returns: # Terminates the process; no further transitions: return [self.s_src.mktrans_not_returning('calling %s() and exiting' % self.fnmeta.name)] return [self._make_transition(oc) for oc in self.outcomes if oc.is_possible] def _make_transition(self, oc): t_new = Transition(self.s_src, oc.state, oc.desc) return t_new class Outcome: # A particular outcome of a FunctionCall # Naming convention "oc_*" or "on_*" within an implementation def __init__(self, fncall, desc, state): check_isinstance(state, State) self.fncall = fncall self.desc = desc self.state = state self.is_possible = True # Use this to ensure that the client sets up the return value: self.v_return = None def __str__(self): return 'outcome %r' % self.desc def get_stmt(self): return self.fncall.stmt def get_return_type(self): return self.get_stmt().fn.type.dereference.type def returns(self, value): check_isinstance(value, numeric_types) self._returns(ConcreteValue(self.get_return_type(), self.get_stmt().loc, value)) def returns_ptr(self, region): check_isinstance(region, Region) self._returns(PointerToRegion(self.get_return_type(), self.get_stmt().loc, region)) def returns_NULL(self): self._returns(ConcreteValue(self.get_return_type(), self.get_stmt().loc, 0)) def _returns(self, v_return): self.v_return = v_return if self.get_stmt().lhs: self.state.assign(self.get_stmt().lhs, v_return, self.get_stmt().loc) def sets_exception(self, exc_name): self.state.cpython.set_exception(exc_name, self.get_stmt().loc) def sets_exception_ptr(self, v_ptr): self.state.cpython.exception_rvalue = v_ptr def adds_external_ref(self, v_ptr): if isinstance(v_ptr, PointerToRegion): self.state.cpython.add_external_ref(v_ptr, self.get_stmt().loc) ############################################################################ class RefcountValue(AbstractValue): """ Value for an ob_refcnt field. 'relvalue' is all of the references owned within this function. 'external' is a WithinRange, a bound on all references owned outside the scope of this function. The actual value of ob_refcnt = (relvalue + external) hence: (relvalue + external.minvalue) <= ob_refcnt <= (relvalue + external.minvalue) Examples: - an argument passed in a borrowed ref starts with (0, [1, 1]), in that the function doesn't own any refs on it, but it has a refcount of at least 1, due to refs we know nothing about. - a newly constructed object gets (1, [0, 0]): we own a reference on it, and we don't know if there are any external refs on it. """ __slots__ = ('r_obj', 'relvalue', 'external') def __init__(self, loc, r_obj, relvalue, external): if loc: check_isinstance(loc, gcc.Location) if r_obj: check_isinstance(r_obj, Region) check_isinstance(external, WithinRange) AbstractValue.__init__(self, get_Py_ssize_t().type, loc) self.r_obj = r_obj self.relvalue = relvalue self.external = external @classmethod def new_ref(cls, loc, r_obj): return RefcountValue(loc, r_obj, relvalue=1, external=WithinRange(get_Py_ssize_t().type, loc, 0, 0)) @classmethod def borrowed_ref(cls, loc, r_obj): return RefcountValue(loc, r_obj, relvalue=0, external=WithinRange(get_Py_ssize_t().type, loc, 1, 1)) def get_min_value(self): return self.relvalue + self.external.minvalue def __str__(self): # Try to present the simplest possible string representation of # a refcount: if self.external.minvalue == self.external.maxvalue: # Known value for external refcount if self.external.minvalue == 0: # We know there are no external refs; don't display it # at all, rather than "0": return 'refs: %i owned' % self.relvalue else: # We know of an exact number of external refs; display # it as borrowed: return ('refs: %i owned, %i borrowed' % (self.relvalue, self.external.minvalue)) return ('refs: %i owned + B borrowed where %s' % (self.relvalue, str(self.external).replace('val', 'B'))) def __repr__(self): return 'RefcountValue(%i, %r)' % (self.relvalue, self.external) def get_referrers_as_json(self, state): # FIXME: # Get a list of Regions holding pointers that: # (a) point at the object for this value, and # (b) ought to contribute to this ob_refcnt's relvalue exp_refs = [] v_return = state.return_rvalue if v_return: if (isinstance(v_return, PointerToRegion) and v_return.region == self.r_obj): # The return value points at this obj: if state.fun.decl.name not in fnnames_returning_borrowed_refs: # ...and this function has not been marked as returning a # borrowed reference: it returns a new one: exp_refs = ['return value'] exp_refs += [ref.as_json() for ref in state.get_persistent_refs_for_region(self.r_obj)] return exp_refs def json_fields(self, state): actual = OrderedDict(refs_we_own=self.relvalue, lower_bound_of_other_refs=self.external.minvalue) exp_refs = self.get_referrers_as_json(state) expected = dict(pointers_to_this=exp_refs) return dict(actual_ob_refcnt=actual, expected_ob_refcnt=expected) def eval_binop(self, exprcode, rhs, rhsdesc, gcctype, loc): if isinstance(rhs, ConcreteValue): if exprcode == gcc.PlusExpr: return RefcountValue(loc, self.r_obj, self.relvalue + rhs.value, self.external) elif exprcode == gcc.MinusExpr: return RefcountValue(loc, self.r_obj, self.relvalue - rhs.value, self.external) return UnknownValue.make(gcctype, loc) def eval_comparison(self, opname, rhs, rhsdesc): """ opname is a string in opnames Return a boolean, or None (meaning we don't know) """ if opname == 'eq': if isinstance(rhs, ConcreteValue): log('comparing refcount value %s with concrete value: %s', self, rhs) # The actual value of ob_refcnt >= lhs.relvalue if self.get_min_value() > rhs.value: # (Equality is thus not possible for this case) return False elif opname == 'le': if isinstance(rhs, ConcreteValue): log('comparing refcount value %s with concrete value: %s', self, rhs) if self.get_min_value() > rhs.value: return False elif opname == 'lt': if isinstance(rhs, ConcreteValue): log('comparing refcount value %s with concrete value: %s', self, rhs) if self.get_min_value() >= rhs.value: return False elif opname == 'ge': if isinstance(rhs, ConcreteValue): log('comparing refcount value %s with concrete value: %s', self, rhs) if self.get_min_value() >= rhs.value: return True elif opname == 'gt': if isinstance(rhs, ConcreteValue): log('comparing refcount value %s with concrete value: %s', self, rhs) if self.get_min_value() > rhs.value: return True class GenericTpDealloc(AbstractValue): """ A function pointer that points to a "typical" tp_dealloc callback i.e. one that frees up the underlying memory """ def get_transitions_for_function_call(self, state, stmt): check_isinstance(state, State) check_isinstance(stmt, gcc.GimpleCall) returntype = stmt.fn.type.dereference.type # Mark the arg as being deallocated: value = state.eval_rvalue(stmt.args[0], stmt.loc) if value.is_null_ptr(): # Freeing NULL has no effect: desc = 'calling tp_dealloc on NULL' region = None else: check_isinstance(value, PointerToRegion) region = value.region check_isinstance(region, Region) log('generic tp_dealloc called for %s', region) # Get the description of the region before trashing it: desc = 'calling tp_dealloc on %s' % region result = state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), desc) s_new = state.use_next_stmt_node() if region is not None: # Mark the region as deallocated: s_new.deallocate_region(stmt, region) return [Transition(state, s_new, desc)] ######################################################################## # Helper functions to generate meaningful explanations of why a NULL # argument is a bug: ######################################################################## def invokes_Py_TYPE(fnmeta, within=None): check_isinstance(fnmeta, FnMeta) if within: return ('%s() invokes Py_TYPE() on the pointer within %s(), thus accessing' ' (NULL)->ob_type' % (fnmeta.name, within)) else: return ('%s() invokes Py_TYPE() on the pointer, thus accessing' ' (NULL)->ob_type' % fnmeta.name) def invokes_Py_TYPE_via_macro(fnmeta, macro): """ Generate a descriptive message for cases of raise_any_null_ptr_func_arg() such as PyDict_SetItem() which invoke the PyDict_Check() macro """ check_isinstance(fnmeta, FnMeta) return ('%s() invokes Py_TYPE() on the pointer via the %s()' ' macro, thus accessing (NULL)->ob_type' % (fnmeta.name, macro)) def invokes_Py_INCREF(fnmeta): check_isinstance(fnmeta, FnMeta) return ('%s() invokes Py_INCREF() on the pointer, thus accessing' ' (NULL)->ob_refcnt' % fnmeta.name) ######################################################################## class CPython(Facet): __slots__ = ('exception_rvalue', 'has_gil',) def __init__(self, state, exception_rvalue=None, has_gil=True, fun=None): Facet.__init__(self, state) check_isinstance(has_gil, bool) if exception_rvalue: check_isinstance(exception_rvalue, AbstractValue) self.exception_rvalue = exception_rvalue else: check_isinstance(fun, gcc.Function) self.exception_rvalue = ConcreteValue(get_PyObjectPtr(), fun.start, 0) self.has_gil = has_gil def copy(self, newstate): f_new = CPython(newstate, self.exception_rvalue, self.has_gil) return f_new def init_for_function(self, fun): log('CPython.init_for_function(%r)', fun) # Initialize PyObject* arguments to sane values # (assume that they're non-NULL) nonnull_args = get_nonnull_arguments(fun.decl.type) for idx, parm in enumerate(fun.decl.arguments): region = self.state.eval_lvalue(parm, None) if type_is_pyobjptr_subclass(parm.type): # We have a PyObject* (or a derived class) log('got python obj arg: %r', region) # Assume it's a non-NULL ptr: objregion = RegionForLocal(parm, None) self.state.region_for_var[objregion] = objregion self.state.value_for_region[region] = PointerToRegion(parm.type, parm.location, objregion) # Assume we have a borrowed reference: ob_refcnt = self.state.make_field_region(objregion, 'ob_refcnt') # FIXME: this should be a memref and fieldref self.state.value_for_region[ob_refcnt] = \ RefcountValue.borrowed_ref(parm.location, objregion) # Assume it has a non-NULL ob_type: ob_type = self.state.make_field_region(objregion, 'ob_type') typeobjregion = Region('region-for-type-of-arg-%r' % parm, None) self.state.value_for_region[ob_type] = PointerToRegion(get_PyTypeObject().pointer, parm.location, typeobjregion) self.state.verify() def get_refcount(self, v_pyobjectptr, stmt): """ Get the ob_refcnt of the given PyObject*, as an AbstractValue """ check_isinstance(v_pyobjectptr, PointerToRegion) check_isinstance(stmt, gcc.Gimple) v_ob_refcnt = self.state.read_field_by_name(stmt, get_Py_ssize_t().type, v_pyobjectptr.region, 'ob_refcnt') return v_ob_refcnt def change_refcount(self, pyobjectptr, loc, fn): """ Manipulate pyobjectptr's ob_refcnt. fn is a function taking a RefcountValue instance, returning another one """ if isinstance(pyobjectptr, UnknownValue): self.state.raise_split_value(pyobjectptr, loc) check_isinstance(pyobjectptr, PointerToRegion) ob_refcnt = self.state.make_field_region(pyobjectptr.region, 'ob_refcnt') check_isinstance(ob_refcnt, Region) oldvalue = self.state.get_store(ob_refcnt, None, loc) # FIXME: gcctype check_isinstance(oldvalue, AbstractValue) log('oldvalue: %r', oldvalue) # If we never had a ob_refcnt, treat it as a borrowed reference: if isinstance(oldvalue, UnknownValue): oldvalue = RefcountValue.borrowed_ref(loc, pyobjectptr.region) check_isinstance(oldvalue, RefcountValue) newvalue = fn(oldvalue) log('newvalue: %r', newvalue) self.state.value_for_region[ob_refcnt] = newvalue return newvalue def add_ref(self, pyobjectptr, loc): """ Add a "visible" reference to pyobjectptr's ob_refcnt i.e. a reference being held by a PyObject* that we are directly tracking. """ def _incref_internal(oldvalue): return RefcountValue(loc, pyobjectptr.region, oldvalue.relvalue + 1, oldvalue.external) self.change_refcount(pyobjectptr, loc, _incref_internal) def add_external_ref(self, pyobjectptr, loc): """ Add an "external" reference to pyobjectptr's ob_refcnt i.e. a reference being held by a PyObject* that we're not directly tracking. """ def _incref_external(oldvalue): return RefcountValue(loc, pyobjectptr.region, oldvalue.relvalue, WithinRange(oldvalue.external.gcctype, loc, oldvalue.external.minvalue + 1, oldvalue.external.maxvalue + 1)) self.change_refcount(pyobjectptr, loc, _incref_external) def dec_ref(self, pyobjectptr, loc): """ Remove a "visible" reference to pyobjectptr's ob_refcnt i.e. a reference being held by a PyObject* that we are directly tracking. """ def _decref_internal(oldvalue): return RefcountValue(loc, pyobjectptr.region, oldvalue.relvalue - 1, oldvalue.external) check_isinstance(pyobjectptr, PointerToRegion) v_ob_refcnt = self.change_refcount(pyobjectptr, loc, _decref_internal) # FIXME: potentially this destroys the object def mktransitions_Py_DECREF(self, v_pyobjectptr, stmt): """ Generate a transitions in which the equivalent to a Py_DECREF occurs: decrement ob_refcnt, and if 0, call _Py_Dealloc((PyObject *)(op)) Does *not* take you to the next statement """ if isinstance(v_pyobjectptr, UnknownValue): self.state.raise_split_value(v_pyobjectptr, stmt.loc) check_isinstance(v_pyobjectptr, PointerToRegion) check_isinstance(stmt, gcc.Gimple) s_new = self.state.copy() s_new.cpython.dec_ref(v_pyobjectptr, stmt.loc) v_ob_refcnt = s_new.cpython.get_refcount(v_pyobjectptr, stmt) # print('ob_refcnt: %r' % v_ob_refcnt) eq_zero = v_ob_refcnt.eval_comparison('eq', ConcreteValue.from_int(1), None) # print('eq_zero: %r' % eq_zero) if eq_zero or eq_zero is None: # tri-state; it might be zero: s_dealloc = s_new.copy() # FIXME: call _Py_Dealloc return [Transition(self.state, s_new, 'Py_DECREF() without deallocation'), Transition(self.state, s_dealloc, 'Py_DECREF() with deallocation')] else: # ob_refcnt != 0, so it doesn't dealloc: return [Transition(self.state, s_new, 'Py_DECREF() without deallocation')] def set_exception(self, exc_name, loc): """ Given the name of a (PyObject*) global for an exception class, such as the string "PyExc_MemoryError", set the exception state to the (PyObject*) for said exception class. The list of standard exception classes can be seen at: http://docs.python.org/c-api/exceptions.html#standard-exceptions """ check_isinstance(exc_name, str) exc_decl = compat.get_exception_decl_by_name(exc_name) check_isinstance(exc_decl, gcc.VarDecl) r_exception = self.state.var_region(exc_decl) v_exception = PointerToRegion(get_PyObjectPtr(), loc, r_exception) self.exception_rvalue = v_exception def bad_internal_call(self, loc): """ Analogous to PyErr_BadInternalCall(), which is a macro to _PyErr_BadInternalCall() at the source file/line location """ self.set_exception('PyExc_SystemError', loc) def bad_argument(self, loc): """ Analogous to PyErr_BadArgument() """ self.set_exception('PyExc_TypeError', loc) def typeobjregion_by_name(self, typeobjname): """ Given a type object string e.g. "PyString_Type", locate the Region storing the PyTypeObject """ check_isinstance(typeobjname, str) # the C identifier of the global PyTypeObject for the type # Get the gcc.VarDecl for the global PyTypeObject typeobjdecl = compat.get_typeobject_decl_by_name(typeobjname) check_isinstance(typeobjdecl, gcc.VarDecl) typeobjregion = self.state.var_region(typeobjdecl) return typeobjregion def object_ctor(self, stmt, typename, typeobjname): """ Given a gcc.GimpleCall to a Python API function that returns a PyObject*, generate a (r_newobj, t_success, t_failure) triple, where r_newobj is a region, and success/failure are Transitions """ check_isinstance(stmt, gcc.GimpleCall) check_isinstance(stmt.fn.operand, gcc.FunctionDecl) check_isinstance(typename, str) # the C struct for the type check_isinstance(typeobjname, str) # the C identifier of the global PyTypeObject for the type fnname = stmt.fn.operand.name returntype = stmt.fn.type.dereference.type # (the region hierarchy is shared by all states, so we can get the # var region from "self", rather than "success") typeobjregion = self.typeobjregion_by_name(typeobjname) # The "success" case: s_success, nonnull = self.mkstate_new_ref(stmt, typename, typeobjregion) t_success = Transition(self.state, s_success, 'when %s() succeeds' % fnname) # The "failure" case: t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, 0), 'when %s() fails' % fnname) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return (nonnull, t_success, t_failure) def object_ctor_bytes(self, stmt): """ As per self.object_ctor(stmt, typename, typeobjname), returning: (r_newobj, t_success, t_failure) where the API entrypoint returns: * a PyStringObject in Python 2 * a PyBytesObject in Python 3 """ if is_py3k(): typename, typeobjname = 'PyBytesObject', 'PyBytes_Type' else: typename, typeobjname = 'PyStringObject', 'PyString_Type' return self.object_ctor(stmt, typename, typeobjname) def steal_reference(self, pyobjectptr, loc): def _steal_ref(v_old): # We have a value known relative to all of the refs owned by the # rest of the program. Given that the rest of the program is # stealing a ref, that is increasing by one, hence our value must # go down by one: return RefcountValue(loc, pyobjectptr.region, v_old.relvalue - 1, WithinRange(v_old.external.gcctype, loc, v_old.external.minvalue + 1, v_old.external.maxvalue + 1)) check_isinstance(pyobjectptr, PointerToRegion) self.change_refcount(pyobjectptr, loc, _steal_ref) def make_sane_object(self, stmt, name, v_refcount, r_typeobj=None): """ Modify this State, adding a new object. The ob_refcnt is set to the given value. The object has ob_type set to either the given typeobj, or a sane new one. Returns r_nonnull, a Region """ check_isinstance(stmt, gcc.Gimple) check_isinstance(name, str) check_isinstance(v_refcount, RefcountValue) # Claim a Region for the object: r_nonnull = self.state.make_heap_region(name, stmt) # Set up ob_refcnt to the given value: r_ob_refcnt = self.state.make_field_region(r_nonnull, 'ob_refcnt') # FIXME: this should be a memref and fieldref self.state.value_for_region[r_ob_refcnt] = v_refcount # If the RefcountValue doesn't have a Region yet, associate it # with that of the new object: if not v_refcount.r_obj: v_refcount.r_obj = r_nonnull # Ensure that the new object has a sane ob_type: if r_typeobj is None: # If no specific type object provided by caller, supply one: r_typeobj = Region('PyTypeObject for %s' % name, None) # it is its own region: self.state.region_for_var[r_typeobj] = r_typeobj # Set up obj->ob_type: ob_type = self.state.make_field_region(r_nonnull, 'ob_type') self.state.value_for_region[ob_type] = PointerToRegion(get_PyTypeObject().pointer, stmt.loc, r_typeobj) # Set up obj->ob_type->tp_dealloc: tp_dealloc = self.state.make_field_region(r_typeobj, 'tp_dealloc') type_of_tp_dealloc = gccutils.get_field_by_name(get_PyTypeObject().type, 'tp_dealloc').type self.state.value_for_region[tp_dealloc] = GenericTpDealloc(type_of_tp_dealloc, stmt.loc) return r_nonnull def mkstate_new_ref(self, stmt, name, typeobjregion=None): """ Make a new State, in which a new ref to some object has been assigned to the statement's LHS. Returns a pair: (newstate, RegionOnHeap for the new object) """ newstate = self.state.use_next_stmt_node() r_nonnull = newstate.cpython.make_sane_object(stmt, name, RefcountValue.new_ref(stmt.loc, None), typeobjregion) if stmt.lhs: newstate.assign(stmt.lhs, PointerToRegion(stmt.lhs.type, stmt.loc, r_nonnull), stmt.loc) # FIXME return newstate, r_nonnull def mkstate_borrowed_ref(self, stmt, fnmeta, r_typeobj=None): """Make a new State, giving a borrowed ref to some object""" check_isinstance(fnmeta, FnMeta) newstate = self.state.use_next_stmt_node() r_nonnull = newstate.cpython.make_sane_object(stmt, 'borrowed reference returned by %s()' % fnmeta.name, RefcountValue.borrowed_ref(stmt.loc, None), r_typeobj) if stmt.lhs: newstate.assign(stmt.lhs, PointerToRegion(stmt.lhs.type, stmt.loc, r_nonnull), stmt.loc) return newstate def mkstate_exception(self, stmt): """Make a new State, giving NULL and some exception""" if stmt.lhs: value = ConcreteValue(stmt.lhs.type, stmt.loc, 0) else: value = None t_failure = self.state.mktrans_assignment(stmt.lhs, value, None) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return t_failure.dest def make_transitions_for_new_ref_or_fail(self, stmt, fnmeta, objname=None): """ Generate the appropriate list of 2 transitions for a call to a function that either: - returns either a new ref, or - fails with NULL and sets an exception Optionally, a name for the new object can be supplied; otherwise a sane default will be used. """ if fnmeta: check_isinstance(fnmeta, FnMeta) if objname is None: objname = 'new ref from call to %s' % fnmeta.name s_success, nonnull = self.mkstate_new_ref(stmt, objname) s_failure = self.mkstate_exception(stmt) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def make_transitions_for_borrowed_ref_or_fail(self, stmt, fnmeta): """ Generate the appropriate list of 2 transitions for a call to a function that either: - returns either a borrowed ref, or - fails with NULL and sets an exception """ check_isinstance(fnmeta, FnMeta) s_success = self.mkstate_borrowed_ref(stmt, fnmeta) s_failure = self.mkstate_exception(stmt) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def object_ptr_has_global_ob_type(self, v_object_ptr, vardecl_name): """ Boolean: do we know that the given PyObject* has an ob_type matching the given global PyTypeObject (e.g. "PyString_Type") """ check_isinstance(v_object_ptr, AbstractValue) check_isinstance(vardecl_name, str) if isinstance(v_object_ptr, PointerToRegion): v_ob_type = self.state.get_value_of_field_by_region(v_object_ptr.region, 'ob_type') if isinstance(v_ob_type, PointerToRegion): if isinstance(v_ob_type.region, RegionForGlobal): if v_ob_type.region.vardecl.name == vardecl_name: return True def iter_python_refcounts(self): # yield a sequence of (Region, AbstractValue) pairs: # [...., (r_obj, v_ob_refcnt), ....] # corresponding to all of the PyObject* memory regions that we know # about, and their ob_refcnt values for var in self.state.region_for_var: check_isinstance(self.state.region_for_var[var], Region) r_obj = self.state.region_for_var[var] log('considering ob_refcnt of %r', r_obj) check_isinstance(r_obj, Region) # Consider those for which we know something about an "ob_refcnt" # field: if 'ob_refcnt' not in r_obj.fields: continue v_ob_refcnt = self.state.get_value_of_field_by_region(r_obj, 'ob_refcnt') yield (r_obj, v_ob_refcnt) def handle_null_error(self, stmt, idx, ptr, rawreturnvalue=0): # Handle Objects/abstract.c's null_error() # idx is the 0-based index of the argument check_isinstance(stmt, gcc.Gimple) check_isinstance(idx, int) check_isinstance(ptr, AbstractValue) if isinstance(ptr, UnknownValue): self.state.raise_split_value(ptr, stmt.loc) if ptr.is_null_ptr(): if stmt.lhs: # null_error() returns a NULL PyObject* # some callsites where the fn returns a PyObject* have: # return null_error() # but others where the fn returns an int have: # null_error() # return -1 value = ConcreteValue(stmt.lhs.type, stmt.loc, rawreturnvalue) else: value = None t_failure = self.state.mktrans_assignment(stmt.lhs, value, None) # null_error() sets PyExc_SystemError if (!PyErr_Occurred()): if t_failure.dest.cpython.exception_rvalue.is_null_ptr(): t_failure.desc = ('when %s raises SystemError due to' ' NULL as argument %i at %s' % (stmt.fn, idx + 1, stmt.loc)) t_failure.dest.cpython.set_exception('PyExc_SystemError', stmt.loc) else: t_failure.desc = ('when %s fails due to' ' NULL as argument %i at %s' % (stmt.fn, idx + 1, stmt.loc)) return t_failure # otherwise, implicit return of None to signify no problems def handle_BadInternalCall_on_null(self, stmt, idx, ptr, v_return): # various API calls have code of the form: # if (ptr == NULL) { # PyErr_BadInternalCall(); # return NULL; # } # idx is the 0-based index of the argument check_isinstance(stmt, gcc.Gimple) check_isinstance(idx, int) check_isinstance(ptr, AbstractValue) if isinstance(ptr, UnknownValue): self.state.raise_split_value(ptr, stmt.loc) if ptr.is_null_ptr(): t_failure = self.state.mktrans_assignment(stmt.lhs, v_return, None) t_failure.desc = ('when %s raises SystemError (via' ' PyErr_BadInternalCall) due to' ' NULL as argument %i at %s' % (stmt.fn, idx + 1, stmt.loc)) t_failure.dest.cpython.bad_internal_call(stmt.loc) return t_failure # otherwise, implicit return of None to signify no problems # Treat calls to various function prefixed with __cpychecker as special, # to help with debugging, and when writing selftests: def impl___cpychecker_log(self, stmt, *args): """ Assuming a C function with this declaration: extern void __cpychecker_log(const char *); and that it is called with a string constant, log the message within the trace. """ returntype = stmt.fn.type.dereference.type desc = args[0].as_string_constant() return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), desc)] def impl___cpychecker_dump(self, stmt, *args): returntype = stmt.fn.type.dereference.type # Give the transition a description that embeds the argument values # This will show up in selftests (and in error reports that embed # traces) desc = '__dump(%s)' % (','.join([str(arg) for arg in args])) return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), desc)] def impl___cpychecker_dump_all(self, stmt, *args): """ Dump all of our state to stdout, to help with debugging """ print(str(stmt.loc)) print(self.state.as_str_table()) returntype = stmt.fn.type.dereference.type return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), None)] def impl___cpychecker_assert_equal(self, stmt, *args): """ Assuming a C function with this declaration: extern void __cpychecker_assert_equal(T, T); for some type T, raise an exception within the checker if the two arguments are non-equal (for use in writing selftests). """ returntype = stmt.fn.type.dereference.type # Give the transition a description that embeds the argument values # This will show up in selftests (and in error reports that embed # traces) if args[0] != args[1]: raise AssertionError('%s != %s' % (args[0], args[1])) desc = '__cpychecker_assert_equal(%s)' % (','.join([str(arg) for arg in args])) return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), desc)] # Specific Python API function implementations # (keep this list alphabetized, discounting case and underscores) ######################################################################## # PyArg_* ######################################################################## def _handle_PyArg_function(self, stmt, fnmeta, v_fmt, v_varargs, with_size_t): """ Handle one of the various PyArg_Parse* functions """ check_isinstance(v_fmt, AbstractValue) check_isinstance(v_varargs, tuple) # of AbstractValue check_isinstance(with_size_t, bool) s_success = self.state.mkstate_concrete_return_of(stmt, 1) s_failure = self.state.mkstate_concrete_return_of(stmt, 0) # Various errors are possible, but a TypeError is always possible # e.g. for the case of the wrong number of arguments: s_failure.cpython.set_exception('PyExc_TypeError', stmt.loc) # Parse the format string, and figure out what the effects of a # successful parsing are: def _get_new_value_for_vararg(unit, exptype): if unit.code == 'O': # non-NULL sane PyObject*: return PointerToRegion(exptype.dereference, stmt.loc, self.make_sane_object(stmt, 'object from arg "O"', RefcountValue.borrowed_ref(stmt.loc, None))) if unit.code == 'O!': if isinstance(exptype, TypeCheckCheckerType): # This is read from, not written to: return None if isinstance(exptype, TypeCheckResultType): # non-NULL sane PyObject* # FIXME: we could perhaps set ob_type to the given type. # However "O!" only enforces the weaker condition: # if (PyType_IsSubtype(arg->ob_type, type)) return PointerToRegion(get_PyObjectPtr(), stmt.loc, self.make_sane_object(stmt, 'object from arg "O!"', RefcountValue.borrowed_ref(stmt.loc, None))) if unit.code == 'O&': # Assume for now that conversion succeeds if isinstance(exptype, ConverterCallbackType): # This is read from, not written to: return None if isinstance(exptype, ConverterResultType): return UnknownValue.make(exptype.type, stmt.loc) # Unknown value: check_isinstance(exptype, gcc.PointerType) return UnknownValue.make(exptype.dereference, stmt.loc) def _handle_successful_parse(fmt): exptypes = fmt.iter_exp_types() for v_vararg, (unit, exptype) in zip(v_varargs, exptypes): if 0: print('v_vararg: %r' % v_vararg) print(' unit: %r' % unit) print(' exptype: %r %s' % (exptype, exptype)) if isinstance(v_vararg, PointerToRegion): v_new = _get_new_value_for_vararg(unit, exptype) if v_new: check_isinstance(v_new, AbstractValue) s_success.value_for_region[v_vararg.region] = v_new fmt_string = v_fmt.as_string_constant() if fmt_string: try: fmt = PyArgParseFmt.from_string(fmt_string, with_size_t) _handle_successful_parse(fmt) except FormatStringWarning: pass return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyArg_Parse(self, stmt, v_args, v_fmt, *v_varargs): fnmeta = FnMeta(name='PyArg_Parse', docurl='http://docs.python.org/c-api/arg.html#PyArg_Parse', declared_in='modsupport.h', prototype='PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...);',) # Also, with #ifdef PY_SSIZE_T_CLEAN # #define PyArg_Parse _PyArg_Parse_SizeT return self._handle_PyArg_function(stmt, fnmeta, v_fmt, v_varargs, with_size_t=False) def impl__PyArg_Parse_SizeT(self, stmt, v_args, v_fmt, *v_varargs): fnmeta = FnMeta(name='_PyArg_Parse_SizeT', docurl='http://docs.python.org/c-api/arg.html#PyArg_Parse', declared_in='modsupport.h', prototype='PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...);',) # Also, with #ifdef PY_SSIZE_T_CLEAN # #define PyArg_Parse _PyArg_Parse_SizeT return self._handle_PyArg_function(stmt, fnmeta, v_fmt, v_varargs, with_size_t=True) def impl_PyArg_ParseTuple(self, stmt, v_args, v_fmt, *v_varargs): fnmeta = FnMeta(name='PyArg_ParseTuple', docurl='http://docs.python.org/c-api/arg.html#PyArg_ParseTuple', declared_in='modsupport.h', prototype='PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);',) # Also, with #ifdef PY_SSIZE_T_CLEAN # #define PyArg_ParseTuple _PyArg_ParseTuple_SizeT return self._handle_PyArg_function(stmt, fnmeta, v_fmt, v_varargs, with_size_t=False) def impl__PyArg_ParseTuple_SizeT(self, stmt, v_args, v_fmt, *v_varargs): fnmeta = FnMeta(name='_PyArg_ParseTuple_SizeT', docurl='http://docs.python.org/c-api/arg.html#PyArg_ParseTuple', declared_in='modsupport.h', prototype='PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);',) # Also, with #ifdef PY_SSIZE_T_CLEAN # #define PyArg_ParseTuple _PyArg_ParseTuple_SizeT return self._handle_PyArg_function(stmt, fnmeta, v_fmt, v_varargs, with_size_t=True) def impl_PyArg_ParseTupleAndKeywords(self, stmt, v_args, v_kwargs, v_fmt, v_keywords, *v_varargs): fnmeta = FnMeta(name='PyArg_ParseTupleAndKeywords', docurl='http://docs.python.org/c-api/arg.html#PyArg_ParseTupleAndKeywords', declared_in='modsupport.h', prototype=('PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,\n' ' const char *, char **, ...);'),) # # Also, with #ifdef PY_SSIZE_T_CLEAN # #define PyArg_ParseTupleAndKeywords _PyArg_ParseTupleAndKeywords_SizeT return self._handle_PyArg_function(stmt, fnmeta, v_fmt, v_varargs, with_size_t=False) def impl__PyArg_ParseTupleAndKeywords_SizeT(self, stmt, v_args, v_kwargs, v_fmt, v_keywords, *v_varargs): fnmeta = FnMeta(name='_PyArg_ParseTupleAndKeywords_SizeT', docurl='http://docs.python.org/c-api/arg.html#PyArg_ParseTupleAndKeywords', declared_in='modsupport.h', prototype=('PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,\n' ' const char *, char **, ...);'),) # # Also, with #ifdef PY_SSIZE_T_CLEAN # #define PyArg_ParseTupleAndKeywords _PyArg_ParseTupleAndKeywords_SizeT return self._handle_PyArg_function(stmt, fnmeta, v_fmt, v_varargs, with_size_t=True) def impl_PyArg_UnpackTuple(self, stmt, v_args, v_name, v_min, v_max, *v_varargs): fnmeta = FnMeta(name='PyArg_UnpackTuple', docurl='http://docs.python.org/c-api/arg.html#PyArg_UnpackTuple', declared_in='modsupport.h', prototype=('PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *,\n' ' Py_ssize_t, Py_ssize_t, ...);'), defined_in='Python/getargs.c') # int # PyArg_UnpackTuple(PyObject *args, const char *name, # Py_ssize_t min, Py_ssize_t max, ...) # # Will only write betweeen v_min..v_max values back # For now, assume that we can figure out min and max during the # analysis: check_isinstance(v_min, ConcreteValue) check_isinstance(v_max, ConcreteValue) # for arg in v_varargs: # print arg # Detect wrong number of arguments: if len(v_varargs) != v_max.value: class WrongNumberOfVarargs(PredictedError): def __init__(self, v_max, v_varargs): self.v_max = v_max self.v_varargs = v_varargs def __str__(self): return ('expected %i vararg pointer(s); got %i' % (v_max.value, len(v_varargs))) raise WrongNumberOfVarargs(v_max, v_varargs) s_failure = self.state.mkstate_concrete_return_of(stmt, 0) # Various errors are possible, but a TypeError is always possible # e.g. for the case of the wrong number of arguments: s_failure.cpython.set_exception('PyExc_TypeError', stmt.loc) result = [self.state.mktrans_from_fncall_state(stmt, s_failure, 'fails', has_siblings=True)] # Enumerate all possible successes, to detect the case where an # optional argument doesn't get initialized for numargs in range(v_min.value, v_max.value + 1): s_success = self.state.mkstate_concrete_return_of(stmt, 1) result.append(self.state.mktrans_from_fncall_state( stmt, s_success, 'successfully unpacks %i argument(s)' % numargs, has_siblings=True)) # Write sane objects to each location that gets written to, # given this number of arguments: for i in range(numargs): vararg = v_varargs[i] if isinstance(vararg, PointerToRegion): # Write back a sane object: v_obj = PointerToRegion(get_PyObjectPtr(), stmt.loc, s_success.cpython.make_sane_object(stmt, 'argument %i' % (i + 1), RefcountValue.borrowed_ref(stmt.loc, None))) s_success.value_for_region[vararg.region] = v_obj return result ######################################################################## def impl_Py_AtExit(self, stmt, v_func): fnmeta = FnMeta(name='Py_AtExit', docurl='http://docs.python.org/c-api/sys.html#Py_AtExit') # Dummy implementation t_return = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(stmt.lhs.type, stmt.loc), 'when %s() returns' % fnmeta.name) return [t_return] ######################################################################## # PyBool_* ######################################################################## def impl_PyBool_FromLong(self, stmt, v_long): fnmeta = FnMeta(name='PyBool_FromLong', docurl='http://docs.python.org/c-api/bool.html#PyBool_FromLong', declared_in='boolobject.h', prototype='PyAPI_FUNC(PyObject *) PyBool_FromLong(long);', defined_in='Objects/boolobject.c', notes=('Always succeeds, returning a new ref to one of' ' the two singleton booleans')) # v_ok = self.state.eval_stmt_args(stmt)[0] s_success, r_nonnull = self.mkstate_new_ref(stmt, 'PyBool_FromLong') return [self.state.mktrans_from_fncall_state(stmt, s_success, 'returns', False)] ######################################################################## # Py_BuildValue* ######################################################################## def _handle_Py_BuildValue(self, stmt, fnmeta, v_fmt, v_varargs, with_size_t): """ Handle one of the various Py_BuildValue functions http://docs.python.org/c-api/arg.html#Py_BuildValue We don't try to model the resulting object, just the success/failure and the impact on the refcounts of any inputs """ check_isinstance(v_fmt, AbstractValue) check_isinstance(v_varargs, tuple) # of AbstractValue check_isinstance(with_size_t, bool) # The function can succeed or fail # If any of the PyObject* inputs are NULL, it is doomed to failure def _handle_successful_parse(fmt): """ Returns a boolean: is success of the function possible? """ exptypes = fmt.iter_exp_types() for v_vararg, (unit, exptype) in zip(v_varargs, exptypes): if 0: print('v_vararg: %r' % v_vararg) print(' unit: %r' % unit) print(' exptype: %r %s' % (exptype, exptype)) if isinstance(unit, ObjectFormatUnit): # NULL inputs ptrs guarantee failure: if isinstance(v_vararg, ConcreteValue): if v_vararg.is_null_ptr(): # The call will fail: return False # non-NULL input ptrs receive "external" references on # success for codes "S" and "O": if isinstance(v_vararg, PointerToRegion): if isinstance(unit, CodeSO): t_success.dest.cpython.add_external_ref(v_vararg, stmt.loc) else: t_success.dest.cpython.steal_reference(v_vararg, stmt.loc) return True t_success, t_failure = self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) fmt_string = v_fmt.as_string_constant() if fmt_string: try: fmt = PyBuildValueFmt.from_string(fmt_string, with_size_t) if not _handle_successful_parse(fmt): return [t_failure] except FormatStringWarning: pass return [t_success, t_failure] def impl_Py_BuildValue(self, stmt, v_fmt, *args): fnmeta = FnMeta(name='Py_BuildValue', docurl='http://docs.python.org/c-api/arg.html#Py_BuildValue', declared_in='modsupport.h', prototype='PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);', defined_in='Python/modsupport.c') # PyObject * # Py_BuildValue(const char *format, ...) # return self._handle_Py_BuildValue(stmt, fnmeta, v_fmt, args, with_size_t=False) def impl__Py_BuildValue_SizeT(self, stmt, v_fmt, *args): fnmeta = FnMeta(name='_Py_BuildValue_SizeT', docurl='http://docs.python.org/c-api/arg.html#Py_BuildValue', declared_in='modsupport.h', # #ifdef PY_SSIZE_T_CLEAN # #define Py_BuildValue _Py_BuildValue_SizeT # #endif # prototype='PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);', defined_in='Python/modsupport.c') # PyObject * # _Py_BuildValue_SizeT(const char *format, ...) # return self._handle_Py_BuildValue(stmt, fnmeta, v_fmt, args, with_size_t=True) ######################################################################## def impl_PyCallable_Check(self, stmt, v_o): fnmeta = FnMeta(name='PyCallable_Check', docurl='http://docs.python.org/c-api/object.html#PyCallable_Check', defined_in='Objects/object.c', # not abstract.c notes='Always succeeds') # robust against NULL s_true = self.state.mkstate_concrete_return_of(stmt, 1) s_false = self.state.mkstate_concrete_return_of(stmt, 0) return [Transition(self.state, s_true, fnmeta.desc_when_call_returns_value('1 (true)')), Transition(self.state, s_false, fnmeta.desc_when_call_returns_value('0 (false)'))] ######################################################################## # PyCapsule_* ######################################################################## def impl_PyCapsule_GetPointer(self, stmt, v_capsule, v_name): fnmeta = FnMeta(name='PyCapsule_GetPointer', docurl='http://docs.python.org/c-api/capsule.html#PyCapsule_GetPointer', prototype='void* PyCapsule_GetPointer(PyObject *capsule, const char *name)', defined_in='Objects/capsule.c') # either returns NULL, setting an exception, or returns non-NULL t_success = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(stmt.lhs.type, stmt.loc), fnmeta.desc_when_call_succeeds()) t_failure = self.state.mktrans_assignment(stmt.lhs, make_null_ptr(stmt.lhs.type, stmt.loc), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_ValueError', stmt.loc) return [t_success, t_failure] ######################################################################## # PyCObject_* ######################################################################## def impl_PyCObject_AsVoidPtr(self, stmt, v_self): fnmeta = FnMeta(name='PyCObject_AsVoidPtr', docurl='http://docs.python.org/c-api/cobject.html#PyCObject_AsVoidPtr', declared_in='cobject.h', prototype='void * PyCObject_AsVoidPtr(PyObject *self)', defined_in='Objects/cobject.c') # TODO: robust against NULL ptr, lazily setting exception returntype = stmt.fn.type.dereference.type t_result = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), 'when %s() returns' % fnmeta.name) return [t_result] def mktrans_cobject_deprecation_warning(self, fnmeta, stmt): """ Generate a Transition simulating the outcome of these clauses: if (cobject_deprecation_warning()) { return NULL; } in CPython 2.7's Objects/cobject.c This indicates that Py_Py3kWarningFlag is enabled, and that the warnings have been so configured (perhaps in other code in the process) as to trigger an exception, leading to a NULL return. Plenty of legacy code doesn't expect a NULL return from these APIs, alas. Include/warnings.h has: #define PyErr_WarnPy3k(msg, stacklevel) \ (Py_Py3kWarningFlag ? PyErr_WarnEx(PyExc_DeprecationWarning, msg, stacklevel) : 0) Python/_warnings.c defines PyErr_WarnEx """ s_deprecation = self.mkstate_exception(stmt) t_deprecation = Transition(self.state, s_deprecation, desc=fnmeta.desc_when_call_fails( why=('when py3k deprecation warnings are enabled and configured' ' to raise exceptions'))) return t_deprecation def impl_PyCObject_FromVoidPtr(self, stmt, v_cobj, v_destr): fnmeta = FnMeta(name='PyCObject_FromVoidPtr', docurl='http://docs.python.org/c-api/cobject.html#PyCObject_FromVoidPtr', declared_in='cobject.h', prototype='PyObject* PyCObject_FromVoidPtr(void* cobj, void (*destr)(void *))', defined_in='Objects/cobject.c', notes='Deprecated API') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyCObject', 'PyCObject_Type') return [t_success, # Prioritize the more interesting failure over regular malloc # failure, so that it doesn't disapper in de-duplication: self.mktrans_cobject_deprecation_warning(fnmeta, stmt), t_failure] def impl_PyCObject_FromVoidPtrAndDesc(self, stmt, v_cobj, v_desc, v_destr): fnmeta = FnMeta(name='PyCObject_FromVoidPtrAndDesc', docurl='http://docs.python.org/c-api/cobject.html#PyCObject_FromVoidPtrAndDesc', declared_in='cobject.h', prototype=('PyAPI_FUNC(PyObject *) PyCObject_FromVoidPtrAndDesc(\n' ' void *cobj, void *desc, void (*destruct)(void*,void*));'), defined_in='Objects/cobject.c', notes='Deprecated API') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyCObject', 'PyCObject_Type') return [t_success, # Prioritize the more interesting failure over regular malloc # failure, so that it doesn't disapper in de-duplication: self.mktrans_cobject_deprecation_warning(fnmeta, stmt), t_failure] ######################################################################## # PyCode_* ######################################################################## def impl_PyCode_New(self, stmt, v_argcount, v_nlocals, v_stacksize, v_flags, v_code, v_consts, v_names, v_varnames, v_freevars, v_cellvars, v_filename, v_name, v_firstlineno, v_lnotab): fnmeta = FnMeta(name='PyCode_New', docurl='http://docs.python.org/c-api/code.html#PyCode_New', declared_in='code.h', prototype=('PyCodeObject *\n' 'PyCode_New(int argcount, int nlocals, int stacksize, int flags,\n' ' PyObject *code, PyObject *consts, PyObject *names,\n' ' PyObject *varnames, PyObject *freevars, PyObject *cellvars,\n' ' PyObject *filename, PyObject *name, int firstlineno,\n' ' PyObject *lnotab);'), defined_in='Objects/codeobject.c') # (used by Cython-generated code in static void __Pyx_AddTraceback in # each file) # For now, ignore the effects on the input variables: r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyCodeObject', 'PyCode_Type') return [t_success, t_failure] ######################################################################## # PyDict_* ######################################################################## def impl_PyDict_GetItem(self, stmt, v_mp, v_key): fnmeta = FnMeta(name='PyDict_GetItem', docurl='http://docs.python.org/c-api/dict.html#PyDict_GetItem', declared_in='dictobject.h', prototype='PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);', defined_in='Objects/dictobject.c', notes=('Returns a borrowed reference, or NULL if not' ' found. It does *not* set an exception (for' ' historical reasons)')) self.state.raise_any_null_ptr_func_arg(stmt, 0, v_mp, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyDict_Check')) self.state.raise_any_null_ptr_func_arg(stmt, 1, v_key, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_CheckExact')) s_success = self.mkstate_borrowed_ref(stmt, fnmeta) t_notfound = self.state.mktrans_assignment(stmt.lhs, make_null_pyobject_ptr(stmt), 'when PyDict_GetItem does not find item') return [self.state.mktrans_from_fncall_state(stmt, s_success, 'succeeds', True), t_notfound] def impl_PyDict_GetItemString(self, stmt, v_dp, v_key): fnmeta = FnMeta(name='PyDict_GetItemString', docurl='http://docs.python.org/c-api/dict.html#PyDict_GetItemString', declared_in='dictobject.h', prototype='PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key);', defined_in='Objects/dictobject.c', notes=('Returns a borrowed ref, or NULL if not found' ' (can also return NULL and set MemoryError)')) self.state.raise_any_null_ptr_func_arg(stmt, 0, v_dp, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyDict_Check')) # (within PyDict_GetItem) self.state.raise_any_null_ptr_func_arg(stmt, 1, v_key, why='%s() invokes PyString_FromString()' % fnmeta.name) s_success = self.mkstate_borrowed_ref(stmt, fnmeta) t_notfound = self.state.mktrans_assignment(stmt.lhs, make_null_pyobject_ptr(stmt), 'PyDict_GetItemString does not find string') if 0: t_memoryexc = self.state.mktrans_assignment(stmt.lhs, make_null_pyobject_ptr(stmt), 'OOM allocating string') # FIXME: set exception return [self.state.mktrans_from_fncall_state(stmt, s_success, 'succeeds', True), t_notfound] #t_memoryexc] def impl_PyDict_New(self, stmt): fnmeta = FnMeta(name='PyDict_New', docurl='http://docs.python.org/c-api/dict.html#PyDict_New') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyDictObject', 'PyDict_Type') return [t_success, t_failure] def _handle_PyDict_SetItem(self, stmt, fnmeta, v_dp, v_key, v_item): s_success = self.state.mkstate_concrete_return_of(stmt, 0) # the dictionary now owns a new ref on "item". We won't model the # insides of the dictionary type. Instead, treat it as a new # external reference: s_success.cpython.add_external_ref(v_item, stmt.loc) s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyDict_SetItem(self, stmt, v_dp, v_key, v_item): fnmeta = FnMeta(name='PyDict_SetItem', declared_in='dictobject.h', prototype='PyAPI_FUNC(int) PyDict_SetItem(PyObject *mp, PyObject *key, PyObject *item);', defined_in='Objects/dictobject.c', docurl='http://docs.python.org/c-api/dict.html#PyDict_SetItem', notes='Can return -1, setting MemoryError. Otherwise returns 0, and adds a ref on the value') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_dp, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyDict_Check')) self.state.raise_any_null_ptr_func_arg(stmt, 1, v_key, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_CheckExact')) self.state.raise_any_null_ptr_func_arg(stmt, 2, v_item, why=invokes_Py_INCREF(fnmeta)) return self._handle_PyDict_SetItem(stmt, fnmeta, v_dp, v_key, v_item) def impl_PyDict_SetItemString(self, stmt, v_dp, v_key, v_item): fnmeta = FnMeta(name='PyDict_SetItemString', declared_in='dictobject.h', prototype='PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item);', defined_in='Objects/dictobject.c', docurl='http://docs.python.org/c-api/dict.html#PyDict_SetItemString', notes=('Can return -1, setting MemoryError.' ' Otherwise returns 0, and adds a ref on the value')) # This is implemented in terms of PyDict_SetItem and shows the same # success and failures: self.state.raise_any_null_ptr_func_arg(stmt, 1, v_key) self.state.raise_any_null_ptr_func_arg(stmt, 2, v_item) # (strictly speaking, v_key goes from being a char* to a PyObject*) return self._handle_PyDict_SetItem(stmt, fnmeta, v_dp, v_key, v_item) def impl_PyDict_Size(self, stmt, v_mp): fnmeta = FnMeta(name='PyDict_Size', declared_in='dictobject.h', prototype='Py_ssize_t PyDict_Size(PyObject *mp);', defined_in='Objects/dictobject.c', docurl='http://docs.python.org/c-api/dict.html#PyDict_Size') # Explicitly checks for NULL (or not a dict) # with PyErr_BadInternalCall(); return -1 t_err = self.handle_BadInternalCall_on_null(stmt, 0, v_mp, ConcreteValue(stmt.lhs.type, stmt.loc, -1)) if t_err: return [t_err] # FIXME: doesn't yet handle the !PyDict_Check(mp) case returntype = stmt.fn.type.dereference.type v_ma_used = self.state.read_field_by_name(stmt, returntype, v_mp.region, 'ma_used') t_return = self.state.mktrans_assignment(stmt.lhs, v_ma_used, fnmeta.desc_when_call_returns_value('ma_used')) return [t_return] ######################################################################## # PyErr_* ######################################################################## def impl_PyErr_Clear(self, stmt): fnmeta = FnMeta(name='PyErr_Clear', # FIXME docurl='http://docs.python.org/c-api/exceptions.html#PyErr_Clear', declared_in='pyerrors.h', prototype='PyAPI_FUNC(void) PyErr_Clear(void);', defined_in='Python/errors.c') # equiv to PyErr_Restore(NULL, NULL, NULL) t_next = self.state.mktrans_nop(stmt, fnmeta.name) t_next.dest.cpython.exception_rvalue = make_null_pyobject_ptr(stmt) return [t_next] def impl_PyErr_Format(self, stmt, v_exc, v_fmt, *v_args): fnmeta = FnMeta(name='PyErr_Format', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_Format', declared_in='pyerrors.h', prototype='PyAPI_FUNC(void) PyErr_SetString(PyObject *, const char *);', defined_in='Python/errors.c', notes='Always returns NULL',) t_next = self.state.mktrans_assignment(stmt.lhs, make_null_pyobject_ptr(stmt), 'PyErr_Format()') t_next.dest.cpython.exception_rvalue = v_exc return [t_next] def impl_PyErr_NewException(self, stmt, v_name, v_base, v_dict): fnmeta = FnMeta(name='PyErr_NewException', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_NewException', prototype=('PyObject*\n' 'Err_NewException(char *name, PyObject *base, PyObject *dict)'), defined_in='Python/errors.c', notes='Return value: New reference (or NULL e.g. MemoryError)') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_name) # "base" and "dict" may be NULL return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta, objname='new exception object from %s' % fnmeta.name) def impl_PyErr_NoMemory(self, stmt): fnmeta = FnMeta(name='PyErr_NoMemory', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_NoMemory', declared_in='pyerrors.h', prototype='PyAPI_FUNC(PyObject *) PyErr_NoMemory(void);', defined_in='Python/errors.c', notes='Always returns NULL',) t_next = self.state.mktrans_assignment(stmt.lhs, make_null_pyobject_ptr(stmt), ('PyErr_NoMemory() returns NULL,' ' raising MemoryError')) t_next.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_next] def impl_PyErr_Occurred(self, stmt): fnmeta = FnMeta(name='PyErr_Occurred', declared_in='pyerrors.h', prototype='PyAPI_FUNC(PyObject *) PyErr_Occurred(void);', defined_in='Python/errors.c', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_Occurred', notes="Returns a borrowed reference; can't fail") t_next = self.state.mktrans_assignment(stmt.lhs, self.exception_rvalue, 'PyErr_Occurred()') return [t_next] def impl_PyErr_Print(self, stmt): fnmeta = FnMeta(name='PyErr_Print', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_Print', declared_in='pythonrun.h', prototype='PyAPI_FUNC(void) PyErr_Print(void);', defined_in='Python/pythonrun.c',) fncall = FunctionCall(self.state, stmt, fnmeta) always = fncall.always() # Clear the error indicator: always.sets_exception_ptr(make_null_pyobject_ptr(stmt)) return fncall.get_transitions() def impl_PyErr_PrintEx(self, stmt, v_int): fnmeta = FnMeta(name='PyErr_PrintEx', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_PrintEx', declared_in='pythonrun.h', prototype='PyAPI_FUNC(void) PyErr_PrintEx(int);', defined_in='Python/pythonrun.c',) fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_int, )) always = fncall.always() # Clear the error indicator: always.sets_exception_ptr(make_null_pyobject_ptr(stmt)) return fncall.get_transitions() def impl_PyErr_SetFromErrno(self, stmt, v_exc): fnmeta = FnMeta(name='PyErr_SetFromErrno', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_SetFromErrno', notes='Always returns NULL',) fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_exc, )) always = fncall.always() always.returns_NULL() always.sets_exception_ptr(v_exc) return fncall.get_transitions() def impl_PyErr_SetFromErrnoWithFilename(self, stmt, v_exc, v_filename): fnmeta = FnMeta(name='PyErr_SetFromErrnoWithFilename', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_SetFromErrnoWithFilename', defined_in='Python/errors.c', notes='Always returns NULL',) # PyObject * # PyErr_SetFromErrnoWithFilename(PyObject *exc, const char *filename) # # "filename" can be NULL. fncall = FunctionCall(self.state, stmt, fnmeta) always = fncall.always() always.returns_NULL() always.sets_exception_ptr(v_exc) return fncall.get_transitions() def impl_PyErr_SetNone(self, stmt, v_exc): fnmeta = FnMeta(name='PyErr_SetNone', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_SetNone', defined_in='Python/errors.c',) # void # PyErr_SetNone(PyObject *exception) # It's acceptable for v_exc to be NULL fncall = FunctionCall(self.state, stmt, fnmeta) always = fncall.always() always.sets_exception_ptr(v_exc) always.adds_external_ref(v_exc) return fncall.get_transitions() def impl_PyErr_SetObject(self, stmt, v_exc, v_value): fnmeta = FnMeta(name='PyErr_SetObject', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_SetObject', declared_in='pyerrors.h', prototype='PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *);', defined_in='Python/errors.c',) # void # PyErr_SetObject(PyObject *exception, PyObject *value) # # It's acceptable for each of v_exc and v_value to be NULL fncall = FunctionCall(self.state, stmt, fnmeta) always = fncall.always() always.sets_exception_ptr(v_exc) always.adds_external_ref(v_exc) always.adds_external_ref(v_value) return fncall.get_transitions() def impl_PyErr_SetString(self, stmt, v_exc, v_string): fnmeta = FnMeta(name='PyErr_SetString', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_SetString', declared_in='pyerrors.h', prototype='PyAPI_FUNC(void) PyErr_SetString(PyObject *, const char *);', defined_in='Python/errors.c',) fncall = FunctionCall(self.state, stmt, fnmeta) always = fncall.always() always.sets_exception_ptr(v_exc) return fncall.get_transitions() def impl_PyErr_WarnEx(self, stmt, v_category, v_text, v_stack_level): fnmeta = FnMeta(name='PyErr_WarnEx', docurl='http://docs.python.org/c-api/exceptions.html#PyErr_WarnEx', # int # PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level) # returns 0 on OK # returns -1 if an exception is raised defined_in='Python/_warnings.c') fncall = FunctionCall(self.state, stmt, fnmeta) on_success = fncall.can_succeed() on_success.returns(0) on_failure = fncall.can_fail() on_failure.returns(-1) on_failure.sets_exception('PyExc_MemoryError') return fncall.get_transitions() ######################################################################## # PyEval_InitThreads() ######################################################################## def impl_PyEval_CallMethod(self, stmt, v_obj, v_method, v_fmt, *v_varargs): fnmeta = FnMeta(name='PyEval_CallMethod', prototype=('PyObject *\n' 'PyEval_CallMethod(PyObject *obj, const char *methodname, const char *format, ...)'), declared_in='ceval.h', defined_in='Python/modsupport.c') fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_obj, v_method, v_fmt), varargs=v_varargs) fncall.crashes_on_null_arg(0, why=invokes_Py_TYPE(fnmeta, within='PyObject_GetAttrString')) # not affected by PY_SSIZE_T_CLEAN self._handle_PyObject_CallMethod(fncall, 2, with_size_t=False) return fncall.get_transitions() def impl_PyEval_CallObjectWithKeywords(self, stmt, v_func, v_arg, v_kw): fnmeta = FnMeta(name='PyEval_CallObjectWithKeywords', prototype=('PyObject *\n' 'PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw)'), defined_in='Python/ceval.c') fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_func, v_arg, v_kw)) fncall.crashes_on_null_arg(0, why='looks up func->ob_type within inner call to PyObject_Call()') # arg and kw can each be NULL, though fncall.new_ref_or_fail() return fncall.get_transitions() def impl_PyEval_InitThreads(self, stmt): fnmeta = FnMeta(name='PyEval_InitThreads', docurl='http://docs.python.org/c-api/init.html#PyEval_InitThreads') # For now, treat it as a no-op: return [self.state.mktrans_nop(stmt, 'PyEval_InitThreads')] def impl_PyEval_RestoreThread(self, stmt, v_tstate): fnmeta = FnMeta(name='PyEval_RestoreThread', docurl='http://docs.python.org/c-api/init.html#PyEval_RestoreThread', prototype='void PyEval_RestoreThread(PyThreadState *tstate)', defined_in='Python/ceval.c', notes='Reclaims the GIL') t_success = self.state.mktrans_nop(stmt, fnmeta.name) t_success.desc = 'reacquiring the GIL by calling %s()' % fnmeta.name # Acquire the GIL: t_success.dest.cpython.has_gil = True return [t_success] def impl_PyEval_SaveThread(self, stmt): fnmeta = FnMeta(name='PyEval_SaveThread', docurl='http://docs.python.org/c-api/init.html#PyEval_SaveThread', prototype='PyThreadState* PyEval_SaveThread()', defined_in='Python/ceval.c', notes='Releases the GIL') returntype = stmt.fn.type.dereference.type t_success = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), 'releasing the GIL by calling %s()' % fnmeta.name) # Release the GIL: t_success.dest.cpython.has_gil = False return [t_success] ######################################################################## # Py_FatalError() ######################################################################## def impl_Py_FatalError(self, stmt, v_message): fnmeta = FnMeta(name='Py_FatalError', docurl='http://docs.python.org/c-api/sys.html#Py_FatalError', prototype='void Py_FatalError(const char *message)') fncall = FunctionCall(self.state, stmt, fnmeta) fncall.never_returns() return fncall.get_transitions() ######################################################################## # PyFile_* ######################################################################## def impl_PyFile_SoftSpace(self, stmt, v_f, v_newflag): fnmeta = FnMeta(name='PyFile_SoftSpace', docurl='http://docs.python.org/c-api/file.html#PyFile_SoftSpace', defined_in='Objects/fileobject.c') # used in Cython-generated code, in static int __Pyx_Print() returntype = stmt.fn.type.dereference.type return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), fnmeta.name)] def impl_PyFile_WriteObject(self, stmt, v_obj, v_p, v_flags): fnmeta = FnMeta(name='PyFile_WriteObject', docurl='http://docs.python.org/c-api/file.html#PyFile_WriteObject', prototype='int PyFile_WriteObject(PyObject *obj, PyObject *p, int flags)', defined_in='Objects/fileobject.c') # used in Cython-generated code, in static int __Pyx_Print() returntype = stmt.fn.type.dereference.type # FIXME: gracefully handles NULL for second argument, but not for first s_success = self.state.mkstate_concrete_return_of(stmt, 0) s_failure = self.state.mkstate_concrete_return_of(stmt, -1) # Various errors can happen; this is just one: s_failure.cpython.set_exception('PyExc_IOError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyFile_WriteString(self, stmt, v_s, v_p): fnmeta = FnMeta(name='PyFile_WriteString', docurl='http://docs.python.org/c-api/file.html#PyFile_WriteString', defined_in='Objects/fileobject.c') # FIXME: gracefully handles NULL for second argument, but not for first s_success = self.state.mkstate_concrete_return_of(stmt, 0) s_failure = self.state.mkstate_concrete_return_of(stmt, -1) # Various errors can happen; this is just one: s_failure.cpython.set_exception('PyExc_IOError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) ######################################################################## # Py_Finalize() ######################################################################## def impl_Py_Finalize(self, stmt): fnmeta = FnMeta(name='Py_Finalize', docurl='http://docs.python.org/c-api/init.html#Py_Finalize') # For now, treat it as a no-op: return [self.state.mktrans_nop(stmt, 'Py_Finalize')] ######################################################################## # PyFloat_* ######################################################################## def impl_PyFloat_AsDouble(self, stmt, v_op): fnmeta = FnMeta(name='PyFloat_AsDouble', declared_in='floatobject.h', # FIXME: docurl prototype=('double ' 'PyFloat_AsDouble(PyObject *op)'), defined_in='Objects/floatobject.c') # gracefully handles NULL with PyErr_BadArgument: if isinstance(v_op, UnknownValue): self.state.raise_split_value(v_op, stmt.loc) if v_op.is_null_ptr(): s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.bad_argument(stmt.loc) return [Transition(self.state, s_failure, '%s() fails due to NULL argument' % fnmeta.name)] # if it's exactly a PyFloat_Type, we extract ob_fval # otherwise, we can call into nb_float # or raise TypeError returntype = stmt.fn.type.dereference.type if self.object_ptr_has_global_ob_type(v_op, 'PyFloat_Type'): # We know it's a PyFloatObject; the call will succeed: # FIXME: cast: v_ob_fval = self.state.read_field_by_name(stmt, returntype, v_op.region, 'ob_fval') t_success = self.state.mktrans_assignment(stmt.lhs, v_ob_fval, 'PyFloat_AsDouble() returns ob_fval') return [t_success] # We don't know if it's a PyFloatObject (or subclass); the call could # fail with TypeError or MemoryError: t_success = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), fnmeta.desc_when_call_succeeds()) t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, -1), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_success, t_failure] def impl_PyFloat_FromDouble(self, stmt, v_fval): fnmeta = FnMeta(name='PyFloat_FromDouble', declared_in='floatobject.h', # FIXME: docurl prototype=('PyObject *' ' PyFloat_FromDouble(double fval)'), defined_in='Objects/floatobject.c') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyFloatObject', 'PyFloat_Type') # Set ob_fval in the new object (when successful): t_success.dest.set_field_by_name(r_newobj, 'ob_fval', v_fval) return [t_success, t_failure] ######################################################################## # PyFrame_* ######################################################################## def impl_PyFrame_New(self, stmt, v_tstate, v_code, v_globals, v_locals): fnmeta = FnMeta(name='PyFrame_New', declared_in='frameobject.h', prototype=('PyFrameObject *\n' 'PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,\n' 'PyObject *locals'), defined_in='Objects/frameobject.c') # (used by Cython-generated code in static void __Pyx_AddTraceback in # each file) # For now, ignore the effects on the input variables: r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyFrameObject', 'PyFrame_Type') return [t_success, t_failure] ######################################################################## # Py_GetVersion ######################################################################## def impl_Py_GetVersion(self, stmt): fnmeta = FnMeta(name='Py_GetVersion', docurl='http://docs.python.org/c-api/init.html#Py_GetVersion', defined_in='Python/getversion.c') # Returns a non-NULL pointer returntype = stmt.fn.type.dereference.type r_result = Region('region-for-sys-version', None) self.state.region_for_var[r_result] = r_result v_nonnull = PointerToRegion(returntype, stmt.loc, r_result) return [self.state.mktrans_assignment(stmt.lhs, v_nonnull, fnmeta.name)] ######################################################################## # PyGILState_* ######################################################################## def impl_PyGILState_Ensure(self, stmt): fnmeta = FnMeta(name='PyGILState_Ensure', docurl='http://docs.python.org/c-api/init.html#PyGILState_Ensure') # Return some opaque handle: returntype = stmt.fn.type.dereference.type return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), 'PyGILState_Ensure')] def impl_PyGILState_Release(self, stmt, v_state): fnmeta = FnMeta(name='PyGILState_Release', docurl='http://docs.python.org/c-api/init.html#PyGILState_Release') # For now, treat it as a no-op: return [self.state.mktrans_nop(stmt, 'PyGILState_Release')] ######################################################################## # PyImport_* ######################################################################## def impl_PyImport_AddModule(self, stmt, v_name): fnmeta = FnMeta(name='PyImport_AddModule', docurl='http://docs.python.org/c-api/import.html#PyImport_AddModule') # used by cython-generated modules # returns a borrowed ref (or NULL+exc) return self.make_transitions_for_borrowed_ref_or_fail(stmt, fnmeta) def impl_PyImport_AppendInittab(self, stmt, v_name, v_initfunc): fnmeta = FnMeta(name='PyImport_AppendInittab', docurl='http://docs.python.org/c-api/import.html#PyImport_AppendInittab') s_success = self.state.mkstate_concrete_return_of(stmt, 0) s_failure = self.state.mkstate_concrete_return_of(stmt, -1) # (doesn't set an exception on failure, and Py_Initialize shouldn't # have been called yet, in any case) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyImport_ImportModule(self, stmt, v_name): fnmeta = FnMeta(name='PyImport_ImportModule', docurl='http://docs.python.org/c-api/import.html#PyImport_ImportModule') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyModuleObject', 'PyModule_Type') return [t_success, t_failure] ######################################################################## # Py_Initialize* ######################################################################## def impl_Py_Initialize(self, stmt): fnmeta = FnMeta(name='Py_Initialize', docurl='http://docs.python.org/c-api/init.html#Py_Initialize') # For now, treat it as a no-op: return [self.state.mktrans_nop(stmt, 'Py_Initialize')] ######################################################################## # Py_InitModule* ######################################################################## def impl_Py_InitModule4_64(self, stmt, v_name, v_methods, v_doc, v_self, v_apiver): fnmeta = FnMeta(name='Py_InitModule4_64', prototype=('PyAPI_FUNC(PyObject *) Py_InitModule4(const char *name, PyMethodDef *methods,\n' ' const char *doc, PyObject *self,\n' ' int apiver);'), notes=('Returns a borrowed reference')) # FIXME: # On 64-bit: # #define Py_InitModule4 Py_InitModule4_64 # with tracerefs: # #define Py_InitModule4 Py_InitModule4TraceRefs_64 # #define Py_InitModule4 Py_InitModule4TraceRefs s_success = self.mkstate_borrowed_ref(stmt, fnmeta) s_failure = self.mkstate_exception(stmt) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) ######################################################################## # Py_Int* ######################################################################## def impl_PyInt_AsLong(self, stmt, v_op): fnmeta = FnMeta(name='PyInt_AsLong', declared_in='intobject.h', prototype='PyAPI_FUNC(long) PyInt_AsLong(PyObject *);', defined_in='Objects/intobject.c', docurl='http://docs.python.org/c-api/int.html#PyInt_AsLong') # Can fail (gracefully) with NULL, and with non-int objects returntype = stmt.fn.type.dereference.type if self.object_ptr_has_global_ob_type(v_op, 'PyInt_Type'): # We know it's a PyIntObject; the call will succeed: # FIXME: cast: v_ob_ival = self.state.read_field_by_name(stmt, returntype, v_op.region, 'ob_ival') t_success = self.state.mktrans_assignment(stmt.lhs, v_ob_ival, 'PyInt_AsLong() returns ob_ival') return [t_success] # We don't know if it's a PyIntObject (or subclass); the call could # fail: t_success = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), fnmeta.desc_when_call_succeeds()) t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, -1), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_success, t_failure] def impl_PyInt_FromLong(self, stmt, v_ival): fnmeta = FnMeta(name='PyInt_FromLong', docurl='http://docs.python.org/c-api/int.html#PyInt_FromLong', declared_in='intobject.h', prototype='PyAPI_FUNC(PyObject *) PyInt_FromLong(long);', defined_in='Objects/intobject.c') # # CPython2 shares objects for integers in the range: # -5 <= ival < 257 # within intobject.c's "small_ints" array and these are preallocated # by _PyInt_Init(). Thus, for these values, we know that the call # cannot fail r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyIntObject', 'PyInt_Type') # Set ob_ival: t_success.dest.set_field_by_name(r_newobj, 'ob_ival', v_ival) if isinstance(v_ival, ConcreteValue): if v_ival.value >= -5 and v_ival.value < 257: # We know that failure isn't possible: return [t_success] return [t_success, t_failure] ######################################################################## # PyIter_* ######################################################################## def impl_PyIter_Next(self, stmt, v_iter): fnmeta = FnMeta(name='PyIter_Next', declared_in='abstract.h', prototype='PyObject * PyIter_Next(PyObject *iter);', defined_in='Objects/abstract.c', docurl='http://docs.python.org/c-api/iter.html#PyIter_Next') returntype = stmt.fn.type.dereference.type self.state.raise_any_null_ptr_func_arg(stmt, 0, v_iter, why='directly accesses iter->ob_type') # Three outcomes: # * returns a new ref (next item) # * returns NULL with no exception set (no more items) # * returns NULL with an exception (error occurred) # The "next value" case: s_nextvalue, nonnull = \ self.mkstate_new_ref(stmt, 'new ref returned by %s()' % fnmeta.name) t_nextvalue = Transition(self.state, s_nextvalue, 'when %s() retrieves a value (new ref)' % fnmeta.name) # The "end of iteration" case: t_end = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, 0), 'when %s() returns NULL without setting an exception (end of iteration)' % fnmeta.name) # The "error occurred" case: t_error = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, 0), 'when %s() returns NULL setting an exception (error occurred)' % fnmeta.name) t_error.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_nextvalue, t_end, t_error] ######################################################################## # PyList_* ######################################################################## def impl_PyList_Append(self, stmt, v_op, v_newitem): fnmeta = FnMeta(name='PyList_Append', declared_in='listobject.h', prototype='PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);', defined_in='Objects/listobject.c', docurl='http://docs.python.org/c-api/list.html#PyList_Append') # If it succeeds, it adds a reference on the item # self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyList_Check')) # It handles newitem being NULL: if v_newitem.is_null_ptr(): s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.bad_internal_call(stmt.loc) return [Transition(self.state, s_failure, 'returning -1 from %s() due to NULL item' % fnmeta.name)] # On success, adds a ref on input: s_success = self.state.mkstate_concrete_return_of(stmt, 0) s_success.cpython.add_ref(v_newitem, stmt.loc) #...and set the pointer value within ob_item array, so that we can # discount that refcount: ob_item_region = self.state.make_field_region(v_op.region, 'ob_item') ob_size_region = self.state.make_field_region(v_op.region, 'ob_size') # Locate the insertion index, based on ob_size: v_index = self.state.read_field_by_name(stmt, get_Py_ssize_t().type, v_op.region, 'ob_size') if isinstance(v_index, ConcreteValue): index_value = v_index.value elif isinstance(v_index, WithinRange): # We don't know the size of the list, just that it (presumably) # has some sane ob_size. # Use the smallest non-negative size (raising it as a 1-item # SplitValue so that the assumption is noted): index_value = max(0, v_index.minvalue) v_index.raise_as_concrete(stmt.loc, index_value, 'when treating ob_size as %i' % index_value) else: raise NotImplementedError() array_region = s_success._array_region(ob_item_region, index_value) s_success.value_for_region[array_region] = v_newitem # Can fail with memory error, overflow error: s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyList_GetItem(self, stmt, v_list, v_index): fnmeta = FnMeta(name='PyList_GetItem', docurl='http://docs.python.org/c-api/list.html#PyList_GetItem', prototype='PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index)', defined_in='Objects/listobject.c', notes='Returns a borrowed reference, or raises an IndexError') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_list, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyList_Check')) # FIXME: for now, simply return a borrowed ref, rather than # trying to track indices and the array: s_success = self.mkstate_borrowed_ref(stmt, fnmeta) return [Transition(self.state, s_success, None)] def impl_PyList_New(self, stmt, v_len): fnmeta = FnMeta(name='PyList_New', docurl='http://docs.python.org/c-api/list.html#PyList_New', prototype='PyObject* PyList_New(Py_ssize_t len)', notes='Returns a new reference, or raises MemoryError') check_isinstance(v_len, AbstractValue) r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyListObject', 'PyList_Type') # Set ob_size: t_success.dest.set_field_by_name(r_newobj, 'ob_size', v_len) # "Allocate" ob_item, and set it up so that all of the array is # treated as NULL: ob_item_region = t_success.dest.make_heap_region( 'ob_item array for PyListObject', stmt) t_success.dest.value_for_region[ob_item_region] = \ ConcreteValue(get_PyObjectPtr(), stmt.loc, 0) ob_item = t_success.dest.make_field_region(r_newobj, 'ob_item') t_success.dest.value_for_region[ob_item] = PointerToRegion(get_PyObjectPtr().pointer, stmt.loc, ob_item_region) return [t_success, t_failure] def impl_PyList_SetItem(self, stmt, v_list, v_index, v_item): fnmeta = FnMeta(name='PyList_SetItem', prototype='int PyList_SetItem(PyObject *list, Py_ssize_t index, PyObject *item)', docurl='http://docs.python.org/c-api/list.html#PyList_SetItem',) self.state.raise_any_null_ptr_func_arg(stmt, 0, v_list, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyList_Check')) # However, it appears to be robust in the face of NULL "item" pointers result = [] # Is it really a list? if 0: # FIXME: check not_a_list = self.state.mkstate_concrete_return_of(stmt, -1) result.append(Transition(self.state, not_a_list, fnmeta.desc_when_call_fails('not a list'))) # Index out of range? if 0: # FIXME: check out_of_range = self.state.mkstate_concrete_return_of(stmt, -1) result.append(Transition(self.state, out_of_range, fnmeta.desc_when_call_fails('index out of range)'))) if 1: s_success = self.state.mkstate_concrete_return_of(stmt, 0) # FIXME: update refcounts # "Steal" a reference to item: if isinstance(v_item, PointerToRegion): s_success.cpython.steal_reference(v_item, stmt.loc) # and discards a # reference to an item already in the list at the affected position. result.append(Transition(self.state, s_success, '%s() succeeds' % fnmeta.name)) return result def impl_PyList_Size(self, stmt, v_list): fnmeta = FnMeta(name='PyList_Size', docurl='http://docs.python.org/c-api/list.html#PyList_Size', prototype='Py_ssize_t PyList_Size(PyObject *list)', defined_in='Objects/listobject.c') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_list, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyList_Check')) returntype = stmt.fn.type.dereference.type v_ob_size = self.state.read_field_by_name(stmt, returntype, v_list.region, 'ob_size') t_return = self.state.mktrans_assignment(stmt.lhs, v_ob_size, fnmeta.desc_when_call_returns_value('ob_size')) return [t_return] ######################################################################## # PyLong_* ######################################################################## def impl_PyLong_FromLong(self, stmt, v_long): fnmeta = FnMeta(name='PyLong_FromLong', docurl='http://docs.python.org/c-api/long.html#PyLong_FromLong') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyLongObject', 'PyLong_Type') return [t_success, t_failure] def impl_PyLong_FromLongLong(self, stmt, v_v): fnmeta = FnMeta(name='PyLong_FromLongLong', docurl='http://docs.python.org/c-api/long.html#PyLong_FromLongLong', prototype='PyObject* PyLong_FromLongLong(PY_LONG_LONG v)') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyLongObject', 'PyLong_Type') return [t_success, t_failure] def impl_PyLong_FromString(self, stmt, v_str, v_pend, v_base): fnmeta = FnMeta(name='PyLong_FromString', declared_in='longobject.h', prototype='PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);', defined_in='Objects/longobject.c', docurl='http://docs.python.org/c-api/long.html#PyLong_FromString') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyLongObject', 'PyLong_Type') return [t_success, t_failure] def impl_PyLong_FromVoidPtr(self, stmt, v_p): fnmeta = FnMeta(name='PyLong_FromVoidPtr', docurl='http://docs.python.org/c-api/long.html#PyLong_FromVoidPtr') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyLongObject', 'PyLong_Type') return [t_success, t_failure] ######################################################################## # PyMapping_* ######################################################################## def impl_PyMapping_Size(self, stmt, v_o): fnmeta = FnMeta(name='PyMapping_Size', docurl='http://docs.python.org/c-api/mapping.html#PyMapping_Size', prototype='Py_ssize_t PyMapping_Size(PyObject *o)', defined_in='Objects/abstract.c', notes='Can cope with NULL (sets exception)') t_success = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(stmt.lhs.type, stmt.loc), fnmeta.desc_when_call_succeeds()) t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(stmt.lhs.type, stmt.loc, -1), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_TypeError', stmt.loc) return [t_success, t_failure] ######################################################################## # PyMem_* ######################################################################## def impl_PyMem_Free(self, stmt, v_ptr): fnmeta = FnMeta(name='PyMem_Free', docurl='http://docs.python.org/c-api/memory.html#PyMem_Free') # FIXME: it's unsafe to call repeatedly, or on the wrong memory region s_new = self.state.use_next_stmt_node() desc = None # It's safe to call on NULL if v_ptr.is_null_ptr(): desc = 'calling PyMem_Free on NULL' elif isinstance(v_ptr, PointerToRegion): # Mark the arg as being deallocated: region = v_ptr.region check_isinstance(region, Region) # Get the description of the region before trashing it: desc = 'calling PyMem_Free on %s' % region #t_temp = state.mktrans_assignment(stmt.lhs, # UnknownValue.make(None, stmt.loc), # 'calling tp_dealloc on %s' % region) # Mark the region as deallocated # Since regions are shared with other states, we have to set this up # for this state by assigning it with a special "DeallocatedMemory" # value # Clear the value for any fields within the region: for k, v in region.fields.items(): if v in s_new.value_for_region: del s_new.value_for_region[v] # Set the default value for the whole region to be "DeallocatedMemory" s_new.region_for_var[region] = region s_new.value_for_region[region] = DeallocatedMemory(None, stmt.loc) return [Transition(self.state, s_new, desc)] def impl_PyMem_Malloc(self, stmt, v_size): fnmeta = FnMeta(name='PyMem_Malloc', docurl='http://docs.python.org/c-api/memory.html#PyMem_Malloc') returntype = stmt.fn.type.dereference.type r_nonnull = self.state.make_heap_region('PyMem_Malloc', stmt) v_nonnull = PointerToRegion(returntype, stmt.loc, r_nonnull) # FIXME: it hasn't been initialized t_success = self.state.mktrans_assignment(stmt.lhs, v_nonnull, fnmeta.desc_when_call_succeeds()) t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, 0), fnmeta.desc_when_call_fails()) return [t_success, t_failure] ######################################################################## # PyModule_* ######################################################################## def impl_PyModule_AddIntConstant(self, stmt, v_module, v_name, v_value): fnmeta = FnMeta(name='PyModule_AddIntConstant', docurl='http://docs.python.org/c-api/module.html#PyModule_AddIntConstant') # (No externally-visible refcount changes) s_success = self.state.mkstate_concrete_return_of(stmt, 0) # Can fail with memory error, overflow error: s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyModule_AddObject(self, stmt, v_module, v_name, v_value): fnmeta = FnMeta(name='PyModule_AddObject', docurl='http://docs.python.org/c-api/module.html#PyModule_AddObject', defined_in='Python/modsupport.c', notes='Steals a reference to the object if if succeeds') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_module, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyModule_Check')) # Explicitly checks for non-NULL obj: if v_value.is_null_ptr(): s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_TypeError', stmt.loc) return [Transition(self.state, s_failure, 'returning -1 from %s()' % fnmeta.name)] # On success, steals a ref from v_value: s_success = self.state.mkstate_concrete_return_of(stmt, 0) s_success.cpython.steal_reference(v_value, stmt.loc) # Can fail with memory error, overflow error: s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyModule_AddStringConstant(self, stmt, v_module, v_name, v_value): fnmeta = FnMeta(name='PyModule_AddStringConstant', docurl='http://docs.python.org/c-api/module.html#PyModule_AddStringConstant',) # (No externally-visible refcount changes) s_success = self.state.mkstate_concrete_return_of(stmt, 0) # Can fail with memory error, overflow error: s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PyModule_GetDict(self, stmt, v_module): fnmeta = FnMeta(name='PyModule_GetDict', docurl='http://docs.python.org/c-api/module.html#PyModule_GetDict', prototype='PyObject* PyModule_GetDict(PyObject *module)', notes='Returns a borrowed reference. Always succeeds') s_success = self.mkstate_borrowed_ref(stmt, fnmeta) return [Transition(self.state, s_success, None)] ######################################################################## # PyNumber_* ######################################################################## def impl_PyNumber_Int(self, stmt, v_o): fnmeta = FnMeta(name='PyNumber_Int', docurl='http://docs.python.org/c-api/number.html#PyNumber_Int', prototype='PyObject * PyNumber_Int(PyObject *o)') t_err = self.handle_null_error(stmt, 0, v_o) if t_err: return [t_err] return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyNumber_Remainer(self, stmt, v_v, v_w): fnmeta = FnMeta(name='PyNumber_Remainder', docurl='http://docs.python.org/c-api/number.html#PyNumber_Remainder', prototype='PyObject * PyNumber_Remainder(PyObject *v, PyObject *w)') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_v, why='%s() reads though v->ob_type within binary_op1()' % fnmeta.name) self.state.raise_any_null_ptr_func_arg(stmt, 1, v_w, why='%s() reads though w->ob_type within binary_op1()' % fnmeta.name) return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) ######################################################################## # PyObject_* ######################################################################## def _handle_PyObject_CallMethod(self, fncall, fmtargidx, with_size_t): """ For functions in Objects/abstract.c that use Py_VaBuildValue or _Py_VaBuildValue_SizeT, then use call_function_tail (e.g. handles PyObject_CallFunction also) Also used by PyEval_CallMethod """ check_isinstance(fncall, FunctionCall) check_isinstance(with_size_t, bool) on_success, on_failure = fncall.new_ref_or_fail() # The function can succeed or fail # If any of the PyObject* inputs are NULL, it is doomed to failure def _handle_successful_parse(fmt): """ Returns a boolean: is success of the function possible? """ exptypes = fmt.iter_exp_types() for v_vararg, (unit, exptype) in zip(fncall.varargs, exptypes): if 0: print('v_vararg: %r' % v_vararg) print(' unit: %r' % unit) print(' exptype: %r %s' % (exptype, exptype)) if isinstance(unit, ObjectFormatUnit): # NULL inputs ptrs guarantee failure: if v_vararg.is_null_ptr(): # The call will fail: return False # non-NULL input ptrs receive "external" references on # success for codes "S" and "O", but code "N" steals a # reference for the args. The args are then decref-ed # by the call. Hence args with code "N" lose a ref: if isinstance(v_vararg, PointerToRegion): if isinstance(unit, CodeN): on_success.state.cpython.dec_ref(v_vararg, fncall.stmt.loc) on_failure.state.cpython.dec_ref(v_vararg, fncall.stmt.loc) return True fmt_string = fncall.args[fmtargidx].as_string_constant() if fmt_string: try: fmt = PyBuildValueFmt.from_string(fmt_string, with_size_t) if not _handle_successful_parse(fmt): on_success.is_possible = False except FormatStringWarning: pass return fncall.get_transitions() def impl_PyObject_AsFileDescriptor(self, stmt, v_o): fnmeta = FnMeta(name='PyObject_AsFileDescriptor', docurl='http://docs.python.org/c-api/object.html#PyObject_AsFileDescriptor', prototype='int PyObject_AsFileDescriptor(PyObject *o)', defined_in='Objects/fileobject.c') # Uses PyInt_Check(o) macro, which will segfault on NULL self.state.raise_any_null_ptr_func_arg(stmt, 0, v_o, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyInt_Check')) # For now, don't try to implement the internal logic: t_return = self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(stmt.lhs.type, stmt.loc), 'when %s() returns' % fnmeta.name) return [t_return] def impl_PyObject_Call(self, stmt, v_o, v_args, v_kw): fnmeta = FnMeta(name='PyObject_Call', docurl='http://docs.python.org/c-api/object.html#PyObject_Call', defined_in='Objects/abstract.c', prototype=('PyObject *\n' 'PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)')) # "func" and "args" must not be NULL, but "kw" can be: self.state.raise_any_null_ptr_func_arg(stmt, 0, v_o, why='looks up func->ob_type') self.state.raise_any_null_ptr_func_arg(stmt, 1, v_args) return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyObject_CallFunction(self, stmt, v_callable, v_format, *args): fnmeta = FnMeta(name='PyObject_CallFunction', docurl='http://docs.python.org/c-api/object.html#PyObject_CallFunction', defined_in='Objects/abstract.c', prototype='PyObject* PyObject_CallFunction(PyObject *callable, char *format, ...)') # callable can be NULL fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_callable, v_format), varargs=args) self._handle_PyObject_CallMethod(fncall, 1, with_size_t=False) return fncall.get_transitions() def impl__PyObject_CallFunction_SizeT(self, stmt, v_callable, v_format, *args): fnmeta = FnMeta(name='_PyObject_CallFunction_SizeT', docurl='http://docs.python.org/c-api/object.html#PyObject_CallFunction', defined_in='Objects/abstract.c', prototype='PyObject * _PyObject_CallFunction_SizeT(PyObject *callable, char *format, ...)') fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_callable, v_format), varargs=args) self._handle_PyObject_CallMethod(fncall, 1, with_size_t=True) return fncall.get_transitions() def _check_objargs(self, stmt, fnmeta, args, base_idx): """ Object/abstract.c: objargs_mktuple(va_list va) expects a NULL-terminated list of PyObject* """ check_isinstance(fnmeta, FnMeta) # must be PyObject* (or NULL): for i, v_arg in enumerate(args): if v_arg.is_null_ptr(): continue if not type_is_pyobjptr_subclass(v_arg.gcctype): loc = v_arg.loc if not loc: loc = stmt.loc gcc.warning(loc, ('argument %i had type %s but was expecting a PyObject* (or subclass)' % (i + base_idx + 1, v_arg.gcctype))) # check NULL-termination: if not args or not args[-1].is_null_ptr(): gcc.warning(stmt.loc, ('arguments to %s were not NULL-terminated' % fnmeta.name)) def impl_PyObject_CallFunctionObjArgs(self, stmt, v_callable, *args): fnmeta = FnMeta(name='PyObject_CallFunctionObjArgs', docurl='http://docs.python.org/c-api/object.html#PyObject_CallFunctionObjArgs', prototype='PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ...)', defined_in='Objects/abstract.c', notes='args must be NULL-terminated') # "callable" can be NULL # Check args: self._check_objargs(stmt, fnmeta, args, 1) return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyObject_CallMethod(self, stmt, v_o, v_name, v_format, *args): fnmeta = FnMeta(name='PyObject_CallMethod', docurl='http://docs.python.org/c-api/object.html#PyObject_CallMethod', defined_in='Objects/abstract.c', prototype=('PyObject *\n' 'PyObject_CallMethod(PyObject *o, char *name, char *format, ...)')) fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_o, v_name, v_format), varargs=args) self._handle_PyObject_CallMethod(fncall, 2, with_size_t=False) return fncall.get_transitions() def impl__PyObject_CallMethod_SizeT(self, stmt, v_o, v_name, v_format, *args): fnmeta = FnMeta(name='_PyObject_CallMethod_SizeT', # abstract.h has: # #ifdef PY_SSIZE_T_CLEAN # #define PyObject_CallMethod _PyObject_CallMethod_SizeT # #endif # defined_in='Objects/abstract.c') # PyObject * # _PyObject_CallMethod_SizeT(PyObject *o, char *name, char *format, ...) fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_o, v_name, v_format), varargs=args) self._handle_PyObject_CallMethod(fncall, 2, with_size_t=True) return fncall.get_transitions() def impl_PyObject_CallMethodObjArgs(self, stmt, v_o, v_name, *args): fnmeta = FnMeta(name='PyObject_CallMethodObjArgs', docurl='http://docs.python.org/c-api/object.html#PyObject_CallMethodObjArgs', defined_in='Objects/abstract.c', prototype='PyObject* PyObject_CallMethodObjArgs(PyObject *o, PyObject *name, ..., NULL)') # "callable" and "name" can be NULL # Check args: self._check_objargs(stmt, fnmeta, args, 2) return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyObject_CallObject(self, stmt, v_o, v_args): fnmeta = FnMeta(name='PyObject_CallObject', docurl='http://docs.python.org/c-api/object.html#PyObject_CallObject', defined_in='Objects/abstract.c', prototype=('PyAPI_FUNC(PyObject *) PyObject_CallObject(PyObject *callable_object,\n' ' PyObject *args);')) # internally, is just: # return PyEval_CallObjectWithKeywords(o, a, NULL); # args can be NULL, but the callable obj can't be: self.state.raise_any_null_ptr_func_arg(stmt, 0, v_o, why=('%s() looks up func->ob_type (within PyObject_Call' ' within PyEval_CallObjectWithKeywords)' % fnmeta.name)) return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyObject_GetAttr(self, stmt, v_v, v_name): fnmeta = FnMeta(name='PyObject_GetAttr', docurl='http://docs.python.org/c-api/object.html#PyObject_GetAttr', defined_in='Objects/object.c', prototype='PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name)') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_v, why=invokes_Py_TYPE(fnmeta)) self.state.raise_any_null_ptr_func_arg(stmt, 1, v_name, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_Check')) return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyObject_GetAttrString(self, stmt, v_v, v_name): fnmeta = FnMeta(name='PyObject_GetAttrString', docurl='http://docs.python.org/c-api/object.html#PyObject_GetAttrString', defined_in='Objects/object.c', prototype='PyObject* PyObject_GetAttrString(PyObject *v, const char *name)') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_v, why=invokes_Py_TYPE(fnmeta)) self.state.raise_any_null_ptr_func_arg(stmt, 1, v_name, why=('%s() can call PyString_InternFromString(), ' 'which calls PyString_FromString(), ' 'which requires a non-NULL pointer' % fnmeta.name)) return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyObject_GetItem(self, stmt, v_o, v_key): fnmeta = FnMeta(name='PyObject_GetItem', docurl='http://docs.python.org/c-api/object.html#PyObject_GetItem', defined_in='Objects/abstract.c', prototype='PyObject* PyObject_GetItem(PyObject *o, PyObject *key)') # safely handles NULL for either argument via null_error(): t_err = self.handle_null_error(stmt, 0, v_o) if t_err: return [t_err] t_err = self.handle_null_error(stmt, 1, v_key) if t_err: return [t_err] return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta) def impl_PyObject_GenericGetAttr(self, stmt, v_o, v_name): fnmeta = FnMeta(name='PyObject_GenericGetAttr', docurl='http://docs.python.org/c-api/object.html#PyObject_GenericGetAttr', prototype='PyObject* PyObject_GenericGetAttr(PyObject *o, PyObject *name)', defined_in='Objects/object.c') fncall = FunctionCall(self.state, stmt, fnmeta, (v_o, v_name)) fncall.crashes_on_null_arg(0, why=invokes_Py_TYPE(fnmeta)) fncall.crashes_on_null_arg(1, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_Check')) # The "success" case: on_success = fncall.can_succeed_new_ref() # The "failure" case: on_failure = fncall.can_fail() on_failure.returns_NULL() on_failure.sets_exception('PyExc_MemoryError') return fncall.get_transitions() def impl_PyObject_GenericSetAttr(self, stmt, v_o, v_name, v_value): fnmeta = FnMeta(name='PyObject_GenericSetAttr', docurl='http://docs.python.org/c-api/object.html#PyObject_GenericSetAttr', prototype='PyObject_GenericSetAttr(PyObject *o, PyObject *name, PyObject *value)', defined_in='Objects/object.c') fncall = FunctionCall(self.state, stmt, fnmeta, (v_o, v_name, v_value)) fncall.crashes_on_null_arg(0, why=invokes_Py_TYPE(fnmeta)) fncall.crashes_on_null_arg(1, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_Check')) # (it appears that value can legitimately be NULL) on_success = fncall.can_succeed() on_success.returns(0) on_failure = fncall.can_fail() on_failure.returns(-1) on_failure.sets_exception('PyExc_AttributeError') return fncall.get_transitions() def impl_PyObject_HasAttrString(self, stmt, v_o, v_attr_name): fnmeta = FnMeta(name='PyObject_HasAttrString', docurl='http://docs.python.org/c-api/object.html#PyObject_HasAttrString') # the object must be non-NULL: it is unconditionally # dereferenced to get the ob_type: self.state.raise_any_null_ptr_func_arg(stmt, 0, v_o) # attr_name must be non-NULL, this fn calls: # PyObject_GetAttrString(PyObject *v, const char *name) # which can call: # PyString_InternFromString(const char *cp) # PyString_FromString(str) <-- must be non-NULL self.state.raise_any_null_ptr_func_arg(stmt, 1, v_attr_name) fncall = FunctionCall(self.state, stmt, fnmeta) on_true = fncall.add_outcome(fnmeta.desc_when_call_returns_value('1 (true)')) on_true.returns(1) on_false = fncall.add_outcome(fnmeta.desc_when_call_returns_value('0 (false)')) on_false.returns(0) return fncall.get_transitions() def impl_PyObject_IsTrue(self, stmt, v_o): fnmeta = FnMeta(name='PyObject_IsTrue', docurl='http://docs.python.org/c-api/object.html#PyObject_IsTrue') fncall = FunctionCall(self.state, stmt, fnmeta) on_true = fncall.add_outcome(fnmeta.desc_when_call_returns_value('1 (true)')) on_true.returns(1) on_false = fncall.add_outcome(fnmeta.desc_when_call_returns_value('0 (false)')) on_false.returns(0) on_failure = fncall.add_outcome(fnmeta.desc_when_call_returns_value('-1 (failure)')) on_failure.returns(-1) on_failure.sets_exception('PyExc_MemoryError') # arbitrarily chosen error return fncall.get_transitions() def impl__PyObject_New(self, stmt, v_typeptr): fnmeta = FnMeta(name='_PyObject_New', # Declaration in objimpl.h', prototype='PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *);') # # For use via this macro: # #define PyObject_New(type, typeobj) \ # ( (type *) _PyObject_New(typeobj) ) # # Definition is in Objects/object.c # # Return value: New reference. check_isinstance(stmt, gcc.GimpleCall) check_isinstance(stmt.fn.operand, gcc.FunctionDecl) # Success case: allocation and assignment: s_success, nonnull = self.mkstate_new_ref(stmt, '_PyObject_New') # ...and set up ob_type on the result object: ob_type = s_success.make_field_region(nonnull, 'ob_type') s_success.value_for_region[ob_type] = v_typeptr t_success = Transition(self.state, s_success, fnmeta.desc_when_call_succeeds()) # Failure case: t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(stmt.lhs.type, stmt.loc, 0), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_success, t_failure] def impl_PyObject_Repr(self, stmt, v_o): fnmeta = FnMeta(name='PyObject_Repr', declared_in='object.h', prototype='PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);', docurl='http://docs.python.org/c-api/object.html#PyObject_Repr') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyStringObject', 'PyString_Type') return [t_success, t_failure] def impl_PyObject_SetAttr(self, stmt, v_o, v_attr_name, v_v): fnmeta = FnMeta(name='PyObject_SetAttr', docurl='http://docs.python.org/c-api/object.html#PyObject_SetAttr', defined_in='Objects/object.c', prototype='int PyObject_SetAttr(PyObject *o, PyObject *attr_name, PyObject *v)') fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_o, v_attr_name, v_v)) fncall.crashes_on_null_arg(0, why=invokes_Py_TYPE(fnmeta)) fncall.crashes_on_null_arg(1, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_Check')) # v_v can be NULL: clears the attribute on_success = fncall.can_succeed() on_success.returns(0) on_failure = fncall.can_fail() on_failure.returns(-1) on_failure.sets_exception('PyExc_TypeError') # e.g. return fncall.get_transitions() def impl_PyObject_SetAttrString(self, stmt, v_o, v_attr_name, v_v): fnmeta = FnMeta(name='PyObject_SetAttrString', docurl='http://docs.python.org/c-api/object.html#PyObject_SetAttrString', defined_in='Objects/object.c', prototype='int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v)') fncall = FunctionCall(self.state, stmt, fnmeta, args=(v_o, v_attr_name, v_v)) fncall.crashes_on_null_arg(0, why=invokes_Py_TYPE(fnmeta)) fncall.crashes_on_null_arg(1, why=('%s() can call PyString_InternFromString(), ' 'which calls PyString_FromString(), ' 'which requires a non-NULL pointer' % fnmeta.name)) # v_v can be NULL: clears the attribute on_success = fncall.can_succeed() on_success.returns(0) on_failure = fncall.can_fail() on_failure.returns(-1) on_failure.sets_exception('PyExc_TypeError') # e.g. return fncall.get_transitions() def impl_PyObject_Str(self, stmt, v_o): fnmeta = FnMeta(name='PyObject_Str', docurl='http://docs.python.org/c-api/object.html#PyObject_Str', declared_in='object.h') # PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *); # also with: # #define PyObject_Bytes PyObject_Str r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyStringObject', 'PyString_Type') return [t_success, t_failure] ######################################################################## # PyOS_* ######################################################################## def impl_PyOS_snprintf(self, stmt, v_str, v_size, v_format, *v_args): fnmeta = FnMeta(name='PyOS_snprintf', docurl='http://docs.python.org/c-api/conversion.html#PyOS_snprintf', prototype='int PyOS_snprintf(char *str, size_t size, const char *format, ...)') returntype = stmt.fn.type.dereference.type return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), None)] ######################################################################## # PyRun_* ######################################################################## def impl_PyRun_SimpleFileExFlags(self, stmt, v_fp, v_filename, v_closeit, v_flags): fnmeta = FnMeta(name='PyRun_SimpleFileExFlags', docurl='http://docs.python.org/c-api/veryhigh.html#PyRun_SimpleFileExFlags') fncall = FunctionCall(self.state, stmt, fnmeta) on_success = fncall.can_succeed() on_success.returns(0) on_failure = fncall.can_fail() on_failure.returns(-1) # (no way to get the exception on failure) # (FIXME: handle the potential autoclosing of the FILE*) return fncall.get_transitions() def impl_PyRun_SimpleStringFlags(self, stmt, v_command, v_flags): fnmeta = FnMeta(name='PyRun_SimpleStringFlags', docurl='http://docs.python.org/c-api/veryhigh.html#PyRun_SimpleStringFlags') fncall = FunctionCall(self.state, stmt, fnmeta) on_success = fncall.can_succeed() on_success.returns(0) on_failure = fncall.can_fail() on_failure.returns(-1) # (no way to get the exception on failure) return fncall.get_transitions() ######################################################################## # PySequence_* ######################################################################## def impl_PySequence_Concat(self, stmt, v_o1, v_o2): fnmeta = FnMeta(name='PySequence_Concat', docurl='http://docs.python.org/c-api/sequence.html#PySequence_Concat', declared_in='abstract.h', prototype='PyAPI_FUNC(PyObject *) PySequence_Concat(PyObject *o1, PyObject *o2);', defined_in='Objects/abstract.c') # safely handles NULL for either argument via null_error(): t_err = self.handle_null_error(stmt, 0, v_o1) if t_err: return [t_err] t_err = self.handle_null_error(stmt, 1, v_o2) if t_err: return [t_err] return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta, 'new ref from %s' % fnmeta.name) def impl_PySequence_DelItem(self, stmt, v_o, v_i): fnmeta = FnMeta(name='PySequence_DelItem', docurl='http://docs.python.org/c-api/sequence.html#PySequence_DelItem', declared_in='abstract.h', prototype='int PySequence_DelItem(PyObject *o, Py_ssize_t i);', defined_in='Objects/abstract.c') # safely handles NULL via null_error(): t_err = self.handle_null_error(stmt, 0, v_o, rawreturnvalue=-1) if t_err: return [t_err] # can fail with -1, setting an exception s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_TypeError', stmt.loc) # otherwise, expect zero s_success = self.state.mkstate_concrete_return_of(stmt, 0) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) def impl_PySequence_GetItem(self, stmt, v_o, v_i): fnmeta = FnMeta(name='PySequence_GetItem', docurl='http://docs.python.org/c-api/sequence.html#PySequence_GetItem', declared_in='abstract.h', prototype='PyAPI_FUNC(PyObject *) PySequence_GetItem(PyObject *o, Py_ssize_t i);', defined_in='Objects/abstract.c') # PyObject * # PySequence_GetItem(PyObject *s, Py_ssize_t i) # { # [... setup and error handling ...] # return m->sq_item(s, i); # } # # When it succeeds, it returns a new reference; see e.g. # Objects/listobject.c: list_item (the sq_item callback for # PyList_Type): it Py_INCREFs the returned item. return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta, 'new ref from %s' % fnmeta.name) def impl_PySequence_GetSlice(self, stmt, v_o, v_i1, v_i2): fnmeta = FnMeta(name='PySequence_GetSlice', docurl='http://docs.python.org/c-api/sequence.html#PySequence_GetSlice', declared_in='abstract.h', prototype='PyAPI_FUNC(PyObject *) PySequence_GetSlice(PyObject *o, Py_ssize_t i1, Py_ssize_t i2);', defined_in='Objects/abstract.c') # safely handles NULL for the obj via null_error(): t_err = self.handle_null_error(stmt, 0, v_o) if t_err: return [t_err] return self.make_transitions_for_new_ref_or_fail(stmt, fnmeta, 'new ref from %s' % fnmeta.name) def impl_PySequence_Length(self, stmt, v_o): return self.impl_PySequence_Size(stmt, v_o) def impl_PySequence_SetItem(self, stmt, v_o, v_i, v_item): fnmeta = FnMeta(name='PySequence_SetItem', prototype='int PySequence_SetItem(PyObject *o, Py_ssize_t i, PyObject *item)', docurl='http://docs.python.org/c-api/sequence.html#PySequence_SetItem', defined_in='Objects/abstract.c') # safely handles NULL for the obj via null_error(): t_err = self.handle_null_error(stmt, 0, v_o, rawreturnvalue=-1) if t_err: return [t_err] result = [] s_success = self.state.mkstate_concrete_return_of(stmt, 0) # The function *doesn't* steal a reference to item # For now, this only covers the "success" case. The call can fail # returning -1: # * raising TypeError if passed a non-sequence, or if the type's # PySequenceMethods table lacks a sq_ass_item callback # * if the call to sq_length fails, propagating some exception # * if the call to sq_ass_item fails, propagating some exception # but we don't track these possibilities yet. result.append(Transition(self.state, s_success, '%s() succeeds' % fnmeta.name)) return result def impl_PySequence_Size(self, stmt, v_o): fnmeta = FnMeta(name='PySequence_Size', docurl='http://docs.python.org/c-api/sequence.html#PySequence_Size', prototype='Py_ssize_t PySequence_Size(PyObject *s)', defined_in='Objects/abstract.c') # safely handles NULL for the obj via null_error(): t_err = self.handle_null_error(stmt, 0, v_o, rawreturnvalue=-1) if t_err: return [t_err] # on success, expect a value >= 0 returntype = stmt.fn.type.dereference.type s_success = self.state.mkstate_return_of(stmt, WithinRange.ge_zero(returntype, stmt.loc)) # else, return -1 and set an exception s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_TypeError', stmt.loc) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) ######################################################################## # PyString_* ######################################################################## def impl_PyString_AsString(self, stmt, v_op): fnmeta = FnMeta(name='PyString_AsString', declared_in='stringobject.h', prototype='PyAPI_FUNC(char *) PyString_AsString(PyObject *);', defined_in='Objects/stringobject.c', docurl='http://docs.python.org/c-api/string.html#PyString_AsString') # # With PyStringObject and their subclasses, it returns # ((PyStringObject *)op) -> ob_sval # With other classes, this call can fail # It will segfault if called with NULL, since it uses PyString_Check, # which reads through the object's ob_type: self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_Check')) returntype = stmt.fn.type.dereference.type if self.object_ptr_has_global_ob_type(v_op, 'PyString_Type'): # We know it's a PyStringObject; the call will succeed: # FIXME: cast: r_ob_sval = self.state.make_field_region(v_op.region, 'ob_sval') v_result = PointerToRegion(returntype, stmt.loc, r_ob_sval) t_success = self.state.mktrans_assignment(stmt.lhs, v_result, 'PyString_AsString() returns ob_sval') return [t_success] # We don't know if it's a PyStringObject (or subclass); the call could # fail: r_nonnull = self.state.make_heap_region('buffer from PyString_AsString()', stmt) v_success = PointerToRegion(returntype, stmt.loc, r_nonnull) t_success = self.state.mktrans_assignment(stmt.lhs, v_success, fnmeta.desc_when_call_succeeds()) t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, 0), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_success, t_failure] def impl_PyString_Concat(self, stmt, v_pv, v_w): fnmeta = FnMeta(name='PyString_Concat', prototype='void PyString_Concat(PyObject **string, PyObject *newpart)', docurl='http://docs.python.org/c-api/string.html#PyString_Concat', defined_in='Objects/stringobject.c', notes='') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_pv, why='%s unconditionally dereferences its first argument' % fnmeta.name) # However, it can survive *pv being NULL; does nothing v_star_pv = self.state.dereference(stmt.fn.operand, v_pv, stmt.loc) if v_star_pv.is_null_ptr(): s_nop = self.state.mkstate_nop(stmt) return [Transition(self.state, s_nop, fnmeta.desc_special('does nothing due to NULL *lhs'))] # It can survive w being NULL: cleans up *pv if v_w.is_null_ptr(): # Py_DECREF(*pv) s_nop = self.state.mkstate_nop(stmt) result = s_nop.cpython.mktransitions_Py_DECREF(v_star_pv, stmt) # *pv = NULL, and set desc: for t_new in result: t_new.dest.value_for_region[v_pv.region] = \ make_null_ptr(get_PyObjectPtr(), stmt.loc) t_new.desc = fnmeta.desc_special( 'cleans up due to NULL right-hand side (%s on *LHS)' % t_new.desc) return result # Try to allocate new string, which can fail: s_success = self.state.mkstate_nop(stmt) typeobjregion = self.typeobjregion_by_name('PyString_Type') r_nonnull = s_success.cpython.make_sane_object( stmt, 'result of %s' % fnmeta.name, RefcountValue.new_ref(stmt.loc, None)) s_failure = self.mkstate_exception(stmt) # Handle Py_DECREF(*pv): t_successes = s_success.cpython.mktransitions_Py_DECREF(v_star_pv, stmt) t_failures = s_failure.cpython.mktransitions_Py_DECREF(v_star_pv, stmt) # Handle *pv = v: for t_success in t_successes: t_success.dest.value_for_region[v_pv.region] = \ PointerToRegion(get_PyObjectPtr(), stmt.loc, r_nonnull) t_success.desc = fnmeta.desc_when_call_succeeds() + ' (%s on *LHS)' % t_success.desc for t_failure in t_failures: t_failure.dest.value_for_region[v_pv.region] = \ ConcreteValue(get_PyObjectPtr(), stmt.loc, 0) t_failure.desc = fnmeta.desc_when_call_fails() + ' (%s on *LHS)' % t_failure.desc return t_successes + t_failures def impl_PyString_ConcatAndDel(self, stmt, v_pv, v_w): fnmeta = FnMeta(name='PyString_ConcatAndDel', prototype='void PyString_ConcatAndDel(PyObject **string, PyObject *newpart)', docurl='http://docs.python.org/c-api/string.html#PyString_ConcatAndDel', defined_in='Objects/stringobject.c', notes='Decrements the reference count of newpart') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_pv, why='dereferences it unconditionally within PyString_Concat') # However, it can survive *pv being NULL; does nothing # It can survive w being NULL: cleans up *pv if isinstance(v_w, UnknownValue): self.state.raise_split_value(v_w, stmt.loc) # Mostly implemented in terms of PyString_Concat: results = self.impl_PyString_Concat(stmt, v_pv, v_w) # decrefs the new *pv, if non-NULL if not v_w.is_null_ptr(): new_results = [] for t_concat in results: for t_withdecref in t_concat.dest.cpython.mktransitions_Py_DECREF(v_w, stmt): t_withdecref.desc = t_concat.desc + ' (%s on RHS)' % t_withdecref.desc new_results.append(t_withdecref) return new_results return results def impl_PyString_FromFormat(self, stmt, v_fmt, *v_args): fnmeta = FnMeta(name='PyString_FromFormat', declared_in='stringobject.h', prototype=('PyAPI_FUNC(PyObject *) PyString_FromFormat(const char*, ...)\n' ' Py_GCC_ATTRIBUTE((format(printf, 1, 2)));'), notes='Returns a new reference', docurl='http://docs.python.org/c-api/string.html#PyString_FromFormat') # (We do not yet check that the format string matches the types of the # varargs) r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyStringObject', 'PyString_Type') return [t_success, t_failure] def impl_PyString_FromString(self, stmt, v_str): fnmeta = FnMeta(name='PyString_FromString', declared_in='stringobject.h', prototype='PyAPI_FUNC(PyObject *) PyString_FromString(const char *);', docurl='http://docs.python.org/c-api/string.html#PyString_FromString') # The input _must_ be non-NULL; it is not checked: self.state.raise_any_null_ptr_func_arg(stmt, 0, v_str) r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyStringObject', 'PyString_Type') return [t_success, t_failure] def impl_PyString_FromStringAndSize(self, stmt, v_str, v_size): fnmeta = FnMeta(name='PyString_FromStringAndSize', declared_in='stringobject.h', prototype='PyAPI_FUNC(PyObject *) PyString_FromStringAndSize(const char *, Py_ssize_t);', docurl='http://docs.python.org/c-api/string.html#PyString_FromStringAndSize', defined_in='Objects/stringobject.c') # # PyObject * # PyString_FromStringAndSize(const char *str, Py_ssize_t size) # v_str, v_size = self.state.eval_stmt_args(stmt) # (the input can legitimately be NULL) r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyStringObject', 'PyString_Type') return [t_success, t_failure] def impl_PyString_InternFromString(self, stmt, v_v): fnmeta = FnMeta(name='PyString_InternFromString', declared_in='stringobject.h', prototype='PyObject* PyString_InternFromString(const char *v)', defined_in='Objects/stringobject.c', docurl='http://docs.python.org/c-api/string.html#PyString_InternFromString') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_v, why=('%s() calls PyString_FromString(), ' 'which requires a non-NULL pointer' % fnmeta.name)) r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyStringObject', 'PyString_Type') return [t_success, t_failure] def impl_PyString_Size(self, stmt, v_string): fnmeta = FnMeta(name='PyString_Size', docurl='http://docs.python.org/c-api/string.html#PyString_Size', prototype='Py_ssize_t PyString_Size(PyObject *string)', defined_in='Objects/stringobject.c') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_string, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyString_Check')) # for strings, returns ob_size if self.object_ptr_has_global_ob_type(v_string, 'PyString_Type'): # We know it's a PyStringObject; the call will succeed: returntype = stmt.fn.type.dereference.type v_ob_size = self.state.read_field_by_name(stmt, returntype, v_op.region, 'ob_size') t_success = self.state.mktrans_assignment(stmt.lhs, v_ob_size, fnmeta.desc_when_call_returns_value('ob_size')) return [t_success] # for non-strings, can fail with -1, setting an exception s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc) # otherwise, expect a non-negative value: returntype = stmt.fn.type.dereference.type s_success = self.state.mkstate_return_of(stmt, WithinRange.ge_zero(returntype, stmt.loc)) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) ######################################################################## # PyStructSequence_* ######################################################################## def impl_PyStructSequence_InitType(self, stmt, v_type, v_desc): fnmeta = FnMeta(name='PyStructSequence_InitType', prototype='void PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)', defined_in='Objects/structseq.c') # For now, treat it as a no-op: return [self.state.mktrans_nop(stmt, 'PyStructSequence_InitType')] def impl_PyStructSequence_New(self, stmt, v_typeptr): fnmeta = FnMeta(name='PyStructSequence_New', declared_in='structseq.h', prototype='PyAPI_FUNC(PyObject *) PyStructSequence_New(PyTypeObject* type);', defined_in='Objects/structseq.c') # From our perspective, this is very similar to _PyObject_New check_isinstance(stmt, gcc.GimpleCall) check_isinstance(stmt.fn.operand, gcc.FunctionDecl) # Success case: allocation and assignment: s_success, nonnull = self.mkstate_new_ref(stmt, 'PyStructSequence_New') # ...and set up ob_type on the result object: ob_type = s_success.make_field_region(nonnull, 'ob_type') s_success.value_for_region[ob_type] = v_typeptr t_success = Transition(self.state, s_success, fnmeta.desc_when_call_succeeds()) # Failure case: t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(stmt.lhs.type, stmt.loc, 0), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_success, t_failure] ######################################################################## # PySys_* ######################################################################## def impl_PySys_GetObject(self, stmt, v_name): fnmeta = FnMeta(name='PySys_GetObject', declared_in='sysmodule.h', defined_in='Python/sysmodule.c', prototype='PyAPI_FUNC(PyObject *) PySys_GetObject(char *);', docurl='http://docs.python.org/c-api/sys.html#PySys_GetObject') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_name, why='%s() invokes PyString_FromString()' % fnmeta.name) s_success = self.mkstate_borrowed_ref(stmt, fnmeta) t_notfound = self.state.mktrans_assignment(stmt.lhs, make_null_pyobject_ptr(stmt), '%s does not find string' % fnmeta.name) return [self.state.mktrans_from_fncall_state(stmt, s_success, 'succeeds', True), t_notfound] def impl_PySys_SetObject(self, stmt, v_name, v_value): fnmeta = FnMeta(name='PySys_SetObject', declared_in='sysmodule.h', defined_in='Python/sysmodule.c', prototype='int PySys_SetObject(char *name, PyObject *v)', docurl='http://docs.python.org/c-api/sys.html#PySys_SetObject') # # can be called with NULL or non-NULL, calls PyDict_SetItemString # on non-NULL, which adds a ref on it returntype = stmt.fn.type.dereference.type t_success = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, 0), fnmeta.desc_when_call_succeeds()) if isinstance(v_value, PointerToRegion): t_success.dest.cpython.add_external_ref(v_value, stmt.loc) t_failure = self.state.mktrans_assignment(stmt.lhs, ConcreteValue(returntype, stmt.loc, -1), fnmeta.desc_when_call_fails()) t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc) return [t_success, t_failure] ######################################################################## # PyTraceback_* ######################################################################## def impl_PyTraceBack_Here(self, stmt, v_frame): fnmeta = FnMeta(name='PyTraceBack_Here', prototype='int PyTraceBack_Here(PyFrameObject *frame)', declared_in='traceback.h', defined_in='Python/traceback.c') # (used in cython-generated code __Pyx_AddTraceback) s_success = self.state.mkstate_concrete_return_of(stmt, 0) s_failure = self.state.mkstate_concrete_return_of(stmt, -1) return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) ######################################################################## # PyTuple_* ######################################################################## def impl_PyTuple_GetItem(self, stmt, v_op, v_i): fnmeta = FnMeta(name='PyTuple_GetItem', docurl='http://docs.python.org/c-api/tuple.html#PyTuple_GetItem', defined_in='Objects/tupleobject.c') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyTuple_Check')) # FIXME: for now, simply return a borrowed ref, rather than # trying to track indices and the array: s_success = self.mkstate_borrowed_ref(stmt, fnmeta) return [Transition(self.state, s_success, None)] def impl_PyTuple_New(self, stmt, v_len): fnmeta = FnMeta(name='PyTuple_New', docurl='http://docs.python.org/c-api/tuple.html#PyTuple_New') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyTupleObject', 'PyTuple_Type') # Set ob_size: t_success.dest.set_field_by_name(r_newobj, 'ob_size', v_len) return [t_success, t_failure] def impl_PyTuple_Pack(self, stmt, v_n, *v_args): fnmeta = FnMeta(name='PyTuple_Pack', docurl='http://docs.python.org/c-api/tuple.html#PyTuple_Pack', defined_in='Objects/tupleobject.c') if isinstance(v_n, ConcreteValue): if v_n.value != len(v_args): class WrongArgCount(PredictedError): def __str__(self): return 'mismatching argument count in call to %s' % fnmeta.name raise WrongArgCount() # All PyObject* args must be non-NULL: for i, v_arg in enumerate(v_args): self.state.raise_any_null_ptr_func_arg(stmt, i+1, v_arg, why=invokes_Py_INCREF(fnmeta)) r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyTupleObject', 'PyTuple_Type') # Set ob_size: t_success.dest.set_field_by_name(r_newobj, 'ob_size', v_n) #FIXME: adds a ref on each item; sets ob_item return [t_success, t_failure] def impl_PyTuple_SetItem(self, stmt, v_op, v_i, v_newitem): fnmeta = FnMeta(name='PyTuple_SetItem', docurl='http://docs.python.org/c-api/tuple.html#PyTuple_SetItem', defined_in='Objects/tupleobject.c') returntype = stmt.fn.type.dereference.type # The CPython implementation uses PyTuple_Check, which uses # Py_TYPE(op), an unchecked read through the ptr: self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyTuple_Check')) # i is range checked # newitem can safely be NULL result = [] # Check that it's a tuple: if not self.object_ptr_has_global_ob_type(v_op, 'PyTuple_Type'): # FIXME: Py_XDECREF on newitem s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.bad_internal_call(stmt.loc) result.append(Transition(self.state, s_failure, fnmeta.desc_when_call_fails('not a tuple'))) # Check that refcount is 1 (mutation during initial creation): v_ob_refcnt = self.state.get_value_of_field_by_region(v_op.region, 'ob_refcnt') # Because of the way we store RefcountValue instances, we can't # easily prove that the refcount == 1, so only follow this path # if we can prove that refcount != 1 eq_one = v_ob_refcnt.eval_comparison('eq', ConcreteValue.from_int(1), None) if eq_one is False: # tri-state # FIXME: Py_XDECREF on newitem s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.bad_internal_call(stmt.loc) result.append(Transition(self.state, s_failure, fnmeta.desc_when_call_fails('refcount is not 1'))) # It's known that no further outcomes are possible: return result # Range check: v_ob_size = self.state.read_field_by_name(stmt, None, v_op.region, 'ob_size') lt_zero = v_i.eval_comparison('lt', ConcreteValue.from_int(0), None) lt_size = v_i.eval_comparison('lt', v_ob_size, None) # The above could be None: signifying that we don't know, and that # False is possible. Out-of-range is possible if either aren't known # to be non-False: if lt_zero is not False or lt_size is not True: s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_IndexError', stmt.loc) result.append(Transition(self.state, s_failure, fnmeta.desc_when_call_fails('index out of range'))) # Within range is only possible if both boundaries are known to not # definitely be wrong: if lt_zero is not True and lt_size is not False: s_success = self.state.mkstate_concrete_return_of(stmt, 0) r_ob_item = s_success.make_field_region(v_op.region, 'ob_item') r_indexed = s_success._array_region(r_ob_item, v_i) # v_olditem = s_success.value_for_region[r_indexed] # FIXME: it does an XDECREF on the olditem s_success.value_for_region[r_indexed] = v_newitem result.append(Transition(self.state, s_success, fnmeta.desc_when_call_succeeds())) return result def impl_PyTuple_Size(self, stmt, v_op): fnmeta = FnMeta(name='PyTuple_Size', docurl='http://docs.python.org/c-api/tuple.html#PyTuple_Size', defined_in='Objects/tupleobject.c') returntype = stmt.fn.type.dereference.type # The CPython implementation uses PyTuple_Check, which uses # Py_TYPE(op), an unchecked read through the ptr: self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyTuple_Check')) # FIXME: cast: v_ob_size = self.state.read_field_by_name(stmt, returntype, v_op.region, 'ob_size') t_success = self.state.mktrans_assignment(stmt.lhs, v_ob_size, fnmeta.desc_when_call_returns_value('ob_size')) if self.object_ptr_has_global_ob_type(v_op, 'PyTuple_Type'): # We know it's a PyTupleObject; the call will succeed: return [t_success] # Can fail if not a tuple: # (For now, ignore the fact that it could be a tuple subclass) s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_SystemError', stmt.loc) t_failure = Transition(self.state, s_failure, fnmeta.desc_when_call_fails('not a tuple')) return [t_success, t_failure] ######################################################################## # PyType_* ######################################################################## def impl_PyType_IsSubtype(self, stmt, v_a, v_b): fnmeta = FnMeta(name='PyType_IsSubtype', docurl='http://docs.python.org/dev/c-api/type.html#PyType_IsSubtype') returntype = stmt.fn.type.dereference.type return [self.state.mktrans_assignment(stmt.lhs, UnknownValue.make(returntype, stmt.loc), None)] def impl_PyType_Ready(self, stmt, v_type): fnmeta = FnMeta(name='PyType_Ready', docurl='http://docs.python.org/dev/c-api/type.html#PyType_Ready') s_success = self.state.mkstate_concrete_return_of(stmt, 0) s_failure = self.state.mkstate_concrete_return_of(stmt, -1) s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc) # various possible errors return self.state.make_transitions_for_fncall(stmt, fnmeta, s_success, s_failure) ######################################################################## # PyUnicode_* ######################################################################## def impl_PyUnicode_AsUTF8String(self, stmt, v_unicode): fnmeta = FnMeta(name='PyUnicode_AsUTF8String', docurl='http://docs.python.org/c-api/unicode.html#PyUnicode_AsUTF8String', prototype='PyObject* PyUnicode_AsUTF8String(PyObject *unicode)', defined_in='Objects/unicodeobject.c') self.state.raise_any_null_ptr_func_arg(stmt, 0, v_unicode, why=invokes_Py_TYPE_via_macro(fnmeta, 'PyUnicode_Check')) r_newobj, t_success, t_failure = self.object_ctor_bytes(stmt) return [t_success, t_failure] def impl_PyUnicodeUCS4_AsUTF8String(self, stmt, v_unicode): return self.impl_PyUnicode_AsUTF8String(stmt, v_unicode) def impl_PyUnicode_DecodeUTF8(self, stmt, v_s, v_size, v_errors): fnmeta = FnMeta(name='PyUnicode_DecodeUTF8', docurl='http://docs.python.org/c-api/unicode.html#PyUnicode_DecodeUTF8', prototype=('PyObject *\n' 'PyUnicode_DecodeUTF8(const char *s,\n' ' Py_ssize_t size,\n' ' const char *errors)'), defined_in='Objects/unicodeobject.c') r_newobj, t_success, t_failure = self.object_ctor(stmt, 'PyUnicodeObject', 'PyUnicode_Type') return [t_success, t_failure] def impl_PyUnicodeUCS4_DecodeUTF8(self, stmt, v_s, v_size, v_errors): return self.impl_PyUnicode_DecodeUTF8(stmt, v_s, v_size, v_errors) ######################################################################## # PyWeakref_* ######################################################################## def impl_PyWeakref_GetObject(self, stmt, v_op): fnmeta = FnMeta(name='PyWeakref_GetObject', docurl='http://docs.python.org/c-api/weakref.html#PyWeakref_GetObject', defined_in='Objects/weakrefobject.c') if isinstance(v_op, UnknownValue): self.state.raise_split_value(v_op, stmt.loc) if v_op.is_null_ptr(): s_failure = self.state.mkstate_concrete_return_of(stmt, 0) s_failure.cpython.bad_internal_call(stmt.loc) return [Transition(self.state, s_failure, '%s() fails due to NULL argument' % fnmeta.name)] s_success = self.mkstate_borrowed_ref(stmt, fnmeta) return [Transition(self.state, s_success, None)] ######################################################################## # (end of Python API implementations) ######################################################################## ######################################################################## # SWIG_* ######################################################################## def impl_SWIG_Python_ErrorType(self, stmt, v_code): fnmeta = FnMeta(name='SWIG_Python_ErrorType', prototype='PyObject* SWIG_Python_ErrorType(int code)') # returns a borrowed reference to one of the builtin exception types # Cannot return NULL # For now, hardcode a TypeError: exc_decl = compat.get_exception_decl_by_name('PyExc_TypeError') check_isinstance(exc_decl, gcc.VarDecl) r_exception = self.state.var_region(exc_decl) v_exception = PointerToRegion(get_PyObjectPtr(), stmt.loc, r_exception) t_next = self.state.mktrans_assignment(stmt.lhs, v_exception, '%s()' % fnmeta.name) return [t_next] def impl_SWIG_Python_SetErrorMsg(self, stmt, v_errtype, v_msg): fnmeta = FnMeta(name='SWIG_Python_SetErrorMsg') # Calls PyErr_SetString: result = self.impl_PyErr_SetString(stmt, v_errtype, v_msg) for t_iter in result: t_iter.desc = 'calling %s()' % fnmeta.name return result def get_traces(fun): stmtgraph = make_stmt_graph(fun) return list(iter_traces(stmtgraph, {'cpython':CPython}, limits=Limits(maxtrans=1024))) def dump_traces_to_stdout(traces): """ For use in selftests: dump the traces to stdout, in a form that (hopefully) will allow usable comparisons against "gold" output ( not embedding anything that changes e.g. names of temporaries, address of wrapper objects, etc) """ def dump_object(rvalue, title): check_isinstance(rvalue, AbstractValue) print(' %s:' % title) print(' repr(): %r' % rvalue) print(' str(): %s' % rvalue) if isinstance(rvalue, PointerToRegion): print(' r->ob_refcnt: %s' % endstate.get_value_of_field_by_region(rvalue.region, 'ob_refcnt')) print(' r->ob_type: %r' % endstate.get_value_of_field_by_region(rvalue.region, 'ob_type')) def dump_region(region, title): check_isinstance(region, Region) print(' %s:' % title) print(' repr(): %r' % region) print(' str(): %s' % region) print(' r->ob_refcnt: %s' % endstate.get_value_of_field_by_region(region, 'ob_refcnt')) print(' r->ob_type: %r' % endstate.get_value_of_field_by_region(region, 'ob_type')) for i, trace in enumerate(traces): print('Trace %i:' % i) # Emit the "interesting transitions" i.e. those with descriptions: print(' Transitions:') for trans in trace.transitions: if trans.desc: print(' %r' % trans.desc) # Emit information about the end state: endstate = trace.states[-1] if trace.err: print(' error: %r' % trace.err) print(' error: %s' % trace.err) if endstate.return_rvalue: dump_object(endstate.return_rvalue, 'Return value') # Other affected PyObject instances: for k in endstate.region_for_var: if not isinstance(endstate.region_for_var[k], Region): continue region = endstate.region_for_var[k] # Consider those for which we know something about an "ob_refcnt" # field: if 'ob_refcnt' not in region.fields: continue if (isinstance(endstate.return_rvalue, PointerToRegion) and region == endstate.return_rvalue.region): # (We did the return value above) continue dump_region(region, str(region)) # Exception state: if hasattr(endstate, 'cpython'): print(' Exception:') print(' %s' % endstate.cpython.exception_rvalue) if i + 1 < len(traces): sys.stdout.write('\n') class DebugAnnotator(Annotator): """ Annotate a trace with copious debug information """ def get_notes(self, transition): loc = transition.src.get_gcc_loc_or_none() if loc is None: # (we can't add a note without a valid location) return [] result = [] # Add refcount information on all PyObject* for k in transition.dest.region_for_var: region = transition.dest.region_for_var[k] check_isinstance(region, Region) if 'ob_refcnt' not in region.fields: continue ra = RefcountAnnotator(region, region.name) result += ra.get_notes(transition) # Show all new/changing regions: for region in transition.dest.value_for_region: dest_value = transition.dest.value_for_region[region] if region in transition.src.value_for_region: src_value = transition.src.value_for_region[region] if dest_value != src_value: result.append(Note(loc, ('%s now has value: %s' % (region, dest_value)))) else: result.append(Note(loc, ('%s has initial value: %s' % (region, dest_value)))) # Show exception information: esa = ExceptionStateAnnotator() result += esa.get_notes(transition) return result class RefcountAnnotator(Annotator): """ Annotate a trace with information on the reference count of a particular object """ def __init__(self, region, desc): check_isinstance(region, Region) check_isinstance(desc, str) self.region = region self.desc = desc def get_notes(self, transition): """ Add a note to every transition that affects reference-counting for our target object """ loc = transition.src.get_gcc_loc_or_none() if loc is None: # (we can't add a note without a valid location) return [] result = [] # Add a note when the ob_refcnt of the object changes: src_refcnt = transition.src.get_value_of_field_by_region(self.region, 'ob_refcnt') dest_refcnt = transition.dest.get_value_of_field_by_region(self.region, 'ob_refcnt') if src_refcnt != dest_refcnt: log('src_refcnt: %r', src_refcnt) log('dest_refcnt: %r', dest_refcnt) result.append(Note(loc, ('ob_refcnt is now %s' % dest_refcnt))) # Add a note when there's a change to the set of persistent storage # locations referencing this object: src_refs = transition.src.get_persistent_refs_for_region(self.region) dest_refs = transition.dest.get_persistent_refs_for_region(self.region) if src_refs != dest_refs: result.append(Note(loc, ('%s is now referenced by %i non-stack value(s): %s' % (self.desc, len(dest_refs), ', '.join([ref.name for ref in dest_refs]))))) if 0: # For debugging: show the history of all references to the given # object: src_refs = transition.src.get_all_refs_for_region(self.region) dest_refs = transition.dest.get_all_refs_for_region(self.region) if src_refs != dest_refs: result.append(Note(loc, ('all refs: %s' % dest_refs))) return result class ExceptionStateAnnotator(Annotator): """ Annotate a trace with information on changes to the thread-local exception state """ def get_notes(self, transition): """ Add a note to every transition that affects thread-local exception state """ loc = transition.src.get_gcc_loc_or_none() if loc is None: # (we can't add a note without a valid location) return [] result = [] if hasattr(transition.dest, 'cpython'): if transition.dest.cpython.exception_rvalue != transition.src.cpython.exception_rvalue: result.append(Note(loc, ('thread-local exception state now has value: %s' % transition.dest.cpython.exception_rvalue))) return result from libcpychecker.initializers import get_all_PyTypeObject_initializers def function_is_tp_iternext_callback(fun): """ Is the given gcc.Function known to be used as the tp_iternext callback within a PyTypeObject? """ check_isinstance(fun, gcc.Function) for typeobj in get_all_PyTypeObject_initializers(): tp_iternext = typeobj.function_ptr_field('tp_iternext') if fun.decl == tp_iternext: return True # Helper function for when ob_refcnt is wrong: def emit_refcount_warning(msg, exp_refcnt, exp_refs, v_ob_refcnt, r_obj, desc, trace, endstate, fun, rep): w = rep.make_warning(fun, endstate.get_gcc_loc(fun), msg) # For dynamically-allocated objects, indicate where they # were allocated: if isinstance(r_obj, RegionOnHeap): alloc_loc = r_obj.alloc_stmt.loc if alloc_loc: w.add_note(r_obj.alloc_stmt.loc, ('%s was allocated at: %s' % (desc, get_src_for_loc(alloc_loc)))) details = ('was expecting final owned ob_refcnt of %s to be %i' % (desc, exp_refcnt)) if exp_refcnt > 0: details += (' due to object being referenced by: %s' % ', '.join(exp_refs)) else: details += (' since nothing references it') details += ' but final ob_refcnt is %s' % v_ob_refcnt w.add_note(endstate.get_gcc_loc(fun), details) # Summarize the control flow we followed through the function: if 1: annotator = RefcountAnnotator(r_obj, desc) else: # Debug help: from libcpychecker.diagnostics import TestAnnotator annotator = TestAnnotator() w.add_trace(trace, annotator) if 0: # Handy for debugging: w.add_note(endstate.get_gcc_loc(fun), 'this was trace %i' % i) return w # Inner loop of check_refcounts() below, split out to keep the # function a more manageable length. # Performs refcount-checking on a single object within one end State # of a Trace def check_refcount_for_one_object(r_obj, v_ob_refcnt, v_return, trace, endstate, fun, rep): # If it's the return value, it should have a net refcnt delta of # 1; all other PyObject should have a net delta of 0: if isinstance(v_return, PointerToRegion) and r_obj == v_return.region: is_return_value = True desc = 'return value' if fun.decl.name in fnnames_returning_borrowed_refs: # ...then this function has been marked as returning a # borrowed reference, rather than a new one: exp_refs = [] else: exp_refs = ['return value'] else: is_return_value = False # Try to get a descriptive name for the region: desc = trace.get_description_for_region(r_obj) # print('desc: %r' % desc) exp_refs = [] # The reference count should also reflect any non-stack pointers # that point at this object: exp_refs += [ref.name for ref in endstate.get_persistent_refs_for_region(r_obj)] exp_refcnt = len(exp_refs) log('exp_refs: %r', exp_refs) if fun.decl.name in stolen_refs_by_fnname: # Then this function is marked as stealing references to one or # more of its arguments: for argindex in stolen_refs_by_fnname[fun.decl.name]: # Get argument's value (in initial state of trace): parm = fun.decl.arguments[argindex - 1] v_parm = trace.states[0].eval_rvalue(parm, None) if isinstance(v_parm, PointerToRegion): if r_obj == v_parm.region: exp_refcnt -= 1 # Here's where we verify the refcount: if isinstance(v_ob_refcnt, RefcountValue): if v_ob_refcnt.relvalue > exp_refcnt: # Refcount is too high: w = emit_refcount_warning('memory leak: ob_refcnt of %s is %i too high' % (desc, v_ob_refcnt.relvalue - exp_refcnt), exp_refcnt, exp_refs, v_ob_refcnt, r_obj, desc, trace, endstate, fun, rep) elif v_ob_refcnt.relvalue < exp_refcnt: # Refcount is too low: w = emit_refcount_warning('future use-after-free: ob_refcnt of %s is %i too low' % (desc, exp_refcnt - v_ob_refcnt.relvalue), exp_refcnt, exp_refs, v_ob_refcnt, r_obj, desc, trace, endstate, fun, rep) # Special-case hint for when None has too low a refcount: if is_return_value: if isinstance(v_return.region, RegionForGlobal): if v_return.region.vardecl.name == '_Py_NoneStruct': w.add_note(endstate.get_gcc_loc(fun), 'consider using "Py_RETURN_NONE;"') # Detect failure to set exceptions when returning NULL, # and verify usage of # __attribute__((cpychecker_negative_result_sets_exception)) def warn_about_NULL_without_exception(v_return, trace, endstate, fun, rep): if not trace.err: if (isinstance(v_return, ConcreteValue) and v_return.value == 0 and str(v_return.gcctype)=='struct PyObject *'): if (isinstance(endstate.cpython.exception_rvalue, ConcreteValue) and endstate.cpython.exception_rvalue.value == 0): # Don't emit the error for functions that are a # PyTypeObject's tp_iternext callback, as it's # legitimate to return NULL from them: # http://docs.python.org/c-api/typeobj.html#tp_iternext if function_is_tp_iternext_callback(fun): return w = rep.make_warning(fun, endstate.get_gcc_loc(fun), 'returning (PyObject*)NULL without setting an exception') w.add_trace(trace, ExceptionStateAnnotator()) # If this is function was marked with our custom # __attribute__((cpychecker_sets_exception)) # then verify that this is the case: if fun.decl.name in fnnames_setting_exception: if (isinstance(endstate.cpython.exception_rvalue, ConcreteValue) and endstate.cpython.exception_rvalue.value == 0): w = rep.make_warning(fun, endstate.get_gcc_loc(fun), ('function is marked with' ' __attribute__((cpychecker_sets_exception))' ' but can return without setting an exception')) w.add_trace(trace, ExceptionStateAnnotator()) # If this is function was marked with our custom # __attribute__((cpychecker_negative_result_sets_exception)) # then verify that this is the case: if fun.decl.name in fnnames_setting_exception_on_negative_result: if (isinstance(v_return, ConcreteValue) and v_return.value < 0): if (isinstance(endstate.cpython.exception_rvalue, ConcreteValue) and endstate.cpython.exception_rvalue.value == 0): w = rep.make_warning(fun, endstate.get_gcc_loc(fun), ('function is marked with __attribute__((' 'cpychecker_negative_result_sets_exception))' ' but can return %s without setting an exception' % v_return.value)) w.add_trace(trace, ExceptionStateAnnotator()) def make_stmt_graph(fun): stmtgraph = StmtGraph(fun, False, omit_complex_edges=True) return stmtgraph def impl_check_refcounts(fun, dump_traces=False, show_possible_null_derefs=False, maxtrans=256): """ Inner implementation of the refcount checker, checking the refcounting behavior of a function, returning a Reporter instance. Used by check_refcounts, but also exposed for use by unit tests that want to get at the Reporter directly (e.g. for JSON output) fun: the gcc.Function to be checked dump_traces: bool: if True, dump information about the traces through the function to stdout (for self tests) """ # Abstract interpretation: # Walk the CFG, gathering the information we're interested in check_isinstance(fun, gcc.Function) # Generate a mapping from facet names to facet classes, so that we know # what additional attributes each State instance will have. # # For now, we just have an optional CPython instance per state, but only # for code that's included Python's headers: facets = {} if get_PyObject(): facets['cpython'] = CPython limits=Limits(maxtrans=maxtrans) stmtgraph = make_stmt_graph(fun) if 0: dot = stmtgraph.to_dot('foo') from gccutils import invoke_dot invoke_dot(dot) try: traces = iter_traces(stmtgraph, facets, limits=limits) except TooComplicated: err = sys.exc_info()[1] gcc.inform(fun.start, 'this function is too complicated for the reference-count checker to fully analyze: not all paths were analyzed') traces = err.complete_traces if dump_traces: traces = list(traces) dump_traces_to_stdout(traces) # Debug dump of all traces in HTML form: if 0: filename = ('%s.%s-refcount-traces.html' % (gcc.get_dump_base_name(), fun.decl.name)) rep = Reporter() for i, trace in enumerate(traces): endstate = trace.states[-1] r = rep.make_debug_dump(fun, endstate.get_gcc_loc(fun), 'Debug dump of trace %i' % i) r.add_trace(trace, DebugAnnotator()) rep.dump_html(fun, filename) rep.flush() gcc.inform(fun.start, ('graphical debug report for function %r written out to %r' % (fun.decl.name, filename))) rep = Reporter() # Iterate through all traces, adding reports to the Reporter: for i, trace in enumerate(traces): trace.log(log, 'TRACE %i' % i) if trace.err: # This trace bails early with a fatal error; it probably doesn't # have a return value log('trace.err: %s %r', trace.err, trace.err) # Unless explicitly enabled, don't report on NULL pointer # dereferences that are only possible, not definite: it may be # that there are invariants that we know nothing about that mean # that they can't happen: # (similarly for arithmetic issues e.g. negative shift, divide by # zero, etc) if isinstance(trace.err, (NullPtrDereference, NullPtrArgument, PredictedArithmeticError)): if not trace.err.isdefinite: if not show_possible_null_derefs: continue w = rep.make_warning(fun, trace.err.loc, str(trace.err)) w.add_trace(trace) if hasattr(trace.err, 'why'): if trace.err.why: w.add_note(trace.err.loc, trace.err.why) # FIXME: in our example this ought to mention where the values came from continue # Otherwise, the trace proceeds normally v_return = trace.return_value() log('trace.return_value(): %s', trace.return_value()) # Ideally, we should "own" exactly one reference, and it should be # the return value. Anything else is an error (and there are other # kinds of error...) # Locate all PyObject that we touched endstate = trace.states[-1] endstate.log(log) log('return_value: %r', v_return) log('endstate.region_for_var: %r', endstate.region_for_var) log('endstate.value_for_region: %r', endstate.value_for_region) if endstate.not_returning: # We have a function that calls exit() or abort() or similar # Don't bother reporting reference leaks etc: the process is # going away continue # Check the refcount of all Python objects we know about: if hasattr(endstate, 'cpython'): for r_obj, v_ob_refcnt in endstate.cpython.iter_python_refcounts(): check_refcount_for_one_object(r_obj, v_ob_refcnt, v_return, trace, endstate, fun, rep) # Detect returning a deallocated object: if v_return: if isinstance(v_return, PointerToRegion): rvalue = endstate.value_for_region.get(v_return.region, None) if isinstance(rvalue, DeallocatedMemory): w = rep.make_warning(fun, endstate.get_gcc_loc(fun), 'returning pointer to deallocated memory') w.add_trace(trace) w.add_note(rvalue.loc, 'memory deallocated here') warn_about_NULL_without_exception(v_return, trace, endstate, fun, rep) # (all traces analysed) return rep def check_refcounts(fun, dump_traces=False, show_traces=False, show_possible_null_derefs=False, show_timings=False, maxtrans=256, dump_json=False): """ The top-level function of the refcount checker, checking the refcounting behavior of a function fun: the gcc.Function to be checked dump_traces: bool: if True, dump information about the traces through the function to stdout (for self tests) show_traces: bool: if True, display a diagram of the state transition graph show_timings: bool: if True, add timing information to stderr """ log('check_refcounts(%r, %r, %r)', fun, dump_traces, show_traces) # show_timings = 1 if show_timings: import time start_cpusecs = time.clock() gcc.inform(fun.start, 'Analyzing reference-counting within %s' % fun.decl.name) if show_traces: from libcpychecker.visualizations import StateGraphPrettyPrinter sg = StateGraph(fun, log, MyState) sgpp = StateGraphPrettyPrinter(sg) dot = sgpp.to_dot() #dot = sgpp.extra_items() # print(dot) invoke_dot(dot) rep = impl_check_refcounts(fun, dump_traces, show_possible_null_derefs, maxtrans) # Organize the Report instances into equivalence classes, simplifying # the list of reports: rep.remove_duplicates() # Flush the reporter's messages, which will actually emit gcc errors and # warnings (if any), for those Report instances that survived # de-duplication rep.flush() if rep.got_warnings(): if dump_json: # JSON output: filename = ('%s.%s.json' % (gcc.get_dump_base_name(), fun.decl.name)) rep.dump_json(fun, filename) filename = ('%s.%s-refcount-errors.html' % (gcc.get_dump_base_name(), fun.decl.name)) rep.dump_html(fun, filename) gcc.inform(fun.start, ('graphical error report for function %r written out to %r' % (fun.decl.name, filename))) filename_v2 = ('%s.%s-refcount-errors.v2.html' % (gcc.get_dump_base_name(), fun.decl.name)) from libcpychecker_html.make_html import HtmlPage data = rep.to_json(fun) srcfile = open(fun.start.file) htmlfile = open(filename_v2, 'w') htmlfile.write(str(HtmlPage(srcfile, data))) htmlfile.close() srcfile.close() if show_timings: end_cpusecs = time.clock() gcc.inform(fun.start, 'Finished analyzing reference-counting within %s' % fun.decl.name) gcc.inform(fun.start, ('%i transitions, %fs CPU' % (limits.trans_seen, end_cpusecs - start_cpusecs))) if 0: dot = cfg_to_dot(fun.cfg, fun.decl.name) invoke_dot(dot) return rep gcc-python-plugin-0.17/libcpychecker/types.py000066400000000000000000000066011342215241600213400ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . """ Helper functions for looking up various CPython implementation types. """ import gcc from gccutils import get_global_typedef, check_isinstance def is_py3k(): """ Is the Python.h we're compiling against python 3? """ if get_global_typedef('PyStringObject'): return False else: return True def is_debug_build(): """ Is the Python.h we're compiling against configured --with-pydebug ? """ obj = get_global_typedef('PyObject') return obj.type.fields[0].name == '_ob_next' def get_Py_ssize_t(): return get_global_typedef('Py_ssize_t') def get_Py_buffer(): return get_global_typedef('Py_buffer') def Py_UNICODE(): return get_global_typedef('Py_UNICODE') def get_PY_LONG_LONG(): # pyport.h can supply PY_LONG_LONG as a #define, as a typedef, or not at all # FIXME # If we have "long long", pyport.h uses that. # Assume so for now: return gcc.Type.long_long() def get_PyObject(): return get_global_typedef('PyObject') def get_PyObjectPtr(): return get_global_typedef('PyObject').pointer def get_PyTypeObject(): return get_global_typedef('PyTypeObject') def get_PyStringObject(): return get_global_typedef('PyStringObject') def get_PyUnicodeObject(): return get_global_typedef('PyUnicodeObject') def get_Py_complex(): return get_global_typedef('Py_complex') # Python 3: def get_PyBytesObject(): return get_global_typedef('PyBytesObject') # Map from name of PyTypeObject global to the typedef for the corresponding # object structure: type_dict = { 'PyBuffer_Type' : 'PyBufferObject', 'PyComplex_Type' : 'PyComplexObject', 'PyCode_Type' : 'PyCodeObject', 'PyDict_Type' : 'PyDictObject', 'PyFile_Type' : 'PyFileObject', 'PyFloat_Type' : 'PyFloatObject', 'PyFrame_Type' : 'PyFrameObject', 'PyFunction_Type' : 'PyFunctionObject', 'PyInt_Type' : 'PyIntObject', 'PyList_Type' : 'PyListObject', 'PyLong_Type' : 'PyLongObject', 'PyModule_Type' : 'PyModuleObject', 'PyCapsule_Type' : 'PyCapsuleObject', 'PyRange_Type' : 'PyRangeObject', 'PySet_Type' : 'PySetObject', 'PyFrozenSet_Type' : 'PyFrozenSetObject', 'PyString_Type' : 'PyStringObject', 'PyTuple_Type' : 'PyTupleObject', 'PyType_Type' : 'PyTypeObject', 'PyUnicode_Type' : 'PyUnicodeObject', } def get_type_for_typeobject(typeobject): check_isinstance(typeobject, gcc.VarDecl) if typeobject.name not in type_dict: return None type_name = type_dict[typeobject.name] return get_global_typedef(type_name) def register_type_object(typeobject, typedef): check_isinstance(typeobject, gcc.VarDecl) type_dict[typeobject.name] = typedef gcc-python-plugin-0.17/libcpychecker/utils.py000066400000000000000000000024551342215241600213370ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . # Logging import sys import gcc logfile = None logging_enabled = False def log(msg, *args): if logging_enabled: # Only do the work of expanding the message if logging is enabled: expanded_msg = msg % args if 1: global logfile if not logfile: filename = gcc.get_dump_base_name() + '.cpychecker-log.txt' logfile = open(filename, 'w') logfile.write(expanded_msg) logfile.write('\n') if 0: sys.stderr.write(expanded_msg) sys.stderr.write('\n') gcc-python-plugin-0.17/libcpychecker/visualizations.py000066400000000000000000000356531342215241600232710ustar00rootroot00000000000000# Copyright 2011 David Malcolm # Copyright 2011 Red Hat, Inc. # # This 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 # . import gcc from gccutils import CfgPrettyPrinter, get_src_for_loc, check_isinstance class StatePrettyPrinter(CfgPrettyPrinter): """ Various ways of annotating a CFG with state information """ def state_to_dot_label(self, state): result = '\n' for key in state.data: value = state.data[key] result += (' %s %s \n' % (self._dot_td(key), self._dot_td(value))) result += '
\n' return result class TracePrettyPrinter(StatePrettyPrinter): """ Annotate a CFG, showing a specific trace of execution through it """ def __init__(self, cfg, trace): self.cfg = cfg self.trace = trace def extra_items(self): # Hook for expansion result = '' result += ' subgraph cluster_trace {\n' result += ' label="Trace";\n' for i, state in enumerate(self.trace.states): result += (' state%i [label=<%s>];\n' % (i, self.state_to_dot_label(state))) if i > 0: result += ' state%i -> state%i;\n' % (i-1, i) result += ' state%i -> %s:stmt%i;\n' % (i, self.block_id(state.loc.bb), state.loc.idx) result += ' }\n'; return result class StateGraphPrettyPrinter(StatePrettyPrinter): """ Annotate a CFG, showing all possible states as execution proceeds through it """ def __init__(self, sg): self.sg = sg self.name = sg.fun.decl.name self.cfg = sg.fun.cfg def state_id(self, state): return 'state%i' % id(state) def state_to_dot_label(self, state, prevstate): result = '\n' # Show data: result += '' # Show location: stmt = state.loc.get_stmt() if stmt: if stmt.loc: result += ('\n') result += '' + self.stmt_to_html(stmt, state.loc.idx) + '\n' result += '
' result += (' %s %s %s\n' % (self._dot_td('Expression'), self._dot_td('lvalue'), self._dot_td('rvalue'))) for key in state.region_for_var: region = state.region_for_var[key] value = state.value_for_region.get(region, None) # Highlight new and changing values: is_new_key = True is_different_value = False if prevstate: if key in prevstate.region_for_var: is_new_key = False prevregion = prevstate.region_for_var[key] prevvalue = prevstate.value_for_region.get(prevregion, None) if value != prevvalue: is_different_value = True if is_new_key: bgcolor = 'green' value_bgcolor = 'green' else: bgcolor = None if is_different_value: value_bgcolor = 'green' else: value_bgcolor = None result += (' %s %s %s\n' % (self._dot_td(key, bgcolor=bgcolor), self._dot_td(region, bgcolor=bgcolor), self._dot_td(value, bgcolor=value_bgcolor))) # Show any return value: if state.return_rvalue: result += (' %s %s %s\n' % (self._dot_td(''), self._dot_td('Return Value', bgcolor='green'), self._dot_td(state.return_rvalue, bgcolor='green'))) result += '
' + self.to_html('%4i ' % stmt.loc.line) + self.code_to_html(get_src_for_loc(stmt.loc)) + '
' + (' ' * (5 + stmt.loc.column-1)) + '^' + '
\n' return result def extra_items(self): # Hook for expansion result = '' result += ' subgraph cluster_state_transitions {\n' result += ' label="State Transitions";\n' result += ' node [shape=box];\n' for state in self.sg.states: prevstate = self.sg.get_prev_state(state) result += (' %s [label=<%s>];\n' % (self.state_id(state), self.state_to_dot_label(state, prevstate))) #result += (' %s -> %s:stmt%i;\n' # % (self.state_id(state), # self.block_id(state.loc.bb), # state.loc.idx)) for transition in self.sg.transitions: if transition.desc: attrliststr = '[label = "%s"]' % self.to_html(transition.desc) else: attrliststr = '' result += (' %s -> %s %s;\n' % (self.state_id(transition.src), self.state_id(transition.dest), attrliststr)) result += ' }\n'; return result #def to_dot(self): # result = 'digraph {\n' # result += self.extra_items() # result += '}\n'; # return result class HtmlRenderer: """ Render a function as HTML, possibly with annotations Uses pygments to syntax-highlight the code. The resulting HTML uses jsplumb to add lines indicating control flow: http://code.google.com/p/jsplumb/ which requires JavaScript and the HTML element """ def __init__(self, fun): check_isinstance(fun, gcc.Function) self.fun = fun from pygments.styles import get_style_by_name from pygments.formatters import HtmlFormatter # Get ready to use Pygments: style = get_style_by_name('default') self.formatter = HtmlFormatter(classprefix='source_') self.trace_idx = 0 def make_header(self): result = '\n' result += ' \n' result += ' %s\n' % self.fun.decl.name # CSS defs, as part of the file: result += ''' \n' result += ' \n' result += ' \n' # Add scripts for jsplumb: result += '\n' result += '\n' result += '\n' result += '\n' return result def make_report(self, report): result = '
\n' % self.trace_idx # Heading: result += '\n' result += ' \n' % self.fun.start.file result += ' \n' % self.fun.decl.name result += ' \n' % report.msg result += '
File: %s
Function: %s
Error: %s
\n' # Render any trace that we have: if report.trace: result += self.make_html_for_trace(report, report.trace) result += '
' result += '
\n' return result def make_html_for_trace(self, report, trace): start_line = self.fun.decl.location.line - 1 end_line = self.fun.end.line + 1 # Figure out which lines get executed. # (Is this the finest granularity we can measure?) reached_lines = set() for state in trace.states: loc = state.get_gcc_loc_or_none() if loc: reached_lines.add(loc.line) # Render source code: srcfile = self.fun.start.file # Extract the source code for the function: import linecache code = '' for linenum in range(start_line, end_line): code += linecache.getline(srcfile, linenum) # Use pygments to convert it all to HTML: from pygments import highlight from pygments.lexers import CLexer html = highlight(code, CLexer(), # FIXME: ^^^ this hardcodes the source language # (e.g. what about C++?) self.formatter) # Carve it up by line, adding our own line numbering: # It contains some initial content, leading up to a
 element
        # Break on it, starting "result" with the initial material:
        html = html.replace('
', '
\n')
        lines = html.splitlines()
        result = lines[0]

        # Generate any notes from the report's annotator (if any):
        notes = []
        annotator = report.get_annotator_for_trace(trace)
        if annotator:
            for trans in trace.transitions:
                notes += annotator.get_notes(trans)

        # The rest contains the actual source lines:
        lines = lines[1:]
        for linenum, line in zip(range(start_line, end_line), lines):
            # Line number:
            if linenum in reached_lines:
                cls = 'reached-lineno'
            else:
                cls = 'lineno'
            result += '%i ' % (cls, linenum)

            # The body of the line:
            if linenum in reached_lines:
                cls = 'reached-line'
            else:
                cls = 'unreached-line'
            result += '' % (self.trace_idx, linenum, cls)
            result += line
            result += '\n'

            # Add any comments for this line:
            for trans in trace.transitions:
                if trans.desc:
                    src_loc = trans.src.get_gcc_loc_or_none()
                    if src_loc and src_loc.line == linenum:
                        result += '%s\n' % trans.desc
            # Report the top-level message, if it happens here:
            if report.loc.line == linenum:
                result += '%s\n' % report.msg
            # Add notes attached to the report:
            for note in report.notes:
                if note.loc and note.loc.line == linenum:
                    result += '%s\n' % note.msg
            # Add any notes from the annotator:
            for note in notes:
                if note.loc and note.loc.line == linenum:
                    result += '%s\n' % note.msg

        result += '
\n' result += '\n' result += '\n' self.trace_idx += 1 return result def make_footer(self): result = ' \n' return result gcc-python-plugin-0.17/libcpychecker_html/000077500000000000000000000000001342215241600206435ustar00rootroot00000000000000gcc-python-plugin-0.17/libcpychecker_html/TODO000066400000000000000000000023721342215241600213370ustar00rootroot00000000000000# Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without any warranty. These are things we planned, but didn't get done, yet. buttons: Should add the "selected" style to the annotations, allowing the user to step through them. report selection: Figure out which report is visible, and make the buttons control *that* report if more than one report is visible, use the previous selection. Change selection when clicking report links as well. annotation clicking: Clicking an annotation makes it selected, until clicking the selected event. Users can use this to stop stepping through notes as well. alignment of notes: When selecting a note, it should line up with the line it's annotating. Move the "states" pane up and down, and make sure the overflow is clipped. There may be some jquery plugins that will make this much easier. * https://developer.mozilla.org/en/DOM/element.offsetTop * http://api.jquery.com/position/ more data: When a state is selected, we could apply mouseovers to variables that show their value within that state. gcc-python-plugin-0.17/libcpychecker_html/__init__.py000066400000000000000000000000001342215241600227420ustar00rootroot00000000000000gcc-python-plugin-0.17/libcpychecker_html/c-api.txt000066400000000000000000000515031342215241600224010ustar00rootroot00000000000000# Copyright 2012 Buck Golemon # # This 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 # . # # This is simply a listing of all the function names in the Python C API # # To regenerate, run this from a checkout of cpython: # grep -RI '\.\. cfunction::' Doc/c-api/ | sed 's|Doc/c-api/||;s/(.*//;s/\.rst:.* / /' # init Py_Initialize init Py_InitializeEx init Py_IsInitialized init Py_Finalize init Py_NewInterpreter init Py_EndInterpreter init Py_SetProgramName init Py_GetProgramName init Py_GetPrefix init Py_GetExecPrefix init Py_GetProgramFullPath init Py_GetPath init Py_GetVersion init Py_GetPlatform init Py_GetCopyright init Py_GetCompiler init Py_GetBuildInfo init PySys_SetArgvEx init PySys_SetArgv init Py_SetPythonHome init Py_GetPythonHome init PyEval_InitThreads init PyEval_ThreadsInitialized init PyEval_AcquireLock init PyEval_ReleaseLock init PyEval_AcquireThread init PyEval_ReleaseThread init PyEval_SaveThread init PyEval_RestoreThread init PyEval_ReInitThreads init PyInterpreterState_New init PyInterpreterState_Clear init PyInterpreterState_Delete init PyThreadState_New init PyThreadState_Clear init PyThreadState_Delete init PyThreadState_Get init PyThreadState_Swap init PyThreadState_GetDict init PyThreadState_SetAsyncExc init PyGILState_Ensure init PyGILState_Release init Py_AddPendingCall init PyEval_SetProfile init PyEval_SetTrace init PyEval_GetCallStats init PyInterpreterState_Head init PyInterpreterState_Next init PyInterpreterState_ThreadHead init PyThreadState_Next file PyFile_Check file PyFile_CheckExact file PyFile_FromString file PyFile_FromFile file PyFile_AsFile file PyFile_IncUseCount file PyFile_DecUseCount file PyFile_GetLine file PyFile_Name file PyFile_SetBufSize file PyFile_SetEncoding file PyFile_SetEncodingAndErrors file PyFile_SoftSpace file PyFile_WriteObject file PyFile_WriteString import PyImport_ImportModule import PyImport_ImportModuleNoBlock import PyImport_ImportModuleEx import PyImport_ImportModuleLevel import PyImport_Import import PyImport_ReloadModule import PyImport_AddModule import PyImport_ExecCodeModule import PyImport_ExecCodeModuleEx import PyImport_GetMagicNumber import PyImport_GetModuleDict import PyImport_GetImporter import _PyImport_Init import PyImport_Cleanup import _PyImport_Fini import _PyImport_FindExtension import _PyImport_FixupExtension import PyImport_ImportFrozenModule import PyImport_AppendInittab import PyImport_ExtendInittab set PySet_Check set PyFrozenSet_Check set PyAnySet_Check set PyAnySet_CheckExact set PyFrozenSet_CheckExact set PySet_New set PyFrozenSet_New set PySet_Size set PySet_GET_SIZE set PySet_Contains set PySet_Add set PySet_Discard set PySet_Pop set PySet_Clear allocation _PyObject_New allocation _PyObject_NewVar allocation _PyObject_Del allocation PyObject_Init allocation PyObject_InitVar allocation PyObject_New allocation PyObject_NewVar allocation PyObject_Del allocation Py_InitModule allocation Py_InitModule3 allocation Py_InitModule4 bool PyBool_Check bool PyBool_FromLong method PyMethod_Check method PyMethod_New method PyMethod_Class method PyMethod_GET_CLASS method PyMethod_Function method PyMethod_GET_FUNCTION method PyMethod_Self method PyMethod_GET_SELF method PyMethod_ClearFreeList module PyModule_Check module PyModule_CheckExact module PyModule_New module PyModule_GetDict module PyModule_GetName module PyModule_GetFilename module PyModule_AddObject module PyModule_AddIntConstant module PyModule_AddStringConstant module PyModule_AddIntMacro module PyModule_AddStringMacro marshal PyMarshal_WriteLongToFile marshal PyMarshal_WriteObjectToFile marshal PyMarshal_WriteObjectToString marshal PyMarshal_ReadLongFromFile marshal PyMarshal_ReadShortFromFile marshal PyMarshal_ReadObjectFromFile marshal PyMarshal_ReadLastObjectFromFile marshal PyMarshal_ReadObjectFromString slice PySlice_Check slice PySlice_New slice PySlice_GetIndices slice PySlice_GetIndicesEx unicode PyUnicode_Check unicode PyUnicode_CheckExact unicode PyUnicode_GET_SIZE unicode PyUnicode_GET_DATA_SIZE unicode PyUnicode_AS_UNICODE unicode PyUnicode_AS_DATA unicode PyUnicode_ClearFreeList unicode Py_UNICODE_ISSPACE unicode Py_UNICODE_ISLOWER unicode Py_UNICODE_ISUPPER unicode Py_UNICODE_ISTITLE unicode Py_UNICODE_ISLINEBREAK unicode Py_UNICODE_ISDECIMAL unicode Py_UNICODE_ISDIGIT unicode Py_UNICODE_ISNUMERIC unicode Py_UNICODE_ISALPHA unicode Py_UNICODE_ISALNUM unicode Py_UNICODE_TOLOWER unicode Py_UNICODE_TOUPPER unicode Py_UNICODE_TOTITLE unicode Py_UNICODE_TODECIMAL unicode Py_UNICODE_TODIGIT unicode Py_UNICODE_TONUMERIC unicode PyUnicode_FromUnicode unicode PyUnicode_FromStringAndSize unicode *PyUnicode_FromString unicode PyUnicode_FromFormat unicode PyUnicode_FromFormatV unicode PyUnicode_AsUnicode unicode PyUnicode_GetSize unicode PyUnicode_FromEncodedObject unicode PyUnicode_FromObject unicode PyUnicode_FromWideChar unicode PyUnicode_AsWideChar unicode PyUnicode_Decode unicode PyUnicode_Encode unicode PyUnicode_AsEncodedString unicode PyUnicode_DecodeUTF8 unicode PyUnicode_DecodeUTF8Stateful unicode PyUnicode_EncodeUTF8 unicode PyUnicode_AsUTF8String unicode PyUnicode_DecodeUTF32 unicode PyUnicode_DecodeUTF32Stateful unicode PyUnicode_EncodeUTF32 unicode PyUnicode_AsUTF32String unicode PyUnicode_DecodeUTF16 unicode PyUnicode_DecodeUTF16Stateful unicode PyUnicode_EncodeUTF16 unicode PyUnicode_AsUTF16String unicode PyUnicode_DecodeUTF7 unicode PyUnicode_DecodeUTF7Stateful unicode PyUnicode_EncodeUTF7 unicode PyUnicode_DecodeUnicodeEscape unicode PyUnicode_EncodeUnicodeEscape unicode PyUnicode_AsUnicodeEscapeString unicode PyUnicode_DecodeRawUnicodeEscape unicode PyUnicode_EncodeRawUnicodeEscape unicode PyUnicode_AsRawUnicodeEscapeString unicode PyUnicode_DecodeLatin1 unicode PyUnicode_EncodeLatin1 unicode PyUnicode_AsLatin1String unicode PyUnicode_DecodeASCII unicode PyUnicode_EncodeASCII unicode PyUnicode_AsASCIIString unicode PyUnicode_DecodeCharmap unicode PyUnicode_EncodeCharmap unicode PyUnicode_AsCharmapString unicode PyUnicode_TranslateCharmap unicode PyUnicode_DecodeMBCS unicode PyUnicode_DecodeMBCSStateful unicode PyUnicode_EncodeMBCS unicode PyUnicode_AsMBCSString unicode PyUnicode_Concat unicode PyUnicode_Split unicode PyUnicode_Splitlines unicode PyUnicode_Translate unicode PyUnicode_Join unicode PyUnicode_Tailmatch unicode PyUnicode_Find unicode PyUnicode_Count unicode PyUnicode_Replace unicode PyUnicode_Compare unicode PyUnicode_RichCompare unicode PyUnicode_Format unicode PyUnicode_Contains int PyInt_Check int PyInt_CheckExact int PyInt_FromString int PyInt_FromLong int PyInt_FromSsize_t int PyInt_FromSize_t int PyInt_AsLong int PyInt_AS_LONG int PyInt_AsUnsignedLongMask int PyInt_AsUnsignedLongLongMask int PyInt_AsSsize_t int PyInt_GetMax int PyInt_ClearFreeList type PyType_Check type PyType_CheckExact type PyType_ClearCache type PyType_Modified type PyType_HasFeature type PyType_IS_GC type PyType_IsSubtype type PyType_GenericAlloc type PyType_GenericNew type PyType_Ready number PyNumber_Check number PyNumber_Add number PyNumber_Subtract number PyNumber_Multiply number PyNumber_Divide number PyNumber_FloorDivide number PyNumber_TrueDivide number PyNumber_Remainder number PyNumber_Divmod number PyNumber_Power number PyNumber_Negative number PyNumber_Positive number PyNumber_Absolute number PyNumber_Invert number PyNumber_Lshift number PyNumber_Rshift number PyNumber_And number PyNumber_Xor number PyNumber_Or number PyNumber_InPlaceAdd number PyNumber_InPlaceSubtract number PyNumber_InPlaceMultiply number PyNumber_InPlaceDivide number PyNumber_InPlaceFloorDivide number PyNumber_InPlaceTrueDivide number PyNumber_InPlaceRemainder number PyNumber_InPlacePower number PyNumber_InPlaceLshift number PyNumber_InPlaceRshift number PyNumber_InPlaceAnd number PyNumber_InPlaceXor number PyNumber_InPlaceOr number PyNumber_Coerce number PyNumber_CoerceEx number PyNumber_Int number PyNumber_Long number PyNumber_Float number PyNumber_Index number PyNumber_ToBase number PyNumber_AsSsize_t number PyIndex_Check function PyFunction_Check function PyFunction_New function PyFunction_GetCode function PyFunction_GetGlobals function PyFunction_GetModule function PyFunction_GetDefaults function PyFunction_SetDefaults function PyFunction_GetClosure function PyFunction_SetClosure structures Py_FindMethod iterator PySeqIter_Check iterator PySeqIter_New iterator PyCallIter_Check iterator PyCallIter_New sys Py_FdIsInteractive sys PyOS_AfterFork sys PyOS_CheckStack sys PyOS_getsig sys PyOS_setsig sys *PySys_GetObject sys *PySys_GetFile sys PySys_SetObject sys PySys_ResetWarnOptions sys PySys_AddWarnOption sys PySys_SetPath sys PySys_WriteStdout sys PySys_WriteStderr sys Py_FatalError sys Py_Exit sys Py_AtExit cobject PyCObject_Check cobject PyCObject_FromVoidPtr cobject PyCObject_FromVoidPtrAndDesc cobject PyCObject_AsVoidPtr cobject PyCObject_GetDesc cobject PyCObject_SetVoidPtr veryhigh Py_Main veryhigh PyRun_AnyFile veryhigh PyRun_AnyFileFlags veryhigh PyRun_AnyFileEx veryhigh PyRun_AnyFileExFlags veryhigh PyRun_SimpleString veryhigh PyRun_SimpleStringFlags veryhigh PyRun_SimpleFile veryhigh PyRun_SimpleFileFlags veryhigh PyRun_SimpleFileEx veryhigh PyRun_SimpleFileExFlags veryhigh PyRun_InteractiveOne veryhigh PyRun_InteractiveOneFlags veryhigh PyRun_InteractiveLoop veryhigh PyRun_InteractiveLoopFlags veryhigh PyParser_SimpleParseString veryhigh PyParser_SimpleParseStringFlags veryhigh PyParser_SimpleParseStringFlagsFilename veryhigh PyParser_SimpleParseFile veryhigh PyParser_SimpleParseFileFlags veryhigh PyRun_String veryhigh PyRun_StringFlags veryhigh PyRun_File veryhigh PyRun_FileEx veryhigh PyRun_FileFlags veryhigh PyRun_FileExFlags veryhigh Py_CompileString veryhigh Py_CompileStringFlags veryhigh PyEval_EvalCode veryhigh PyEval_EvalCodeEx veryhigh PyEval_EvalFrame veryhigh PyEval_EvalFrameEx veryhigh PyEval_MergeCompilerFlags capsule PyCapsule_CheckExact capsule PyCapsule_New capsule PyCapsule_GetPointer capsule PyCapsule_GetDestructor capsule PyCapsule_GetContext capsule PyCapsule_GetName capsule PyCapsule_Import capsule PyCapsule_IsValid capsule PyCapsule_SetContext capsule PyCapsule_SetDestructor capsule PyCapsule_SetName capsule PyCapsule_SetPointer bytearray PyByteArray_Check bytearray PyByteArray_CheckExact bytearray PyByteArray_FromObject bytearray PyByteArray_FromStringAndSize bytearray PyByteArray_Concat bytearray PyByteArray_Size bytearray PyByteArray_AsString bytearray PyByteArray_Resize bytearray PyByteArray_AS_STRING bytearray PyByteArray_GET_SIZE sequence PySequence_Check sequence PySequence_Size sequence PySequence_Concat sequence PySequence_Repeat sequence PySequence_InPlaceConcat sequence PySequence_InPlaceRepeat sequence PySequence_GetItem sequence PySequence_GetSlice sequence PySequence_SetItem sequence PySequence_DelItem sequence PySequence_SetSlice sequence PySequence_DelSlice sequence PySequence_Count sequence PySequence_Contains sequence PySequence_Index sequence PySequence_List sequence PySequence_Tuple sequence PySequence_Fast sequence PySequence_Fast_GET_ITEM sequence PySequence_Fast_ITEMS sequence PySequence_ITEM sequence PySequence_Fast_GET_SIZE object PyObject_Print object PyObject_HasAttr object PyObject_HasAttrString object PyObject_GetAttr object PyObject_GetAttrString object PyObject_GenericGetAttr object PyObject_SetAttr object PyObject_SetAttrString object PyObject_GenericSetAttr object PyObject_DelAttr object PyObject_DelAttrString object PyObject_RichCompare object PyObject_RichCompareBool object PyObject_Cmp object PyObject_Compare object PyObject_Repr object PyObject_Str object PyObject_Bytes object PyObject_Unicode object PyObject_IsInstance object PyObject_IsSubclass object PyCallable_Check object PyObject_Call object PyObject_CallObject object PyObject_CallFunction object PyObject_CallMethod object PyObject_CallFunctionObjArgs object PyObject_CallMethodObjArgs object PyObject_Hash object PyObject_HashNotImplemented object PyObject_IsTrue object PyObject_Not object PyObject_Type object PyObject_TypeCheck object PyObject_Length object PyObject_GetItem object PyObject_SetItem object PyObject_DelItem object PyObject_AsFileDescriptor object PyObject_Dir object PyObject_GetIter class PyClass_Check class PyClass_IsSubclass class PyInstance_Check class PyInstance_New class PyInstance_NewRaw code PyCode_Check code PyCode_GetNumFree code *PyCode_New code PyCode_NewEmpty complex _Py_c_sum complex _Py_c_diff complex _Py_c_neg complex _Py_c_prod complex _Py_c_quot complex _Py_c_pow complex PyComplex_Check complex PyComplex_CheckExact complex PyComplex_FromCComplex complex PyComplex_FromDoubles complex PyComplex_RealAsDouble complex PyComplex_ImagAsDouble complex PyComplex_AsCComplex cell PyCell_Check cell PyCell_New cell PyCell_Get cell PyCell_GET cell PyCell_Set cell PyCell_SET gcsupport PyObject_GC_New gcsupport PyObject_GC_NewVar gcsupport PyObject_GC_Resize gcsupport PyObject_GC_Track gcsupport _PyObject_GC_TRACK gcsupport PyObject_GC_Del gcsupport PyObject_GC_UnTrack gcsupport _PyObject_GC_UNTRACK gcsupport Py_VISIT list PyList_Check list PyList_CheckExact list PyList_New list PyList_Size list PyList_GET_SIZE list PyList_GetItem list PyList_GET_ITEM list PyList_SetItem list PyList_SET_ITEM list PyList_Insert list PyList_Append list PyList_GetSlice list PyList_SetSlice list PyList_Sort list PyList_Reverse list PyList_AsTuple codec PyCodec_Register codec PyCodec_KnownEncoding codec PyCodec_Encode codec PyCodec_Decode codec PyCodec_Encoder codec PyCodec_Decoder codec PyCodec_IncrementalEncoder codec PyCodec_IncrementalDecoder codec PyCodec_StreamReader codec PyCodec_StreamWriter codec PyCodec_RegisterError codec PyCodec_LookupError codec PyCodec_StrictErrors codec PyCodec_IgnoreErrors codec PyCodec_ReplaceErrors codec PyCodec_XMLCharRefReplaceErrors codec PyCodec_BackslashReplaceErrors memory PyMem_Malloc memory PyMem_Realloc memory PyMem_Free memory PyMem_New memory PyMem_Resize memory PyMem_Del refcounting Py_INCREF refcounting Py_XINCREF refcounting Py_DECREF refcounting Py_XDECREF refcounting Py_CLEAR datetime PyDate_Check datetime PyDate_CheckExact datetime PyDateTime_Check datetime PyDateTime_CheckExact datetime PyTime_Check datetime PyTime_CheckExact datetime PyDelta_Check datetime PyDelta_CheckExact datetime PyTZInfo_Check datetime PyTZInfo_CheckExact datetime PyDate_FromDate datetime PyDateTime_FromDateAndTime datetime PyTime_FromTime datetime PyDelta_FromDSU datetime PyDateTime_GET_YEAR datetime PyDateTime_GET_MONTH datetime PyDateTime_GET_DAY datetime PyDateTime_DATE_GET_HOUR datetime PyDateTime_DATE_GET_MINUTE datetime PyDateTime_DATE_GET_SECOND datetime PyDateTime_DATE_GET_MICROSECOND datetime PyDateTime_TIME_GET_HOUR datetime PyDateTime_TIME_GET_MINUTE datetime PyDateTime_TIME_GET_SECOND datetime PyDateTime_TIME_GET_MICROSECOND datetime PyDateTime_FromTimestamp datetime PyDate_FromTimestamp weakref PyWeakref_Check weakref PyWeakref_CheckRef weakref PyWeakref_CheckProxy weakref PyWeakref_NewRef weakref PyWeakref_NewProxy weakref PyWeakref_GetObject weakref PyWeakref_GET_OBJECT objbuffer PyObject_AsCharBuffer objbuffer PyObject_AsReadBuffer objbuffer PyObject_CheckReadBuffer objbuffer PyObject_AsWriteBuffer buffer PyObject_CheckBuffer buffer PyObject_GetBuffer buffer PyBuffer_Release buffer PyBuffer_SizeFromFormat buffer PyObject_CopyToObject buffer PyBuffer_IsContiguous buffer PyBuffer_FillContiguousStrides buffer PyBuffer_FillInfo buffer *PyMemoryView_FromObject buffer *PyMemoryView_FromBuffer buffer *PyMemoryView_GetContiguous buffer PyMemoryView_Check buffer *PyMemoryView_GET_BUFFER buffer PyBuffer_Check buffer PyBuffer_FromObject buffer PyBuffer_FromReadWriteObject buffer PyBuffer_FromMemory buffer PyBuffer_FromReadWriteMemory buffer PyBuffer_New arg PyArg_ParseTuple arg PyArg_VaParse arg PyArg_ParseTupleAndKeywords arg PyArg_VaParseTupleAndKeywords arg PyArg_Parse arg PyArg_UnpackTuple arg Py_BuildValue arg Py_VaBuildValue gen PyGen_Check gen PyGen_CheckExact gen PyGen_New descriptor PyDescr_NewGetSet descriptor PyDescr_NewMember descriptor PyDescr_NewMethod descriptor PyDescr_NewWrapper descriptor PyDescr_NewClassMethod descriptor PyDescr_IsData descriptor PyWrapper_New string PyString_Check string PyString_CheckExact string PyString_FromString string PyString_FromStringAndSize string PyString_FromFormat string PyString_FromFormatV string PyString_Size string PyString_GET_SIZE string PyString_AsString string PyString_AS_STRING string PyString_AsStringAndSize string PyString_Concat string PyString_ConcatAndDel string _PyString_Resize string PyString_Format string PyString_InternInPlace string PyString_InternFromString string PyString_Decode string PyString_AsDecodedObject string PyString_Encode string PyString_AsEncodedObject long PyLong_Check long PyLong_CheckExact long PyLong_FromLong long PyLong_FromUnsignedLong long PyLong_FromSsize_t long PyLong_FromSize_t long PyLong_FromSsize_t long PyLong_FromSize_t long PyLong_FromLongLong long PyLong_FromUnsignedLongLong long PyLong_FromDouble long PyLong_FromString long PyLong_FromUnicode long PyLong_FromVoidPtr long PyLong_AsLong long PyLong_AsLongAndOverflow long PyLong_AsLongLongAndOverflow long PyLong_AsSsize_t long PyLong_AsUnsignedLong long PyLong_AsSsize_t long PyLong_AsLongLong long PyLong_AsUnsignedLongLong long PyLong_AsUnsignedLongMask long PyLong_AsUnsignedLongLongMask long PyLong_AsDouble long PyLong_AsVoidPtr float PyFloat_Check float PyFloat_CheckExact float PyFloat_FromString float PyFloat_FromDouble float PyFloat_AsDouble float PyFloat_AS_DOUBLE float PyFloat_GetInfo float PyFloat_GetMax float PyFloat_GetMin float PyFloat_ClearFreeList float PyFloat_AsString float PyFloat_AsReprString conversion PyOS_snprintf conversion PyOS_vsnprintf conversion PyOS_string_to_double conversion PyOS_ascii_strtod conversion PyOS_ascii_formatd conversion PyOS_double_to_string conversion PyOS_ascii_atof conversion PyOS_stricmp conversion PyOS_strnicmp iter PyIter_Check iter PyIter_Next mapping PyMapping_Check mapping PyMapping_Size mapping PyMapping_DelItemString mapping PyMapping_DelItem mapping PyMapping_HasKeyString mapping PyMapping_HasKey mapping PyMapping_Keys mapping PyMapping_Values mapping PyMapping_Items mapping PyMapping_GetItemString mapping PyMapping_SetItemString dict PyDict_Check dict PyDict_CheckExact dict PyDict_New dict PyDictProxy_New dict PyDict_Clear dict PyDict_Contains dict PyDict_Copy dict PyDict_SetItem dict PyDict_SetItemString dict PyDict_DelItem dict PyDict_DelItemString dict PyDict_GetItem dict PyDict_GetItemString dict PyDict_Items dict PyDict_Keys dict PyDict_Values dict PyDict_Size dict PyDict_Next dict PyDict_Merge dict PyDict_Update dict PyDict_MergeFromSeq2 reflection PyEval_GetBuiltins reflection PyEval_GetLocals reflection PyEval_GetGlobals reflection PyEval_GetFrame reflection PyFrame_GetLineNumber reflection PyEval_GetRestricted reflection PyEval_GetFuncName reflection PyEval_GetFuncDesc tuple PyTuple_Check tuple PyTuple_CheckExact tuple PyTuple_New tuple PyTuple_Pack tuple PyTuple_Size tuple PyTuple_GET_SIZE tuple PyTuple_GetItem tuple PyTuple_GET_ITEM tuple PyTuple_GetSlice tuple PyTuple_SetItem tuple PyTuple_SET_ITEM tuple _PyTuple_Resize tuple PyTuple_ClearFreeList exceptions PyErr_PrintEx exceptions PyErr_Print exceptions PyErr_Occurred exceptions PyErr_ExceptionMatches exceptions PyErr_GivenExceptionMatches exceptions PyErr_NormalizeException exceptions PyErr_Clear exceptions PyErr_Fetch exceptions PyErr_Restore exceptions PyErr_SetString exceptions PyErr_SetObject exceptions PyErr_Format exceptions PyErr_SetNone exceptions PyErr_BadArgument exceptions PyErr_NoMemory exceptions PyErr_SetFromErrno exceptions PyErr_SetFromErrnoWithFilename exceptions PyErr_SetFromWindowsErr exceptions PyErr_SetExcFromWindowsErr exceptions PyErr_SetFromWindowsErrWithFilename exceptions PyErr_SetExcFromWindowsErrWithFilename exceptions PyErr_BadInternalCall exceptions PyErr_WarnEx exceptions PyErr_Warn exceptions PyErr_WarnExplicit exceptions PyErr_WarnPy3k exceptions PyErr_CheckSignals exceptions PyErr_SetInterrupt exceptions PySignal_SetWakeupFd exceptions PyErr_NewException exceptions PyErr_NewExceptionWithDoc exceptions PyErr_WriteUnraisable exceptions PyUnicodeDecodeError_Create exceptions PyUnicodeEncodeError_Create exceptions PyUnicodeTranslateError_Create exceptions PyUnicodeDecodeError_GetEncoding exceptions PyUnicodeDecodeError_GetObject exceptions PyUnicodeDecodeError_GetStart exceptions PyUnicodeDecodeError_SetStart exceptions PyUnicodeDecodeError_GetEnd exceptions PyUnicodeDecodeError_SetEnd exceptions PyUnicodeDecodeError_GetReason exceptions PyUnicodeDecodeError_SetReason exceptions Py_EnterRecursiveCall exceptions Py_LeaveRecursiveCall gcc-python-plugin-0.17/libcpychecker_html/capi.py000066400000000000000000000026271342215241600221400ustar00rootroot00000000000000# Copyright 2012 Buck Golemon # # This 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 # . """ Module to help figure out urls for Python C-API functions. """ from os.path import dirname, abspath, join HERE = dirname(abspath(__file__)) # Map functions to their modules. FUNCTIONS = {} def init(): """Initialize this module""" for line in open(join(HERE, 'c-api.txt')): line = line.strip() if line.startswith('#'): continue module, function = line.split() FUNCTIONS[function] = module del module, function def get_url(function): """Get a url for a function""" module = FUNCTIONS.get(function) if module: return "http://docs.python.org/c-api/%s.html#%s" % (module, function) else: return None init() # This is done once, upon import gcc-python-plugin-0.17/libcpychecker_html/extlib/000077500000000000000000000000001342215241600221325ustar00rootroot00000000000000gcc-python-plugin-0.17/libcpychecker_html/extlib/reset-20110126.min.css000066400000000000000000000015341342215241600254450ustar00rootroot00000000000000/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) */ a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}table{border-collapse:collapse;border-spacing:0} gcc-python-plugin-0.17/libcpychecker_html/extlib/zepto-1.1.3.min.js000066400000000000000000000600731342215241600250570ustar00rootroot00000000000000/* Zepto v1.1.3 - zepto event ajax form ie - zeptojs.com/license */ var Zepto=function(){function L(t){return null==t?String(t):j[T.call(t)]||"object"}function Z(t){return"function"==L(t)}function $(t){return null!=t&&t==t.window}function _(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function D(t){return"object"==L(t)}function R(t){return D(t)&&!$(t)&&Object.getPrototypeOf(t)==Object.prototype}function M(t){return"number"==typeof t.length}function k(t){return s.call(t,function(t){return null!=t})}function z(t){return t.length>0?n.fn.concat.apply([],t):t}function F(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function q(t){return t in f?f[t]:f[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function H(t,e){return"number"!=typeof e||c[F(t)]?e:e+"px"}function I(t){var e,n;return u[t]||(e=a.createElement(t),a.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),u[t]=n),u[t]}function V(t){return"children"in t?o.call(t.children):n.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function U(n,i,r){for(e in i)r&&(R(i[e])||A(i[e]))?(R(i[e])&&!R(n[e])&&(n[e]={}),A(i[e])&&!A(n[e])&&(n[e]=[]),U(n[e],i[e],r)):i[e]!==t&&(n[e]=i[e])}function B(t,e){return null==e?n(t):n(t).filter(e)}function J(t,e,n,i){return Z(e)?e.call(t,n,i):e}function X(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function W(e,n){var i=e.className,r=i&&i.baseVal!==t;return n===t?r?i.baseVal:i:void(r?i.baseVal=n:e.className=n)}function Y(t){var e;try{return t?"true"==t||("false"==t?!1:"null"==t?null:/^0/.test(t)||isNaN(e=Number(t))?/^[\[\{]/.test(t)?n.parseJSON(t):t:e):t}catch(i){return t}}function G(t,e){e(t);for(var n in t.childNodes)G(t.childNodes[n],e)}var t,e,n,i,C,N,r=[],o=r.slice,s=r.filter,a=window.document,u={},f={},c={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,h=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,p=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,d=/^(?:body|html)$/i,m=/([A-Z])/g,g=["val","css","html","text","data","width","height","offset"],v=["after","prepend","before","append"],y=a.createElement("table"),x=a.createElement("tr"),b={tr:a.createElement("tbody"),tbody:y,thead:y,tfoot:y,td:x,th:x,"*":a.createElement("div")},w=/complete|loaded|interactive/,E=/^[\w-]*$/,j={},T=j.toString,S={},O=a.createElement("div"),P={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},A=Array.isArray||function(t){return t instanceof Array};return S.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var i,r=t.parentNode,o=!r;return o&&(r=O).appendChild(t),i=~S.qsa(r,e).indexOf(t),o&&O.removeChild(t),i},C=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},N=function(t){return s.call(t,function(e,n){return t.indexOf(e)==n})},S.fragment=function(e,i,r){var s,u,f;return h.test(e)&&(s=n(a.createElement(RegExp.$1))),s||(e.replace&&(e=e.replace(p,"<$1>")),i===t&&(i=l.test(e)&&RegExp.$1),i in b||(i="*"),f=b[i],f.innerHTML=""+e,s=n.each(o.call(f.childNodes),function(){f.removeChild(this)})),R(r)&&(u=n(s),n.each(r,function(t,e){g.indexOf(t)>-1?u[t](e):u.attr(t,e)})),s},S.Z=function(t,e){return t=t||[],t.__proto__=n.fn,t.selector=e||"",t},S.isZ=function(t){return t instanceof S.Z},S.init=function(e,i){var r;if(!e)return S.Z();if("string"==typeof e)if(e=e.trim(),"<"==e[0]&&l.test(e))r=S.fragment(e,RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=S.qsa(a,e)}else{if(Z(e))return n(a).ready(e);if(S.isZ(e))return e;if(A(e))r=k(e);else if(D(e))r=[e],e=null;else if(l.test(e))r=S.fragment(e.trim(),RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=S.qsa(a,e)}}return S.Z(r,e)},n=function(t,e){return S.init(t,e)},n.extend=function(t){var e,n=o.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){U(t,n,e)}),t},S.qsa=function(t,e){var n,i="#"==e[0],r=!i&&"."==e[0],s=i||r?e.slice(1):e,a=E.test(s);return _(t)&&a&&i?(n=t.getElementById(s))?[n]:[]:1!==t.nodeType&&9!==t.nodeType?[]:o.call(a&&!i?r?t.getElementsByClassName(s):t.getElementsByTagName(e):t.querySelectorAll(e))},n.contains=function(t,e){return t!==e&&t.contains(e)},n.type=L,n.isFunction=Z,n.isWindow=$,n.isArray=A,n.isPlainObject=R,n.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},n.inArray=function(t,e,n){return r.indexOf.call(e,t,n)},n.camelCase=C,n.trim=function(t){return null==t?"":String.prototype.trim.call(t)},n.uuid=0,n.support={},n.expr={},n.map=function(t,e){var n,r,o,i=[];if(M(t))for(r=0;r=0?e:e+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return r.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return Z(t)?this.not(this.not(t)):n(s.call(this,function(e){return S.matches(e,t)}))},add:function(t,e){return n(N(this.concat(n(t,e))))},is:function(t){return this.length>0&&S.matches(this[0],t)},not:function(e){var i=[];if(Z(e)&&e.call!==t)this.each(function(t){e.call(this,t)||i.push(this)});else{var r="string"==typeof e?this.filter(e):M(e)&&Z(e.item)?o.call(e):n(e);this.forEach(function(t){r.indexOf(t)<0&&i.push(t)})}return n(i)},has:function(t){return this.filter(function(){return D(t)?n.contains(this,t):n(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!D(t)?t:n(t)},last:function(){var t=this[this.length-1];return t&&!D(t)?t:n(t)},find:function(t){var e,i=this;return e="object"==typeof t?n(t).filter(function(){var t=this;return r.some.call(i,function(e){return n.contains(e,t)})}):1==this.length?n(S.qsa(this[0],t)):this.map(function(){return S.qsa(this,t)})},closest:function(t,e){var i=this[0],r=!1;for("object"==typeof t&&(r=n(t));i&&!(r?r.indexOf(i)>=0:S.matches(i,t));)i=i!==e&&!_(i)&&i.parentNode;return n(i)},parents:function(t){for(var e=[],i=this;i.length>0;)i=n.map(i,function(t){return(t=t.parentNode)&&!_(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return B(e,t)},parent:function(t){return B(N(this.pluck("parentNode")),t)},children:function(t){return B(this.map(function(){return V(this)}),t)},contents:function(){return this.map(function(){return o.call(this.childNodes)})},siblings:function(t){return B(this.map(function(t,e){return s.call(V(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return n.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=I(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=Z(t);if(this[0]&&!e)var i=n(t).get(0),r=i.parentNode||this.length>1;return this.each(function(o){n(this).wrapAll(e?t.call(this,o):r?i.cloneNode(!0):i)})},wrapAll:function(t){if(this[0]){n(this[0]).before(t=n(t));for(var e;(e=t.children()).length;)t=e.first();n(t).append(this)}return this},wrapInner:function(t){var e=Z(t);return this.each(function(i){var r=n(this),o=r.contents(),s=e?t.call(this,i):t;o.length?o.wrapAll(s):r.append(s)})},unwrap:function(){return this.parent().each(function(){n(this).replaceWith(n(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(e){return this.each(function(){var i=n(this);(e===t?"none"==i.css("display"):e)?i.show():i.hide()})},prev:function(t){return n(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return n(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0===arguments.length?this.length>0?this[0].innerHTML:null:this.each(function(e){var i=this.innerHTML;n(this).empty().append(J(this,t,e,i))})},text:function(e){return 0===arguments.length?this.length>0?this[0].textContent:null:this.each(function(){this.textContent=e===t?"":""+e})},attr:function(n,i){var r;return"string"==typeof n&&i===t?0==this.length||1!==this[0].nodeType?t:"value"==n&&"INPUT"==this[0].nodeName?this.val():!(r=this[0].getAttribute(n))&&n in this[0]?this[0][n]:r:this.each(function(t){if(1===this.nodeType)if(D(n))for(e in n)X(this,e,n[e]);else X(this,n,J(this,i,t,this.getAttribute(n)))})},removeAttr:function(t){return this.each(function(){1===this.nodeType&&X(this,t)})},prop:function(e,n){return e=P[e]||e,n===t?this[0]&&this[0][e]:this.each(function(t){this[e]=J(this,n,t,this[e])})},data:function(e,n){var i=this.attr("data-"+e.replace(m,"-$1").toLowerCase(),n);return null!==i?Y(i):t},val:function(t){return 0===arguments.length?this[0]&&(this[0].multiple?n(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value):this.each(function(e){this.value=J(this,t,e,this.value)})},offset:function(t){if(t)return this.each(function(e){var i=n(this),r=J(this,t,e,i.offset()),o=i.offsetParent().offset(),s={top:r.top-o.top,left:r.left-o.left};"static"==i.css("position")&&(s.position="relative"),i.css(s)});if(0==this.length)return null;var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(t,i){if(arguments.length<2){var r=this[0],o=getComputedStyle(r,"");if(!r)return;if("string"==typeof t)return r.style[C(t)]||o.getPropertyValue(t);if(A(t)){var s={};return n.each(A(t)?t:[t],function(t,e){s[e]=r.style[C(e)]||o.getPropertyValue(e)}),s}}var a="";if("string"==L(t))i||0===i?a=F(t)+":"+H(t,i):this.each(function(){this.style.removeProperty(F(t))});else for(e in t)t[e]||0===t[e]?a+=F(e)+":"+H(e,t[e])+";":this.each(function(){this.style.removeProperty(F(e))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf(n(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?r.some.call(this,function(t){return this.test(W(t))},q(t)):!1},addClass:function(t){return t?this.each(function(e){i=[];var r=W(this),o=J(this,t,e,r);o.split(/\s+/g).forEach(function(t){n(this).hasClass(t)||i.push(t)},this),i.length&&W(this,r+(r?" ":"")+i.join(" "))}):this},removeClass:function(e){return this.each(function(n){return e===t?W(this,""):(i=W(this),J(this,e,n,i).split(/\s+/g).forEach(function(t){i=i.replace(q(t)," ")}),void W(this,i.trim()))})},toggleClass:function(e,i){return e?this.each(function(r){var o=n(this),s=J(this,e,r,W(this));s.split(/\s+/g).forEach(function(e){(i===t?!o.hasClass(e):i)?o.addClass(e):o.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var n="scrollTop"in this[0];return e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var n="scrollLeft"in this[0];return e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),i=this.offset(),r=d.test(e[0].nodeName)?{top:0,left:0}:e.offset();return i.top-=parseFloat(n(t).css("margin-top"))||0,i.left-=parseFloat(n(t).css("margin-left"))||0,r.top+=parseFloat(n(e[0]).css("border-top-width"))||0,r.left+=parseFloat(n(e[0]).css("border-left-width"))||0,{top:i.top-r.top,left:i.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||a.body;t&&!d.test(t.nodeName)&&"static"==n(t).css("position");)t=t.offsetParent;return t})}},n.fn.detach=n.fn.remove,["width","height"].forEach(function(e){var i=e.replace(/./,function(t){return t[0].toUpperCase()});n.fn[e]=function(r){var o,s=this[0];return r===t?$(s)?s["inner"+i]:_(s)?s.documentElement["scroll"+i]:(o=this.offset())&&o[e]:this.each(function(t){s=n(this),s.css(e,J(this,r,t,s[e]()))})}}),v.forEach(function(t,e){var i=e%2;n.fn[t]=function(){var t,o,r=n.map(arguments,function(e){return t=L(e),"object"==t||"array"==t||null==e?e:S.fragment(e)}),s=this.length>1;return r.length<1?this:this.each(function(t,a){o=i?a:a.parentNode,a=0==e?a.nextSibling:1==e?a.firstChild:2==e?a:null,r.forEach(function(t){if(s)t=t.cloneNode(!0);else if(!o)return n(t).remove();G(o.insertBefore(t,a),function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},n.fn[i?t+"To":"insert"+(e?"Before":"After")]=function(e){return n(e)[t](this),this}}),S.Z.prototype=n.fn,S.uniq=N,S.deserializeValue=Y,n.zepto=S,n}();window.Zepto=Zepto,void 0===window.$&&(window.$=Zepto),function(t){function l(t){return t._zid||(t._zid=e++)}function h(t,e,n,i){if(e=p(e),e.ns)var r=d(e.ns);return(s[l(t)]||[]).filter(function(t){return!(!t||e.e&&t.e!=e.e||e.ns&&!r.test(t.ns)||n&&l(t.fn)!==l(n)||i&&t.sel!=i)})}function p(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function d(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function m(t,e){return t.del&&!u&&t.e in f||!!e}function g(t){return c[t]||u&&f[t]||t}function v(e,i,r,o,a,u,f){var h=l(e),d=s[h]||(s[h]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return t(document).ready(r);var s=p(i);s.fn=r,s.sel=a,s.e in c&&(r=function(e){var n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?s.fn.apply(this,arguments):void 0}),s.del=u;var l=u||r;s.proxy=function(t){if(t=j(t),!t.isImmediatePropagationStopped()){t.data=o;var i=l.apply(e,t._args==n?[t]:[t].concat(t._args));return i===!1&&(t.preventDefault(),t.stopPropagation()),i}},s.i=d.length,d.push(s),"addEventListener"in e&&e.addEventListener(g(s.e),s.proxy,m(s,f))})}function y(t,e,n,i,r){var o=l(t);(e||"").split(/\s/).forEach(function(e){h(t,e,n,i).forEach(function(e){delete s[o][e.i],"removeEventListener"in t&&t.removeEventListener(g(e.e),e.proxy,m(e,r))})})}function j(e,i){return(i||!e.isDefaultPrevented)&&(i||(i=e),t.each(E,function(t,n){var r=i[t];e[t]=function(){return this[n]=x,r&&r.apply(i,arguments)},e[n]=b}),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?i.returnValue===!1:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=x)),e}function T(t){var e,i={originalEvent:t};for(e in t)w.test(e)||t[e]===n||(i[e]=t[e]);return j(i,t)}var n,e=1,i=Array.prototype.slice,r=t.isFunction,o=function(t){return"string"==typeof t},s={},a={},u="onfocusin"in window,f={focus:"focusin",blur:"focusout"},c={mouseenter:"mouseover",mouseleave:"mouseout"};a.click=a.mousedown=a.mouseup=a.mousemove="MouseEvents",t.event={add:v,remove:y},t.proxy=function(e,n){if(r(e)){var i=function(){return e.apply(n,arguments)};return i._zid=l(e),i}if(o(n))return t.proxy(e[n],e);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var x=function(){return!0},b=function(){return!1},w=/^([A-Z]|returnValue$|layer[XY]$)/,E={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,s,a,u,f){var c,l,h=this;return e&&!o(e)?(t.each(e,function(t,e){h.on(t,s,a,e,f)}),h):(o(s)||r(u)||u===!1||(u=a,a=s,s=n),(r(a)||a===!1)&&(u=a,a=n),u===!1&&(u=b),h.each(function(n,r){f&&(c=function(t){return y(r,t.type,u),u.apply(this,arguments)}),s&&(l=function(e){var n,o=t(e.target).closest(s,r).get(0);return o&&o!==r?(n=t.extend(T(e),{currentTarget:o,liveFired:r}),(c||u).apply(o,[n].concat(i.call(arguments,1)))):void 0}),v(r,e,u,a,s,l||c)}))},t.fn.off=function(e,i,s){var a=this;return e&&!o(e)?(t.each(e,function(t,e){a.off(t,i,e)}),a):(o(i)||r(s)||s===!1||(s=i,i=n),s===!1&&(s=b),a.each(function(){y(this,e,s,i)}))},t.fn.trigger=function(e,n){return e=o(e)||t.isPlainObject(e)?t.Event(e):j(e),e._args=n,this.each(function(){"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var i,r;return this.each(function(s,a){i=T(o(e)?t.Event(e):e),i._args=n,i.target=a,t.each(h(a,e.type||e),function(t,e){return r=e.proxy(i),i.isImmediatePropagationStopped()?!1:void 0})}),r},"focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return t?this.bind(e,t):this.trigger(e)}}),["focus","blur"].forEach(function(e){t.fn[e]=function(t){return t?this.bind(e,t):this.each(function(){try{this[e]()}catch(t){}}),this}}),t.Event=function(t,e){o(t)||(e=t,t=e.type);var n=document.createEvent(a[t]||"Events"),i=!0;if(e)for(var r in e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return n.initEvent(t,i,!0),j(n)}}(Zepto),function(t){function l(e,n,i){var r=t.Event(n);return t(e).trigger(r,i),!r.isDefaultPrevented()}function h(t,e,i,r){return t.global?l(e||n,i,r):void 0}function p(e){e.global&&0===t.active++&&h(e,null,"ajaxStart")}function d(e){e.global&&!--t.active&&h(e,null,"ajaxStop")}function m(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||h(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void h(e,n,"ajaxSend",[t,e])}function g(t,e,n,i){var r=n.context,o="success";n.success.call(r,t,o,e),i&&i.resolveWith(r,[t,o,e]),h(n,r,"ajaxSuccess",[e,n,t]),y(o,e,n)}function v(t,e,n,i,r){var o=i.context;i.error.call(o,n,e,t),r&&r.rejectWith(o,[n,e,t]),h(i,o,"ajaxError",[n,i,t||e]),y(e,n,i)}function y(t,e,n){var i=n.context;n.complete.call(i,e,t),h(n,i,"ajaxComplete",[e,n]),d(n)}function x(){}function b(t){return t&&(t=t.split(";",2)[0]),t&&(t==f?"html":t==u?"json":s.test(t)?"script":a.test(t)&&"xml")||"text"}function w(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function E(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=w(e.url,e.data),e.data=void 0)}function j(e,n,i,r){return t.isFunction(n)&&(r=i,i=n,n=void 0),t.isFunction(i)||(r=i,i=void 0),{url:e,data:n,success:i,dataType:r}}function S(e,n,i,r){var o,s=t.isArray(n),a=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),r&&(n=i?r:r+"["+(a||"object"==o||"array"==o?n:"")+"]"),!r&&s?e.add(u.name,u.value):"array"==o||!i&&"object"==o?S(e,u,i,n):e.add(n,u)})}var i,r,e=0,n=window.document,o=/)<[^<]*)*<\/script>/gi,s=/^(?:text|application)\/javascript/i,a=/^(?:text|application)\/xml/i,u="application/json",f="text/html",c=/^\s*$/;t.active=0,t.ajaxJSONP=function(i,r){if(!("type"in i))return t.ajax(i);var f,h,o=i.jsonpCallback,s=(t.isFunction(o)?o():o)||"jsonp"+ ++e,a=n.createElement("script"),u=window[s],c=function(e){t(a).triggerHandler("error",e||"abort")},l={abort:c};return r&&r.promise(l),t(a).on("load error",function(e,n){clearTimeout(h),t(a).off().remove(),"error"!=e.type&&f?g(f[0],l,i,r):v(null,n||"error",l,i,r),window[s]=u,f&&t.isFunction(u)&&u(f[0]),u=f=void 0}),m(l,i)===!1?(c("abort"),l):(window[s]=function(){f=arguments},a.src=i.url.replace(/\?(.+)=\?/,"?$1="+s),n.head.appendChild(a),i.timeout>0&&(h=setTimeout(function(){c("timeout")},i.timeout)),l)},t.ajaxSettings={type:"GET",beforeSend:x,success:x,error:x,complete:x,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:u,xml:"application/xml, text/xml",html:f,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0},t.ajax=function(e){var n=t.extend({},e||{}),o=t.Deferred&&t.Deferred();for(i in t.ajaxSettings)void 0===n[i]&&(n[i]=t.ajaxSettings[i]);p(n),n.crossDomain||(n.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(n.url)&&RegExp.$2!=window.location.host),n.url||(n.url=window.location.toString()),E(n),n.cache===!1&&(n.url=w(n.url,"_="+Date.now()));var s=n.dataType,a=/\?.+=\?/.test(n.url);if("jsonp"==s||a)return a||(n.url=w(n.url,n.jsonp?n.jsonp+"=?":n.jsonp===!1?"":"callback=?")),t.ajaxJSONP(n,o);var j,u=n.accepts[s],f={},l=function(t,e){f[t.toLowerCase()]=[t,e]},h=/^([\w-]+:)\/\//.test(n.url)?RegExp.$1:window.location.protocol,d=n.xhr(),y=d.setRequestHeader;if(o&&o.promise(d),n.crossDomain||l("X-Requested-With","XMLHttpRequest"),l("Accept",u||"*/*"),(u=n.mimeType||u)&&(u.indexOf(",")>-1&&(u=u.split(",",2)[0]),d.overrideMimeType&&d.overrideMimeType(u)),(n.contentType||n.contentType!==!1&&n.data&&"GET"!=n.type.toUpperCase())&&l("Content-Type",n.contentType||"application/x-www-form-urlencoded"),n.headers)for(r in n.headers)l(r,n.headers[r]);if(d.setRequestHeader=l,d.onreadystatechange=function(){if(4==d.readyState){d.onreadystatechange=x,clearTimeout(j);var e,i=!1;if(d.status>=200&&d.status<300||304==d.status||0==d.status&&"file:"==h){s=s||b(n.mimeType||d.getResponseHeader("content-type")),e=d.responseText;try{"script"==s?(1,eval)(e):"xml"==s?e=d.responseXML:"json"==s&&(e=c.test(e)?null:t.parseJSON(e))}catch(r){i=r}i?v(i,"parsererror",d,n,o):g(e,d,n,o)}else v(d.statusText||null,d.status?"error":"abort",d,n,o)}},m(d,n)===!1)return d.abort(),v(null,"abort",d,n,o),d;if(n.xhrFields)for(r in n.xhrFields)d[r]=n.xhrFields[r];var T="async"in n?n.async:!0;d.open(n.type,n.url,T,n.username,n.password);for(r in f)y.apply(d,f[r]);return n.timeout>0&&(j=setTimeout(function(){d.onreadystatechange=x,d.abort(),v(null,"timeout",d,n,o)},n.timeout)),d.send(n.data?n.data:null),d},t.get=function(){return t.ajax(j.apply(null,arguments))},t.post=function(){var e=j.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=j.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,i){if(!this.length)return this;var a,r=this,s=e.split(/\s/),u=j(e,n,i),f=u.success;return s.length>1&&(u.url=s[0],a=s[1]),u.success=function(e){r.html(a?t("