diff -Nru abi-compliance-checker-1.99.14/INSTALL abi-compliance-checker-2.3/INSTALL --- abi-compliance-checker-1.99.14/INSTALL 2015-11-01 18:20:50.000000000 +0000 +++ abi-compliance-checker-2.3/INSTALL 2018-05-15 05:26:59.000000000 +0000 @@ -1,16 +1,15 @@ Copyright (C) 2009-2011 Institute for System Programming, RAS Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) -Copyright (C) 2011-2012 ROSA Laboratory -Copyright (C) 2012-2015 Andrey Ponomarenko's ABI Laboratory +Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory All rights reserved. RELEASE INFORMATION -Project: ABI Compliance Checker (ACC) -Version: 1.99.14 -Date: 2015-11-01 +Project: ABI Compliance Checker (ABICC) +Version: 2.3 +Date: May 15, 2018 This file explains how to install and setup environment @@ -22,18 +21,19 @@ 1. Requirements for Linux and FreeBSD 2. Requirements for Mac OS X 3. Requirements for MS Windows - 4. Configuring and Installing - 5. Running the Tool - 6. Usage with ABI Dumper + 4. Configure and Install + 5. Usage (with ABI Dumper) + 6. Usage (Original) 1. REQUIREMENTS FOR LINUX AND FREEBSD ===================================== - 1. G++ (3.0-4.7, 4.8.3, 4.9 or newer) + 1. G++ (3.0 or newer) 2. GNU Binutils (c++filt, readelf, objdump) - 3. Perl 5 (5.8 or newer) - 4. Ctags (5.8 or newer) + 3. Perl 5 + 4. Ctags + 5. ABI Dumper (1.1 or newer) @@ -41,29 +41,43 @@ ============================ 1. Xcode (g++, c++filt, otool, nm) - 2. Ctags (5.8 or newer) + 2. Perl 5 + 3. Ctags + +2.1 Setup environment + + 1. If /usr/bin/g++ points to clang, then please + specify GCC path by the -gcc-path option + + 2. You can install GCC by the command: + + brew install homebrew/versions/gcc49 + + And then specify its path: + + abi-compliance-checker --gcc-path=/usr/local/bin/gcc-4.9 ... 3. REQUIREMENTS FOR MS WINDOWS ============================== - 1. MinGW (3.0-4.7, 4.8.3, 4.9 or newer) + 1. MinGW (3.0 or newer) 2. MS Visual C++ (dumpbin, undname, cl) 3. Active Perl 5 (5.8 or newer) 4. Sigcheck v1.71 or newer 5. Info-ZIP 3.0 (zip, unzip) - 6. Ctags (5.8 or newer) + 6. Ctags 3.1 Setup environment 1. Add tool locations to the PATH environment variable - 2. Run vsvars32.bat script (C:\Microsoft Visual Studio 9.0\Common7\Tools\) + 2. Run vcvars64.bat script (C:\Microsoft Visual Studio 9.0\VC\bin\) -4. CONFIGURING AND INSTALLING -============================= +4. CONFIGURE AND INSTALL +======================== This command will install the abi-compliance-checker program into the PREFIX/bin system directory and private modules into the PREFIX/share: @@ -76,48 +90,54 @@ -5. RUNNING THE TOOL -=================== +5. USAGE (WITH ABI DUMPER) +========================== - 1. Create XML-descriptors for two versions - of a library (OLD.xml and NEW.xml): + Library should be compiled with -g -Og + options to contain DWARF debug info. - - 1.0 - + Create ABI dumps for both library versions + using the ABI Dumper tool (https://github.com/lvc/abi-dumper): - - /path1/to/header(s)/ - /path2/to/header(s)/ - ... - + abi-dumper OLD.so -o ABI-0.dump -lver 0 + abi-dumper NEW.so -o ABI-1.dump -lver 1 - - /path1/to/library(ies)/ - /path2/to/library(ies)/ - ... - + You can filter public ABI symbols with the help of + additional -public-headers option of the ABI Dumper tool. + + Compare ABI dumps to create report: - 2. abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml - 3. For advanced usage, see doc/Readme.html or --help option + abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump -6. USAGE WITH ABI DUMPER -======================== +6. USAGE (ORIGINAL) +=================== - 1. Library should be compiled with -g - option to contain DWARF debug info - - 2. Create ABI dumps for both library versions - using the ABI Dumper tool (https://github.com/lvc/abi-dumper): - - abi-dumper OLD.so -o ABI-0.dump -lver 0 - abi-dumper NEW.so -o ABI-1.dump -lver 1 - - 3. Compare ABI dumps: + Create XML-descriptors for two versions + of a library (OLD.xml and NEW.xml): + + + 1.0 + + + + /path1/to/header(s)/ + /path2/to/header(s)/ + ... + + + + /path1/to/library(ies)/ + /path2/to/library(ies)/ + ... + + + Check compatibility: + + abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml - abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump + For advanced usage, see doc/index.html or -help option. Enjoy! diff -Nru abi-compliance-checker-1.99.14/LICENSE abi-compliance-checker-2.3/LICENSE --- abi-compliance-checker-1.99.14/LICENSE 2015-11-01 18:20:50.000000000 +0000 +++ abi-compliance-checker-2.3/LICENSE 2018-05-15 05:26:59.000000000 +0000 @@ -1,460 +1,125 @@ -This program is free software. You may use, redistribute and/or modify it -under the terms of either the GNU General Public License (GPL) or the GNU -Lesser General Public License (LGPL). + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 ---------------------------------------------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) 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 -this service 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 make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. 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. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -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 -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the 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 a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE 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. - - 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 -convey 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 2 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, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision 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, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This 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. - ---------------------------------------------------------------------------- - - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1991 Free Software Foundation, Inc. + Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. -[This is the first released version of the library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. - This license, the Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, 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 -this service 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. + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the library, or if you modify it. +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source -code. If you link a program with the library, you must provide -complete object files to the recipients so that they can relink them -with the library, after making changes to the library and recompiling +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, so that any problems introduced by others will not reflect on -the original authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The -former contains code derived from the library, while the latter only -works together with the library. +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. - - GNU LIBRARY GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -General Public License (also called "this License"). Each licensee is -addressed as "you". + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs @@ -481,7 +146,7 @@ on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - + 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an @@ -603,7 +268,7 @@ Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. - 6. As an exception to the Sections above, you may also compile or + 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit @@ -630,23 +295,31 @@ Library will not necessarily be able to recompile the application to use the modified definitions.) - b) Accompany the work with a written offer, valid for at + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. - c) If distribution of the work is made by offering access to copy + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. - d) Verify that the user has already received a copy of these + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, -the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. @@ -695,7 +368,7 @@ original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to +You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent @@ -738,7 +411,7 @@ written in the body of this License. 13. The Free Software Foundation may publish revised and/or new -versions of the Library General Public License from time to time. +versions of the Lesser 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. @@ -759,7 +432,7 @@ of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. @@ -782,7 +455,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries @@ -801,18 +474,19 @@ Copyright (C) This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public + modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + version 2.1 of the License, or (at your option) any later version. This library 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 - Library General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA Also add information on how to contact you by electronic and paper mail. @@ -821,7 +495,8 @@ necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. , 1 April 1990 Ty Coon, President of Vice diff -Nru abi-compliance-checker-1.99.14/Makefile abi-compliance-checker-2.3/Makefile --- abi-compliance-checker-1.99.14/Makefile 2015-11-01 18:20:50.000000000 +0000 +++ abi-compliance-checker-2.3/Makefile 2018-05-15 05:26:59.000000000 +0000 @@ -1,8 +1,15 @@ prefix ?= /usr -.PHONY: install +.PHONY: all + +all: + echo "Nothing to build." + install: perl Makefile.pl -install -prefix "$(prefix)" uninstall: perl Makefile.pl -remove -prefix "$(prefix)" + +clean: + echo "Nothing to clean up." diff -Nru abi-compliance-checker-1.99.14/Makefile.pl abi-compliance-checker-2.3/Makefile.pl --- abi-compliance-checker-1.99.14/Makefile.pl 2015-11-01 18:20:50.000000000 +0000 +++ abi-compliance-checker-2.3/Makefile.pl 2018-05-15 05:26:59.000000000 +0000 @@ -1,28 +1,30 @@ #!/usr/bin/perl ########################################################################### -# Makefile for ABI Compliance Checker +# A makefile to install the tool # Install/remove the tool for GNU/Linux, FreeBSD and Mac OS X # # Copyright (C) 2009-2011 Institute for System Programming, RAS # Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) -# Copyright (C) 2011-2012 ROSA Laboratory -# Copyright (C) 2012-2015 Andrey Ponomarenko's ABI Laboratory +# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory # # Written by Andrey Ponomarenko # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License or the GNU Lesser -# General Public License as published by the Free Software Foundation. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, +# This library 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. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# and the GNU Lesser General Public License along with this program. -# If not, see . +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA ########################################################################### +use strict; use Getopt::Long; Getopt::Long::Configure ("posix_default", "no_ignore_case"); use File::Path qw(mkpath rmtree); @@ -32,7 +34,6 @@ use Cwd qw(abs_path); use File::Find; use Config; -use strict; my $TOOL_SNAME = "abi-compliance-checker"; my $ARCHIVE_DIR = abs_path(dirname($0)); @@ -273,18 +274,19 @@ sub readFile($) { my $Path = $_[0]; - return "" if(not $Path or not -f $Path); + open(FILE, $Path) || die ("can't open file \'$Path\': $!\n"); local $/ = undef; my $Content = ; close(FILE); + return $Content; } sub writeFile($$) { my ($Path, $Content) = @_; - return if(not $Path); + open(FILE, ">".$Path) || die ("can't open file \'$Path\': $!\n"); print FILE $Content; close(FILE); diff -Nru abi-compliance-checker-1.99.14/README abi-compliance-checker-2.3/README --- abi-compliance-checker-1.99.14/README 2015-11-01 18:20:50.000000000 +0000 +++ abi-compliance-checker-2.3/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -NAME: - ABI Compliance Checker (ACC) - a tool for checking backward binary and source-level compatibility of a C/C++ software library. - - The tool checks header files and shared objects of old and new versions and analyzes changes in API/ABI that may break binary and/or source compatibility: changes in calling stack, v-table changes, removed symbols, renamed fields, etc. - - The tool is intended for developers of software libraries and Linux maintainers who are interested in ensuring backward compatibility, i.e. allow old applications to run or to be recompiled with newer library versions. - - The tool is developed by Andrey Ponomarenko. You can order additional reports for visualization of the ABI structure and high detailed binary compatibility analysis here: http://abi-laboratory.pro/ - -INSTALL: - sudo make install prefix=/usr - -REQUIRES: - Perl 5 - G++ - GNU Binutils - Ctags - -USAGE: - abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml - - OLD.xml and NEW.xml are XML-descriptors: - - - 1.0 - - - - /path/to/headers/ - - - - /path/to/libraries/ - - -ADV. USAGE: - For advanced usage, see doc/Readme.html or output of --help option. - -COMPATIBILITY: - ABI Dumper >= 0.98 (https://github.com/lvc/abi-dumper) - -USAGE WITH ABI DUMPER: - - 1. Library should be compiled with "-g -Og" GCC options - to contain DWARF debug info - - 2. Create ABI dumps for both library versions - using the ABI Dumper (https://github.com/lvc/abi-dumper) tool: - - abi-dumper OLD.so -o ABI-0.dump -lver 0 - abi-dumper NEW.so -o ABI-1.dump -lver 1 - - 3. Compare ABI dumps: - - abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump diff -Nru abi-compliance-checker-1.99.14/README.md abi-compliance-checker-2.3/README.md --- abi-compliance-checker-1.99.14/README.md 1970-01-01 00:00:00.000000000 +0000 +++ abi-compliance-checker-2.3/README.md 2018-05-15 05:26:59.000000000 +0000 @@ -0,0 +1,93 @@ +ABICC 2.3 +========= + +ABI Compliance Checker (ABICC) — a tool for checking backward binary and source-level compatibility of a C/C++ software library. + +Contents +-------- + +1. [ About ](#about) +2. [ Install ](#install) +3. [ Usage ](#usage) +4. [ Test suite ](#test-suite) + +About +----- + +The tool analyzes changes in API/ABI (ABI=API+compiler ABI) that may break binary compatibility and/or source compatibility: changes in calling stack, v-table changes, removed symbols, renamed fields, etc. + +The tool can create and compare ABI dumps for header files and shared objects of a library. The ABI dump for a library can also be created by the ABI Dumper tool (https://github.com/lvc/abi-dumper) if shared objects include debug-info. + +The tool is intended for developers of software libraries and Linux maintainers who are interested in ensuring backward compatibility, i.e. allow old applications to run or to be recompiled with newer library versions. + +The tool is a core of the ABI Tracker and Upstream Tracker projects: https://abi-laboratory.pro/tracker/ + +The tool is developed by Andrey Ponomarenko. + +Install +------- + + sudo make install prefix=/usr + +###### Requires + +* Perl 5 +* GCC C++ (3.0 or newer) +* GNU Binutils +* Ctags +* ABI Dumper (1.1 or newer) + +###### Platforms + +* Linux +* Mac OS X +* Windows + +Usage +----- + +###### With ABI Dumper + +1. Library should be compiled with `-g -Og` GCC options to contain DWARF debug info + +2. Create ABI dumps for both library versions using the ABI Dumper (https://github.com/lvc/abi-dumper) tool: + + abi-dumper OLD.so -o ABI-0.dump -lver 0 + abi-dumper NEW.so -o ABI-1.dump -lver 1 + +3. You can filter public ABI with the help of additional `-public-headers` option of the ABI Dumper tool + +4. Compare ABI dumps to create report: + + abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump + +###### Compile headers + + abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml + +`OLD.xml` and `NEW.xml` are XML-descriptors: + + + 1.0 + + + + /path/to/headers/ + + + + /path/to/libraries/ + + +###### Adv. usage + +For advanced usage, see `doc/index.html` or output of `-help` option. + +Test suite +---------- + +The tool is tested properly in the ABI Tracker and Upstream Tracker projects, by the community and by the internal test suite: + + abi-compliance-checker -test + +There are about 100 test cases for C and 200 test cases for C++ API/ABI breaks. diff -Nru abi-compliance-checker-1.99.14/abi-compliance-checker.pl abi-compliance-checker-2.3/abi-compliance-checker.pl --- abi-compliance-checker-1.99.14/abi-compliance-checker.pl 2015-11-01 18:20:50.000000000 +0000 +++ abi-compliance-checker-2.3/abi-compliance-checker.pl 2018-05-15 05:26:59.000000000 +0000 @@ -1,176 +1,112 @@ #!/usr/bin/perl -########################################################################### -# ABI Compliance Checker (ABICC) 1.99.14 +######################################################################## +# ABI Compliance Checker (ABICC) 2.3 # A tool for checking backward compatibility of a C/C++ library API # # Copyright (C) 2009-2011 Institute for System Programming, RAS # Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) -# Copyright (C) 2011-2012 ROSA Laboratory -# Copyright (C) 2012-2015 Andrey Ponomarenko's ABI Laboratory +# Copyright (C) 2012-2018 Andrey Ponomarenko's ABI Laboratory # # Written by Andrey Ponomarenko # # PLATFORMS # ========= -# Linux, FreeBSD, Mac OS X, Haiku, MS Windows, Symbian +# Linux, FreeBSD, Solaris, Mac OS X, MS Windows, Symbian, Haiku # # REQUIREMENTS # ============ # Linux -# - G++ (3.0-4.7, 4.8.3, 4.9 or newer) +# - G++ (3.0 or newer) # - GNU Binutils (readelf, c++filt, objdump) -# - Perl 5 (5.8 or newer) -# - Ctags (5.8 or newer) +# - Perl 5 +# - Ctags +# - ABI Dumper >= 1.1 # # Mac OS X # - Xcode (g++, c++filt, otool, nm) -# - Ctags (5.8 or newer) +# - Ctags # # MS Windows -# - MinGW (3.0-4.7, 4.8.3, 4.9 or newer) +# - MinGW (3.0 or newer) # - MS Visual C++ (dumpbin, undname, cl) # - Active Perl 5 (5.8 or newer) -# - Sigcheck v1.71 or newer -# - Info-ZIP 3.0 (zip, unzip) -# - Ctags (5.8 or newer) +# - Sigcheck v2.52 or newer +# - GnuWin Zip and UnZip +# - Ctags (Exuberant or Universal) # - Add tool locations to the PATH environment variable -# - Run vsvars32.bat (C:\Microsoft Visual Studio 9.0\Common7\Tools\) +# - Run vcvars64.bat (C:\Microsoft Visual Studio 9.0\VC\bin\) # -# COMPATIBILITY -# ============= -# ABI Dumper >= 0.99.12 +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. # -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License or the GNU Lesser -# General Public License as published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, +# This library 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. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# and the GNU Lesser General Public License along with this program. -# If not, see . -########################################################################### +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA +######################################################################## use Getopt::Long; Getopt::Long::Configure ("posix_default", "no_ignore_case"); use File::Path qw(mkpath rmtree); use File::Temp qw(tempdir); -use File::Copy qw(copy move); -use Cwd qw(abs_path cwd realpath); -use Storable qw(dclone); +use File::Copy qw(copy); +use File::Basename qw(dirname); +use Cwd qw(abs_path cwd); use Data::Dumper; -use Config; -my $TOOL_VERSION = "1.99.14"; -my $ABI_DUMP_VERSION = "3.2"; +my $TOOL_VERSION = "2.3"; +my $ABI_DUMP_VERSION = "3.5"; +my $ABI_DUMP_VERSION_MIN = "3.5"; + my $XML_REPORT_VERSION = "1.2"; my $XML_ABI_DUMP_VERSION = "1.2"; -my $OSgroup = get_OSgroup(); -my $ORIG_DIR = cwd(); -my $TMP_DIR = tempdir(CLEANUP=>1); -my $LOCALE = "C.UTF-8"; # Internal modules -my $MODULES_DIR = get_Modules(); -push(@INC, get_dirname($MODULES_DIR)); +my $MODULES_DIR = getModules(); +push(@INC, dirname($MODULES_DIR)); + +# Basic modules +my %LoadedModules = (); +loadModule("Basic"); +loadModule("Input"); +loadModule("Path"); +loadModule("Logging"); +loadModule("Utils"); +loadModule("TypeAttr"); +loadModule("Filter"); +loadModule("SysFiles"); +loadModule("Descriptor"); +loadModule("Mangling"); + # Rules DB my %RULES_PATH = ( "Binary" => $MODULES_DIR."/RulesBin.xml", "Source" => $MODULES_DIR."/RulesSrc.xml"); -my ($Help, $ShowVersion, %Descriptor, $TargetLibraryName, -$TestTool, $DumpAPI, $SymbolsListPath, $CheckHeadersOnly_Opt, $UseDumps, -$AppPath, $StrictCompat, $DumpVersion, $ParamNamesPath, -%RelativeDirectory, $TargetTitle, $TestDump, $LoggingPath, -%TargetVersion, $InfoMsg, $CrossGcc, %OutputLogPath, -$OutputReportPath, $OutputDumpPath, $ShowRetVal, $SystemRoot_Opt, $DumpSystem, -$CmpSystems, $TargetLibsPath, $Debug, $CrossPrefix, $UseStaticLibs, $NoStdInc, -$TargetComponent_Opt, $TargetSysInfo, $TargetHeader, $ExtendedCheck, $Quiet, -$SkipHeadersPath, $CppCompat, $LogMode, $StdOut, $ListAffected, $ReportFormat, -$UserLang, $TargetHeadersPath, $BinaryOnly, $SourceOnly, $BinaryReportPath, -$SourceReportPath, $UseXML, $SortDump, $DumpFormat, -$ExtraInfo, $ExtraDump, $Force, $Tolerance, $Tolerant, $SkipSymbolsListPath, -$CheckInfo, $Quick, $AffectLimit, $AllAffected, $CppIncompat, -$SkipInternalSymbols, $SkipInternalTypes, $TargetArch, $GccOptions, -$TypesListPath, $SkipTypesListPath); - -my $CmdName = get_filename($0); -my %OS_LibExt = ( - "dynamic" => { - "linux"=>"so", - "macos"=>"dylib", - "windows"=>"dll", - "symbian"=>"dso", - "default"=>"so" - }, - "static" => { - "linux"=>"a", - "windows"=>"lib", - "symbian"=>"lib", - "default"=>"a" - } -); +my $BYTE = 8; +my $CmdName = getFilename($0); -my %OS_Archive = ( - "windows"=>"zip", - "default"=>"tar.gz" +my %HomePage = ( + "Dev"=>"https://github.com/lvc/abi-compliance-checker", + "Doc"=>"https://lvc.github.io/abi-compliance-checker/" ); -my %ERROR_CODE = ( - # Compatible verdict - "Compatible"=>0, - "Success"=>0, - # Incompatible verdict - "Incompatible"=>1, - # Undifferentiated error code - "Error"=>2, - # System command is not found - "Not_Found"=>3, - # Cannot access input files - "Access_Error"=>4, - # Cannot compile header files - "Cannot_Compile"=>5, - # Header compiled with errors - "Compile_Error"=>6, - # Invalid input ABI dump - "Invalid_Dump"=>7, - # Incompatible version of ABI dump - "Dump_Version"=>8, - # Cannot find a module - "Module_Error"=>9, - # Empty intersection between - # headers and shared objects - "Empty_Intersection"=>10, - # Empty set of symbols in headers - "Empty_Set"=>11 -); - -my $HomePage = "http://lvc.github.io/abi-compliance-checker/"; - my $ShortUsage = "ABI Compliance Checker (ABICC) $TOOL_VERSION A tool for checking backward compatibility of a C/C++ library API -Copyright (C) 2015 Andrey Ponomarenko's ABI Laboratory -License: GNU LGPL or GNU GPL +Copyright (C) 2018 Andrey Ponomarenko's ABI Laboratory +License: GNU LGPL 2.1 Usage: $CmdName [options] -Example: $CmdName -lib NAME -old OLD.xml -new NEW.xml - -OLD.xml and NEW.xml are XML-descriptors: +Example: $CmdName -l NAME -old ABI-0.dump -new ABI-1.dump - - 1.0 - - - - /path/to/headers/ - - - - /path/to/libraries/ - +ABI-0.dump and ABI-1.dump are ABI dumps generated +by the ABI Dumper or ABICC tools. More info: $CmdName --help\n"; @@ -180,116 +116,142 @@ exit(0); } -GetOptions("h|help!" => \$Help, - "i|info!" => \$InfoMsg, - "v|version!" => \$ShowVersion, - "dumpversion!" => \$DumpVersion, -# general options - "l|lib|library=s" => \$TargetLibraryName, - "d1|old|o=s" => \$Descriptor{1}{"Path"}, - "d2|new|n=s" => \$Descriptor{2}{"Path"}, - "dump|dump-abi|dump_abi=s" => \$DumpAPI, -# extra options - "app|application=s" => \$AppPath, - "static-libs!" => \$UseStaticLibs, - "gcc-path|cross-gcc=s" => \$CrossGcc, - "gcc-prefix|cross-prefix=s" => \$CrossPrefix, - "gcc-options=s" => \$GccOptions, - "sysroot=s" => \$SystemRoot_Opt, - "v1|vnum1|version1|vnum=s" => \$TargetVersion{1}, - "v2|vnum2|version2=s" => \$TargetVersion{2}, - "s|strict!" => \$StrictCompat, - "symbols-list=s" => \$SymbolsListPath, - "types-list=s" => \$TypesListPath, - "skip-symbols=s" => \$SkipSymbolsListPath, - "skip-types=s" => \$SkipTypesListPath, - "headers-list=s" => \$TargetHeadersPath, - "skip-headers=s" => \$SkipHeadersPath, - "header=s" => \$TargetHeader, - "headers-only|headers_only!" => \$CheckHeadersOnly_Opt, - "show-retval!" => \$ShowRetVal, - "use-dumps!" => \$UseDumps, - "nostdinc!" => \$NoStdInc, - "dump-system=s" => \$DumpSystem, - "sysinfo=s" => \$TargetSysInfo, - "cmp-systems!" => \$CmpSystems, - "libs-list=s" => \$TargetLibsPath, - "ext|extended!" => \$ExtendedCheck, - "q|quiet!" => \$Quiet, - "stdout!" => \$StdOut, - "report-format=s" => \$ReportFormat, - "dump-format=s" => \$DumpFormat, - "xml!" => \$UseXML, - "lang=s" => \$UserLang, - "arch=s" => \$TargetArch, - "binary|bin|abi!" => \$BinaryOnly, - "source|src|api!" => \$SourceOnly, - "limit-affected|affected-limit=s" => \$AffectLimit, -# other options - "test!" => \$TestTool, - "test-dump!" => \$TestDump, - "debug!" => \$Debug, - "cpp-compatible!" => \$CppCompat, - "cpp-incompatible!" => \$CppIncompat, - "p|params=s" => \$ParamNamesPath, - "relpath1|relpath=s" => \$RelativeDirectory{1}, - "relpath2=s" => \$RelativeDirectory{2}, - "dump-path=s" => \$OutputDumpPath, - "sort!" => \$SortDump, - "report-path=s" => \$OutputReportPath, - "bin-report-path=s" => \$BinaryReportPath, - "src-report-path=s" => \$SourceReportPath, - "log-path=s" => \$LoggingPath, - "log1-path=s" => \$OutputLogPath{1}, - "log2-path=s" => \$OutputLogPath{2}, - "logging-mode=s" => \$LogMode, - "list-affected!" => \$ListAffected, - "title|l-full|lib-full=s" => \$TargetTitle, - "component=s" => \$TargetComponent_Opt, - "extra-info=s" => \$ExtraInfo, - "extra-dump!" => \$ExtraDump, - "force!" => \$Force, - "tolerance=s" => \$Tolerance, - "tolerant!" => \$Tolerant, - "check!" => \$CheckInfo, - "quick!" => \$Quick, - "all-affected!" => \$AllAffected, - "skip-internal-symbols|skip-internal=s" => \$SkipInternalSymbols, - "skip-internal-types=s" => \$SkipInternalTypes, -) or ERR_MESSAGE(); +GetOptions( + "h|help!" => \$In::Opt{"Help"}, + "i|info!" => \$In::Opt{"InfoMsg"}, + "v|version!" => \$In::Opt{"ShowVersion"}, + "dumpversion!" => \$In::Opt{"DumpVersion"}, +# General + "l|lib|library=s" => \$In::Opt{"TargetLib"}, + "d1|old|o=s" => \$In::Desc{1}{"Path"}, + "d2|new|n=s" => \$In::Desc{2}{"Path"}, + "dump|dump-abi|dump_abi=s" => \$In::Opt{"DumpABI"}, + "d|f|filter=s" => \$In::Opt{"FilterPath"}, +# Extra + "debug!" => \$In::Opt{"Debug"}, + "debug-mangling!" => \$In::Opt{"DebugMangling"}, + "ext|extended!" => \$In::Opt{"ExtendedCheck"}, + "static|static-libs!" => \$In::Opt{"UseStaticLibs"}, + "gcc-path|cross-gcc=s" => \$In::Opt{"CrossGcc"}, + "gcc-prefix|cross-prefix=s" => \$In::Opt{"CrossPrefix"}, + "gcc-options=s" => \$In::Opt{"GccOptions"}, + "count-symbols=s" => \$In::Opt{"CountSymbols"}, + "use-dumps!" => \$In::Opt{"UseDumps"}, + "xml!" => \$In::Opt{"UseXML"}, + "app|application=s" => \$In::Opt{"AppPath"}, + "headers-only!" => \$In::Opt{"CheckHeadersOnly"}, + "v1|vnum1|version1=s" => \$In::Desc{1}{"TargetVersion"}, + "v2|vnum2|version2=s" => \$In::Desc{2}{"TargetVersion"}, + "relpath1=s" => \$In::Desc{1}{"RelativeDirectory"}, + "relpath2=s" => \$In::Desc{2}{"RelativeDirectory"}, +# Test + "test!" => \$In::Opt{"TestTool"}, + "test-dump!" => \$In::Opt{"TestDump"}, + "test-abi-dumper!" => \$In::Opt{"TestABIDumper"}, +# Report + "s|strict!" => \$In::Opt{"StrictCompat"}, + "binary|bin|abi!" => \$In::Opt{"BinOnly"}, + "source|src|api!" => \$In::Opt{"SrcOnly"}, +# Report path + "report-path=s" => \$In::Opt{"OutputReportPath"}, + "bin-report-path=s" => \$In::Opt{"BinReportPath"}, + "src-report-path=s" => \$In::Opt{"SrcReportPath"}, +# Report format + "show-retval!" => \$In::Opt{"ShowRetVal"}, + "stdout!" => \$In::Opt{"StdOut"}, + "report-format=s" => \$In::Opt{"ReportFormat"}, + "old-style!" => \$In::Opt{"OldStyle"}, + "title=s" => \$In::Opt{"TargetTitle"}, + "component=s" => \$In::Opt{"TargetComponent"}, + "p|params=s" => \$In::Opt{"ParamNamesPath"}, + "limit-affected|affected-limit=s" => \$In::Opt{"AffectLimit"}, + "all-affected!" => \$In::Opt{"AllAffected"}, + "list-affected!" => \$In::Opt{"ListAffected"}, +# ABI dump + "dump-path=s" => \$In::Opt{"OutputDumpPath"}, + "dump-format=s" => \$In::Opt{"DumpFormat"}, + "check!" => \$In::Opt{"CheckInfo"}, + "extra-info=s" => \$In::Opt{"ExtraInfo"}, + "extra-dump!" => \$In::Opt{"ExtraDump"}, + "relpath=s" => \$In::Desc{1}{"RelativeDirectory"}, + "vnum=s" => \$In::Desc{1}{"TargetVersion"}, + "sort!" => \$In::Opt{"SortDump"}, +# Filter symbols and types + "symbols-list=s" => \$In::Opt{"SymbolsListPath"}, + "types-list=s" => \$In::Opt{"TypesListPath"}, + "skip-symbols=s" => \$In::Opt{"SkipSymbolsListPath"}, + "skip-types=s" => \$In::Opt{"SkipTypesListPath"}, + "skip-internal-symbols|skip-internal=s" => \$In::Opt{"SkipInternalSymbols"}, + "skip-internal-types=s" => \$In::Opt{"SkipInternalTypes"}, + "keep-cxx!" => \$In::Opt{"KeepCxx"}, + "keep-reserved!" => \$In::Opt{"KeepReserved"}, +# Filter header files + "skip-headers=s" => \$In::Opt{"SkipHeadersPath"}, + "headers-list=s" => \$In::Opt{"TargetHeadersPath"}, + "header=s" => \$In::Opt{"TargetHeader"}, + "nostdinc!" => \$In::Opt{"NoStdInc"}, + "tolerance=s" => \$In::Opt{"Tolerance"}, + "tolerant!" => \$In::Opt{"Tolerant"}, + "skip-unidentified!" => \$In::Opt{"SkipUnidentified"}, +# Filter rules + "skip-typedef-uncover!" => \$In::Opt{"SkipTypedefUncover"}, + "check-private-abi!" => \$In::Opt{"CheckPrivateABI"}, + "disable-constants-check!" => \$In::Opt{"DisableConstantsCheck"}, + "skip-added-constants!" => \$In::Opt{"SkipAddedConstants"}, + "skip-removed-constants!" => \$In::Opt{"SkipRemovedConstants"}, +# Other + "lang=s" => \$In::Opt{"UserLang"}, + "arch=s" => \$In::Opt{"TargetArch"}, + "mingw-compatible!" => \$In::Opt{"MinGWCompat"}, + "cxx-incompatible|cpp-incompatible!" => \$In::Opt{"CxxIncompat"}, + "cpp-compatible!" => \$In::Opt{"CxxCompat"}, + "quick!" => \$In::Opt{"Quick"}, + "force!" => \$In::Opt{"Force"}, +# OS analysis + "dump-system=s" => \$In::Opt{"DumpSystem"}, + "cmp-systems!" => \$In::Opt{"CmpSystems"}, + "sysroot=s" => \$In::Opt{"SystemRoot"}, + "sysinfo=s" => \$In::Opt{"TargetSysInfo"}, + "libs-list=s" => \$In::Opt{"TargetLibsPath"}, +# Logging + "log-path=s" => \$In::Opt{"LoggingPath"}, + "log1-path=s" => \$In::Desc{1}{"OutputLogPath"}, + "log2-path=s" => \$In::Desc{2}{"OutputLogPath"}, + "logging-mode=s" => \$In::Opt{"LogMode"}, + "q|quiet!" => \$In::Opt{"Quiet"} +) or errMsg(); -sub ERR_MESSAGE() +sub errMsg() { printMsg("INFO", "\n".$ShortUsage); - exit($ERROR_CODE{"Error"}); + exit(getErrorCode("Error")); } -my $LIB_TYPE = $UseStaticLibs?"static":"dynamic"; -my $SLIB_TYPE = $LIB_TYPE; -if($OSgroup!~/macos|windows/ and $SLIB_TYPE eq "dynamic") -{ # show as "shared" library - $SLIB_TYPE = "shared"; -} -my $LIB_EXT = getLIB_EXT($OSgroup); -my $AR_EXT = getAR_EXT($OSgroup); -my $BYTE_SIZE = 8; -my $COMMON_LOG_PATH = "logs/run.log"; +# Default log path +$In::Opt{"DefaultLog"} = "logs/run.log"; -my $HelpMessage=" +my $HelpMessage = " NAME: ABI Compliance Checker ($CmdName) Check backward compatibility of a C/C++ library API DESCRIPTION: - ABI Compliance Checker (ABICC) is a tool for checking backward binary and - source-level compatibility of a $SLIB_TYPE C/C++ library. The tool checks - header files and $SLIB_TYPE libraries (*.$LIB_EXT) of old and new versions and - analyzes changes in API and ABI (ABI=API+compiler ABI) that may break binary - and/or source-level compatibility: changes in calling stack, v-table changes, - removed symbols, renamed fields, etc. Binary incompatibility may result in - crashing or incorrect behavior of applications built with an old version of - a library if they run on a new one. Source incompatibility may result in - recompilation errors with a new library version. + ABI Compliance Checker (ABICC) is a tool for checking backward binary + compatibility and backward source compatibility of a C/C++ library API. + + The tool analyzes changes in API and ABI (ABI=API+compiler ABI) that may + break binary compatibility and/or source compatibility: changes in calling + stack, v-table changes, removed symbols, renamed fields, etc. + + Binary incompatibility may result in crashing or incorrect behavior of + applications built with an old version of a library if they run on a new + one. Source incompatibility may result in recompilation errors with a new + library version. + + The tool can create and compare ABI dumps for header files and shared + objects of a library. The ABI dump for a library can also be created by + the ABI Dumper tool (https://github.com/lvc/abi-dumper) if shared objects + include debug-info. The tool is intended for developers of software libraries and maintainers of operating systems who are interested in ensuring backward compatibility, @@ -301,38 +263,79 @@ the application to a new library version. This tool is free software: you can redistribute it and/or modify it - under the terms of the GNU LGPL or GNU GPL. + under the terms of the GNU LGPL 2.1. + +USAGE #1 (WITH ABI DUMPER): + + 1. Library should be compiled with \"-g -Og\" GCC options + to contain DWARF debug info + + 2. Create ABI dumps for both library versions + using the ABI Dumper (https://github.com/lvc/abi-dumper) tool: + + abi-dumper OLD.so -o ABI-0.dump -lver 0 + abi-dumper NEW.so -o ABI-1.dump -lver 1 + + 3. You can filter public ABI with the help of + additional -public-headers option of the ABI Dumper tool. + + 4. Compare ABI dumps to create report: + + abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump + +USAGE #2 (ORIGINAL): + + 1. Create XML-descriptors for two versions + of a library (OLD.xml and NEW.xml): + + + 1.0 + + + + /path/to/headers/ + + + + /path/to/libraries/ + + + 2. Compare Xml-descriptors to create report: -USAGE: - $CmdName [options] + abi-compliance-checker -lib NAME -old OLD.xml -new NEW.xml -EXAMPLE: - $CmdName -lib NAME -old OLD.xml -new NEW.xml +USAGE #3 (CREATE ABI DUMPS): - OLD.xml and NEW.xml are XML-descriptors: + 1. Create XML-descriptors for two versions + of a library (OLD.xml and NEW.xml): - - 1.0 - + + 1.0 + - - /path1/to/header(s)/ - /path2/to/header(s)/ - ... - + + /path/to/headers/ + - - /path1/to/library(ies)/ - /path2/to/library(ies)/ - ... - + + /path/to/libraries/ + -INFORMATION OPTIONS: + 2. Create ABI dumps: + + abi-compliance-checker -lib NAME -dump OLD.xml -dump-path ./ABI-0.dump + abi-compliance-checker -lib NAME -dump NEW.xml -dump-path ./ABI-1.dump + + 3. Compare ABI dumps to create report: + + abi-compliance-checker -l NAME -old ABI-0.dump -new ABI-1.dump + +INFO OPTIONS: -h|-help Print this help. -i|-info - Print complete info. + Print complete info including all options. -v|-version Print version information. @@ -341,14 +344,15 @@ Print the tool version ($TOOL_VERSION) and don't do anything else. GENERAL OPTIONS: - -l|-lib|-library NAME - Library name (without version). + -l|-library NAME + Any name of the library. - -d1|-old|-o PATH - Descriptor of 1st (old) library version. + -old|-d1 PATH + Descriptor of the 1st (old) library version. It may be one of the following: - 1. XML-descriptor (VERSION.xml file): + 1. ABI dump generated by the ABI Dumper tool + 2. XML-descriptor (*.xml file): 1.0 @@ -368,41 +372,57 @@ ... - 2. ABI dump generated by -dump option - 3. Directory with headers and/or $SLIB_TYPE libraries - 4. Single header file + 3. ABI dump generated by -dump option + 4. Directory with headers and libraries + 5. Single header file - If you are using an 2-4 descriptor types then you should - specify version numbers with -v1 and -v2 options too. + If you are using 4-5 descriptor types then you should + specify version numbers with -v1 and -v2 options. For more information, please see: - http://ispras.linuxbase.org/index.php/Library_Descriptor + https://lvc.github.io/abi-compliance-checker/Xml-Descriptor.html - -d2|-new|-n PATH - Descriptor of 2nd (new) library version. + -new|-d2 PATH + Descriptor of the 2nd (new) library version. - -dump|-dump-abi PATH + -dump PATH Create library ABI dump for the input XML descriptor. You can transfer it anywhere and pass instead of the descriptor. Also it can be used for debugging the tool. - - Supported versions of ABI dump: 2.0<=V<=$ABI_DUMP_VERSION\n"; + + -filter PATH + A path to XML descriptor with skip_* rules to filter + analyzed symbols in the report. +"; -sub HELP_MESSAGE() { +sub helpMsg() { printMsg("INFO", $HelpMessage." -MORE INFO: +MORE OPTIONS: $CmdName --info\n"); } -sub INFO_MESSAGE() +sub infoMsg() { printMsg("INFO", "$HelpMessage EXTRA OPTIONS: - -app|-application PATH - This option allows to specify the application that should be checked - for portability to the new library version. + -debug + Debugging mode. Print debug info on the screen. Save intermediate + analysis stages in the debug directory: + debug/LIB_NAME/VERSION/ - -static-libs + Also consider using -dump option for debugging the tool. + + -ext|-extended + If your library A is supposed to be used by other library B and you + want to control the ABI of B, then you should enable this option. The + tool will check for changes in all data types, even if they are not + used by any function in the library A. Such data types are not part + of the A library ABI, but may be a part of the ABI of the B library. + + The short scheme is: + app C (broken) -> lib B (broken ABI) -> lib A (stable ABI) + + -static Check static libraries instead of the shared ones. The section of the XML-descriptor should point to static libraries location. @@ -411,15 +431,30 @@ -gcc-prefix PREFIX GCC toolchain prefix. - + -gcc-options OPTS Additional compiler options. - -sysroot DIR - Specify the alternative root directory. The tool will search for include - paths in the DIR/usr/include and DIR/usr/lib directories. + -count-symbols PATH + Count total public symbols in the ABI dump. + + -use-dumps + Make dumps for two versions of a library and compare dumps. This should + increase the performance of the tool and decrease the system memory usage. + + -xml + Alias for: --report-format=xml or --dump-format=xml + + -app|-application PATH + This option allows to specify the application that should be checked + for portability to the new library version. + + -headers-only + Check header files without libraries. It is easy to run, but may + provide a low quality compatibility report with false positives and + without detecting of added/removed symbols. - -v1|-version1 NUM + -v1|-vnum1 NUM Specify 1st library version outside the descriptor. This option is needed if you have preferred an alternative descriptor type (see -d1 option). @@ -428,61 +463,251 @@ VERSION - -v2|-version2 NUM + -v2|-vnum2 NUM Specify 2nd library version outside the descriptor. - -vnum NUM - Specify the library version in the generated ABI dump. The section - of the input XML descriptor will be overwritten in this case. + -relpath1 PATH + Replace {RELPATH} macros to PATH in the 1st XML-descriptor (-d1). + + -relpath2 PATH + Replace {RELPATH} macros to PATH in the 2nd XML-descriptor (-d2). + +TEST OPTIONS: + -test + Run internal tests. Create two binary incompatible versions of a sample + library and run the tool to check them for compatibility. This option + allows to check if the tool works correctly in the current environment. + + -test-dump + Test ability to create, read and compare ABI dumps. + + -test-abi-dumper + Compare ABI dumps created by the ABI Dumper tool. + +REPORT OPTIONS: + -binary|-bin|-abi + Show binary compatibility problems only. + Generate report to: + compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html + + -source|-src|-api + Show source compatibility problems only. + Generate report to: + compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html -s|-strict Treat all compatibility warnings as problems. Add a number of \"Low\" severity problems to the return value of the tool. - -headers-only - Check header files without $SLIB_TYPE libraries. It is easy to run, but may - provide a low quality compatibility report with false positives and - without detecting of added/removed symbols. - - Alternatively you can write \"none\" word to the section - in the XML-descriptor: - - none - +REPORT PATH OPTIONS: + -report-path PATH + Path to compatibility report. + Default: + compat_reports/LIB_NAME/V1_to_V2/compat_report.html + + -bin-report-path PATH + Path to binary compatibility report. + Default: + compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html + -src-report-path PATH + Path to source compatibility report. + Default: + compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html + +REPORT FORMAT OPTIONS: -show-retval Show the symbol's return type in the report. + -stdout + Print analysis results (compatibility reports and ABI dumps) to stdout + instead of creating a file. This would allow piping data to other programs. + + -report-format FMT + Change format of compatibility report. + Formats: + htm - HTML format (default) + xml - XML format + + -old-style + Generate old-style report. + + -title NAME + Change library name in the report title to NAME. By default + will be displayed a name specified by -l option. + + -component NAME + The component name in the title and summary of the HTML report. + Default: + library + + -p|-params PATH + Path to file with the function parameter names. It can be used + for improving report view if the library header files have no + parameter names. File format: + + func1;param1;param2;param3 ... + func2;param1;param2;param3 ... + ... + + -limit-affected LIMIT + The maximum number of affected symbols listed under the description + of the changed type in the report. + + -list-affected + Generate file with the list of incompatible + symbols beside the HTML compatibility report. + Use 'c++filt \@file' command from GNU binutils + to unmangle C++ symbols in the generated file. + Default names: + abi_affected.txt + src_affected.txt + +ABI DUMP OPTIONS: + -dump-path PATH + Specify a *.dump file path where to generate an ABI dump. + Default: + abi_dumps/LIB_NAME/VERSION/ABI.dump + + -dump-format FMT + Change format of ABI dump. + Formats: + perl - Data::Dumper format (default) + xml - XML format + + -check + Check completeness of the ABI dump. + + -extra-info DIR + Dump extra info to DIR. + + -extra-dump + Create extended ABI dump containing all symbols + from the translation unit. + + -relpath PATH + Replace {RELPATH} macros to PATH in the XML-descriptor used + for dumping the library ABI (see -dump option). + + -vnum NUM + Specify the library version in the generated ABI dump. The section + of the input XML descriptor will be overwritten in this case. + + -sort + Enable sorting of data in ABI dumps. + +FILTER SYMBOLS OPTIONS: -symbols-list PATH This option allows to specify a file with a list of symbols (mangled names in C++) that should be checked. Other symbols will not be checked. - + -types-list PATH This option allows to specify a file with a list of types that should be checked. Other types will not be checked. - + -skip-symbols PATH The list of symbols that should not be checked. - + -skip-types PATH The list of types that should not be checked. - -headers-list PATH - The file with a list of headers, that should be checked/dumped. - + -skip-internal-symbols PATTERN + Do not check symbols matched by the regular expression. + + -skip-internal-types PATTERN + Do not check types matched by the regular expression. + It's matched against full qualified type names (e.g. 'struct xyz::Name'). + It has to match any part of type name. + + -keep-cxx + Check _ZS*, _ZNS* and _ZNKS* symbols. + + -keep-reserved + Report changes in reserved fields. + +FILTER HEADERS OPTIONS: -skip-headers PATH The file with the list of header files, that should not be checked. - + + -headers-list PATH + The file with a list of headers, that should be checked/dumped. + -header NAME Check/Dump ABI of this header only. - -use-dumps - Make dumps for two versions of a library and compare dumps. This should - increase the performance of the tool and decrease the system memory usage. - -nostdinc Do not search in GCC standard system directories for header files. + -tolerance LEVEL + Apply a set of heuristics to successfully compile input + header files. You can enable several tolerance levels by + joining them into one string (e.g. 13, 124, etc.). + Levels: + 1 - skip non-Linux headers (e.g. win32_*.h, etc.) + 2 - skip internal headers (e.g. *_p.h, impl/*.h, etc.) + 3 - skip headers that include non-Linux headers + 4 - skip headers included by others + + -tolerant + Enable highest tolerance level [1234]. + + -skip-unidentified + Skip header files in 'headers' and 'include_preamble' sections + of the XML descriptor that cannot be found. This is useful if + you are trying to use the same descriptor for different targets. + +FILTER RULES OPTIONS: + -skip-typedef-uncover + Do not report a problem if type is covered or + uncovered by typedef (useful for broken debug info). + + -check-private-abi + Check data types from the private part of the ABI when + comparing ABI dumps created by the ABI Dumper tool with + use of the -public-headers option. + + Requires ABI Dumper >= 0.99.14 + + -disable-constants-check + Do not check for changes in constants. + + -skip-added-constants + Do not detect added constants. + + -skip-removed-constants + Do not detect removed constants. + +OTHER OPTIONS: + -lang LANG + Set library language (C or C++). You can use this option if the tool + cannot auto-detect a language. This option may be useful for checking + C-library headers (--lang=C) in --headers-only or --extended modes. + + -arch ARCH + Set library architecture (x86, x86_64, ia64, arm, ppc32, ppc64, s390, + ect.). The option is useful if the tool cannot detect correct architecture + of the input objects. + + -mingw-compatible + If input header files are compatible with the MinGW GCC compiler, + then you can tell the tool about this and speedup the analysis. + + -cxx-incompatible + Set this option if input C header files use C++ keywords. The tool + will try to replace such keywords at preprocessor stage and replace + them back in the final TU dump. + + -cpp-compatible + Do nothing. + + -quick + Quick analysis. Disable check of some template instances. + + -force + Try to enable this option if the tool checked not all + types and symbols in header files. + +OS ANALYSIS OPTIONS: -dump-system NAME -sysroot DIR Find all the shared libraries and header files in DIR directory, create XML descriptors and make ABI dumps for each library. The result @@ -543,146 +768,27 @@ /* Additional GCC options, one per line */ + -cmp-systems -d1 sys_dumps/NAME1/ARCH -d2 sys_dumps/NAME2/ARCH + Compare two ABI dumps of a system. Create compatibility reports for + each system library and the common HTML report including the summary + of test results for all checked libraries. + + Summary report will be generated to: + sys_compat_reports/NAME1_to_NAME2/ARCH + + -sysroot DIR + Specify the alternative root directory. The tool will search for include + paths in the DIR/usr/include and DIR/usr/lib directories. + -sysinfo DIR This option should be used with -dump-system option to dump ABI of operating systems and configure the dumping process. - You can find a sample in the package: - modules/Targets/{unix, symbian, windows} - - -cmp-systems -d1 sys_dumps/NAME1/ARCH -d2 sys_dumps/NAME2/ARCH - Compare two system ABI dumps. Create compatibility reports for each - library and the common HTML report including the summary of test - results for all checked libraries. Report will be generated to: - sys_compat_reports/NAME1_to_NAME2/ARCH -libs-list PATH The file with a list of libraries, that should be dumped by the -dump-system option or should be checked by the -cmp-systems option. - -ext|-extended - If your library A is supposed to be used by other library B and you - want to control the ABI of B, then you should enable this option. The - tool will check for changes in all data types, even if they are not - used by any function in the library A. Such data types are not part - of the A library ABI, but may be a part of the ABI of the B library. - - The short scheme is: - app C (broken) -> lib B (broken ABI) -> lib A (stable ABI) - - -q|-quiet - Print all messages to the file instead of stdout and stderr. - Default path (can be changed by -log-path option): - $COMMON_LOG_PATH - - -stdout - Print analysis results (compatibility reports and ABI dumps) to stdout - instead of creating a file. This would allow piping data to other programs. - - -report-format FMT - Change format of compatibility report. - Formats: - htm - HTML format (default) - xml - XML format - - -dump-format FMT - Change format of ABI dump. - Formats: - perl - Data::Dumper format (default) - xml - XML format - - -xml - Alias for: --report-format=xml or --dump-format=xml - - -lang LANG - Set library language (C or C++). You can use this option if the tool - cannot auto-detect a language. This option may be useful for checking - C-library headers (--lang=C) in --headers-only or --extended modes. - - -arch ARCH - Set library architecture (x86, x86_64, ia64, arm, ppc32, ppc64, s390, - ect.). The option is useful if the tool cannot detect correct architecture - of the input objects. - - -binary|-bin|-abi - Show \"Binary\" compatibility problems only. - Generate report to: - compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html - - -source|-src|-api - Show \"Source\" compatibility problems only. - Generate report to: - compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html - - -limit-affected LIMIT - The maximum number of affected symbols listed under the description - of the changed type in the report. - -OTHER OPTIONS: - -test - Run internal tests. Create two binary incompatible versions of a sample - library and run the tool to check them for compatibility. This option - allows to check if the tool works correctly in the current environment. - - -test-dump - Test ability to create, read and compare ABI dumps. - - -debug - Debugging mode. Print debug info on the screen. Save intermediate - analysis stages in the debug directory: - debug/LIB_NAME/VERSION/ - - Also consider using --dump option for debugging the tool. - - -cpp-compatible - If your header files are written in C language and can be compiled - by the G++ compiler (i.e. don't use C++ keywords), then you can tell - the tool about this and speedup the analysis. - - -cpp-incompatible - Set this option if input C header files use C++ keywords. - - -p|-params PATH - Path to file with the function parameter names. It can be used - for improving report view if the library header files have no - parameter names. File format: - - func1;param1;param2;param3 ... - func2;param1;param2;param3 ... - ... - - -relpath PATH - Replace {RELPATH} macros to PATH in the XML-descriptor used - for dumping the library ABI (see -dump option). - - -relpath1 PATH - Replace {RELPATH} macros to PATH in the 1st XML-descriptor (-d1). - - -relpath2 PATH - Replace {RELPATH} macros to PATH in the 2nd XML-descriptor (-d2). - - -dump-path PATH - Specify a *.abi.$AR_EXT or *.abi file path where to generate an ABI dump. - Default: - abi_dumps/LIB_NAME/LIB_NAME_VERSION.abi.$AR_EXT - - -sort - Enable sorting of data in ABI dumps. - - -report-path PATH - Path to compatibility report. - Default: - compat_reports/LIB_NAME/V1_to_V2/compat_report.html - - -bin-report-path PATH - Path to \"Binary\" compatibility report. - Default: - compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html - - -src-report-path PATH - Path to \"Source\" compatibility report. - Default: - compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html - +LOGGING OPTIONS: -log-path PATH Log path for all messages. Default: @@ -705,63 +811,16 @@ a - append old logs n - do not write any logs - -list-affected - Generate file with the list of incompatible - symbols beside the HTML compatibility report. - Use 'c++filt \@file' command from GNU binutils - to unmangle C++ symbols in the generated file. - Default names: - abi_affected.txt - src_affected.txt - - -component NAME - The component name in the title and summary of the HTML report. - Default: - library - - -title NAME - Change library name in the report title to NAME. By default - will be displayed a name specified by -l option. - - -extra-info DIR - Dump extra info to DIR. - - -extra-dump - Create extended ABI dump containing all symbols - from the translation unit. - - -force - Try to use this option if the tool doesn't work. - - -tolerance LEVEL - Apply a set of heuristics to successfully compile input - header files. You can enable several tolerance levels by - joining them into one string (e.g. 13, 124, etc.). - Levels: - 1 - skip non-Linux headers (e.g. win32_*.h, etc.) - 2 - skip internal headers (e.g. *_p.h, impl/*.h, etc.) - 3 - skip headers that iclude non-Linux headers - 4 - skip headers included by others - - -tolerant - Enable highest tolerance level [1234]. - - -check - Check completeness of the ABI dump. - - -quick - Quick analysis. Disable check of some template instances. - - -skip-internal-symbols PATTERN - Do not check symbols matched by the pattern. - - -skip-internal-types PATTERN - Do not check types matched by the pattern. + -q|-quiet + Print all messages to the file instead of stdout and stderr. + Default path (can be changed by -log-path option): + ".$In::Opt{"DefaultLog"}." -REPORT: +REPORT PATH: Compatibility report will be generated to: compat_reports/LIB_NAME/V1_to_V2/compat_report.html +LOG PATH: Log will be generated to: logs/LIB_NAME/V1/log.txt logs/LIB_NAME/V2/log.txt @@ -770,668 +829,39 @@ 0 - Compatible. The tool has run without any errors. non-zero - Incompatible or the tool has run with errors. -MORE INFORMATION: - ".$HomePage."\n"); +MORE INFO: + ".$HomePage{"Doc"}." + ".$HomePage{"Dev"}."\n\n"); } -my %Operator_Indication = ( - "not" => "~", - "assign" => "=", - "andassign" => "&=", - "orassign" => "|=", - "xorassign" => "^=", - "or" => "|", - "xor" => "^", - "addr" => "&", - "and" => "&", - "lnot" => "!", - "eq" => "==", - "ne" => "!=", - "lt" => "<", - "lshift" => "<<", - "lshiftassign" => "<<=", - "rshiftassign" => ">>=", - "call" => "()", - "mod" => "%", - "modassign" => "%=", - "subs" => "[]", - "land" => "&&", - "lor" => "||", - "rshift" => ">>", - "ref" => "->", - "le" => "<=", - "deref" => "*", - "mult" => "*", - "preinc" => "++", - "delete" => " delete", - "vecnew" => " new[]", - "vecdelete" => " delete[]", - "predec" => "--", - "postinc" => "++", - "postdec" => "--", - "plusassign" => "+=", - "plus" => "+", - "minus" => "-", - "minusassign" => "-=", - "gt" => ">", - "ge" => ">=", - "new" => " new", - "multassign" => "*=", - "divassign" => "/=", - "div" => "/", - "neg" => "-", - "pos" => "+", - "memref" => "->*", - "compound" => "," ); - -my %UnknownOperator; - -my %NodeType= ( - "array_type" => "Array", - "binfo" => "Other", - "boolean_type" => "Intrinsic", - "complex_type" => "Intrinsic", - "const_decl" => "Other", - "enumeral_type" => "Enum", - "field_decl" => "Other", - "function_decl" => "Other", - "function_type" => "FunctionType", - "identifier_node" => "Other", - "integer_cst" => "Other", - "integer_type" => "Intrinsic", - "vector_type" => "Vector", - "method_type" => "MethodType", - "namespace_decl" => "Other", - "parm_decl" => "Other", - "pointer_type" => "Pointer", - "real_cst" => "Other", - "real_type" => "Intrinsic", - "record_type" => "Struct", - "reference_type" => "Ref", - "string_cst" => "Other", - "template_decl" => "Other", - "template_type_parm" => "TemplateParam", - "typename_type" => "TypeName", - "sizeof_expr" => "SizeOf", - "tree_list" => "Other", - "tree_vec" => "Other", - "type_decl" => "Other", - "union_type" => "Union", - "var_decl" => "Other", - "void_type" => "Intrinsic", - "nop_expr" => "Other", # - "addr_expr" => "Other", # - "offset_type" => "Other" ); - -my %CppKeywords_C = map {$_=>1} ( - # C++ 2003 keywords - "public", - "protected", - "private", - "default", - "template", - "new", - #"asm", - "dynamic_cast", - "auto", - "try", - "namespace", - "typename", - "using", - "reinterpret_cast", - "friend", - "class", - "virtual", - "const_cast", - "mutable", - "static_cast", - "export", - # C++0x keywords - "noexcept", - "nullptr", - "constexpr", - "static_assert", - "explicit", - # cannot be used as a macro name - # as it is an operator in C++ - "and", - #"and_eq", - "not", - #"not_eq", - "or" - #"or_eq", - #"bitand", - #"bitor", - #"xor", - #"xor_eq", - #"compl" -); - -my %CppKeywords_F = map {$_=>1} ( - "delete", - "catch", - "alignof", - "thread_local", - "decltype", - "typeid" -); - -my %CppKeywords_O = map {$_=>1} ( - "bool", - "register", - "inline", - "operator" -); - -my %CppKeywords_A = map {$_=>1} ( - "this", - "throw", - "template" -); - -foreach (keys(%CppKeywords_C), -keys(%CppKeywords_F), -keys(%CppKeywords_O)) { - $CppKeywords_A{$_}=1; -} - -# Header file extensions as described by gcc -my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+"; - -my %IntrinsicMangling = ( - "void" => "v", - "bool" => "b", - "wchar_t" => "w", - "char" => "c", - "signed char" => "a", - "unsigned char" => "h", - "short" => "s", - "unsigned short" => "t", - "int" => "i", - "unsigned int" => "j", - "long" => "l", - "unsigned long" => "m", - "long long" => "x", - "__int64" => "x", - "unsigned long long" => "y", - "__int128" => "n", - "unsigned __int128" => "o", - "float" => "f", - "double" => "d", - "long double" => "e", - "__float80" => "e", - "__float128" => "g", - "..." => "z" -); - -my %IntrinsicNames = map {$_=>1} keys(%IntrinsicMangling); - -my %StdcxxMangling = ( - "3std"=>"St", - "3std9allocator"=>"Sa", - "3std12basic_string"=>"Sb", - "3std12basic_stringIcE"=>"Ss", - "3std13basic_istreamIcE"=>"Si", - "3std13basic_ostreamIcE"=>"So", - "3std14basic_iostreamIcE"=>"Sd" -); - -my $DEFAULT_STD_PARMS = "std::(allocator|less|char_traits|regex_traits|istreambuf_iterator|ostreambuf_iterator)"; -my %DEFAULT_STD_ARGS = map {$_=>1} ("_Alloc", "_Compare", "_Traits", "_Rx_traits", "_InIter", "_OutIter"); - -my $ADD_TMPL_INSTANCES = 1; -my $EMERGENCY_MODE_48 = 0; - -my %ConstantSuffix = ( - "unsigned int"=>"u", - "long"=>"l", - "unsigned long"=>"ul", - "long long"=>"ll", - "unsigned long long"=>"ull" -); - -my %ConstantSuffixR = -reverse(%ConstantSuffix); - -my %OperatorMangling = ( - "~" => "co", - "=" => "aS", - "|" => "or", - "^" => "eo", - "&" => "an",#ad (addr) - "==" => "eq", - "!" => "nt", - "!=" => "ne", - "<" => "lt", - "<=" => "le", - "<<" => "ls", - "<<=" => "lS", - ">" => "gt", - ">=" => "ge", - ">>" => "rs", - ">>=" => "rS", - "()" => "cl", - "%" => "rm", - "[]" => "ix", - "&&" => "aa", - "||" => "oo", - "*" => "ml",#de (deref) - "++" => "pp",# - "--" => "mm",# - "new" => "nw", - "delete" => "dl", - "new[]" => "na", - "delete[]" => "da", - "+=" => "pL", - "+" => "pl",#ps (pos) - "-" => "mi",#ng (neg) - "-=" => "mI", - "*=" => "mL", - "/=" => "dV", - "&=" => "aN", - "|=" => "oR", - "%=" => "rM", - "^=" => "eO", - "/" => "dv", - "->*" => "pm", - "->" => "pt",#rf (ref) - "," => "cm", - "?" => "qu", - "." => "dt", - "sizeof"=> "sz"#st -); - -my %Intrinsic_Keywords = map {$_=>1} ( - "true", - "false", - "_Bool", - "_Complex", - "const", - "int", - "long", - "void", - "short", - "float", - "volatile", - "restrict", - "unsigned", - "signed", - "char", - "double", - "class", - "struct", - "union", - "enum" -); - -my %GlibcHeader = map {$_=>1} ( - "aliases.h", - "argp.h", - "argz.h", - "assert.h", - "cpio.h", - "ctype.h", - "dirent.h", - "envz.h", - "errno.h", - "error.h", - "execinfo.h", - "fcntl.h", - "fstab.h", - "ftw.h", - "glob.h", - "grp.h", - "iconv.h", - "ifaddrs.h", - "inttypes.h", - "langinfo.h", - "limits.h", - "link.h", - "locale.h", - "malloc.h", - "math.h", - "mntent.h", - "monetary.h", - "nl_types.h", - "obstack.h", - "printf.h", - "pwd.h", - "regex.h", - "sched.h", - "search.h", - "setjmp.h", - "shadow.h", - "signal.h", - "spawn.h", - "stdarg.h", - "stdint.h", - "stdio.h", - "stdlib.h", - "string.h", - "strings.h", - "tar.h", - "termios.h", - "time.h", - "ulimit.h", - "unistd.h", - "utime.h", - "wchar.h", - "wctype.h", - "wordexp.h" ); - -my %GlibcDir = map {$_=>1} ( - "arpa", - "bits", - "gnu", - "netinet", - "net", - "nfs", - "rpc", - "sys", - "linux" ); - -my %WinHeaders = map {$_=>1} ( - "dos.h", - "process.h", - "winsock.h", - "config-win.h", - "mem.h", - "windows.h", - "winsock2.h", - "crtdbg.h", - "ws2tcpip.h" -); - -my %ObsoleteHeaders = map {$_=>1} ( - "iostream.h", - "fstream.h" -); - -my %AlienHeaders = map {$_=>1} ( - # Solaris - "thread.h", - "sys/atomic.h", - # HPUX - "sys/stream.h", - # Symbian - "AknDoc.h", - # Atari ST - "ext.h", - "tos.h", - # MS-DOS - "alloc.h", - # Sparc - "sys/atomic.h" -); - -my %ConfHeaders = map {$_=>1} ( - "atomic", - "conf.h", - "config.h", - "configure.h", - "build.h", - "setup.h" -); - -my %LocalIncludes = map {$_=>1} ( - "/usr/local/include", - "/usr/local" ); - -my %OS_AddPath=( -# These paths are needed if the tool cannot detect them automatically - "macos"=>{ - "include"=>[ - "/Library", - "/Developer/usr/include" - ], - "lib"=>[ - "/Library", - "/Developer/usr/lib" - ], - "bin"=>[ - "/Developer/usr/bin" - ] - }, - "beos"=>{ - # Haiku has GCC 2.95.3 by default - # try to find GCC>=3.0 in /boot/develop/abi - "include"=>[ - "/boot/common", - "/boot/develop" - ], - "lib"=>[ - "/boot/common/lib", - "/boot/system/lib", - "/boot/apps" - ], - "bin"=>[ - "/boot/common/bin", - "/boot/system/bin", - "/boot/develop/abi" - ] - } -); - -my %Slash_Type=( - "default"=>"/", - "windows"=>"\\" -); - -my $SLASH = $Slash_Type{$OSgroup}?$Slash_Type{$OSgroup}:$Slash_Type{"default"}; - -# Global Variables -my %COMMON_LANGUAGE=( - 1 => "C", - 2 => "C" ); - -my $MAX_COMMAND_LINE_ARGUMENTS = 4096; -my $MAX_CPPFILT_FILE_SIZE = 50000; -my $CPPFILT_SUPPORT_FILE; - -my (%WORD_SIZE, %CPU_ARCH, %GCC_VERSION); - -my $STDCXX_TESTING = 0; -my $GLIBC_TESTING = 0; -my $CPP_HEADERS = 0; - -my $CheckHeadersOnly = $CheckHeadersOnly_Opt; -my $CheckUndefined = 0; - -my $TargetComponent = undef; -if($TargetComponent_Opt) { - $TargetComponent = lc($TargetComponent_Opt); -} -else -{ # default: library - # other components: header, system, ... - $TargetComponent = "library"; -} +# Aliases +my (%SymbolInfo, %TypeInfo, %TName_Tid, %Constants) = (); +# Global +my %Cache; my $TOP_REF = "to the top"; - -my $SystemRoot; - -my $MAIN_CPP_DIR; my %RESULT; -my %LOG_PATH; -my %DEBUG_PATH; -my %Cache; -my %LibInfo; -my $COMPILE_ERRORS = 0; -my %CompilerOptions; -my %CheckedDyLib; -my $TargetLibraryShortName = parse_libname($TargetLibraryName, "shortest", $OSgroup); - -# Constants (#defines) -my %Constants; -my %SkipConstants; -my %EnumConstants; - -# Extra Info -my %SymbolHeader; -my %KnownLibs; - -# Templates -my %TemplateInstance; -my %BasicTemplate; -my %TemplateArg; -my %TemplateDecl; -my %TemplateMap; - -# Types -my %TypeInfo; -my %SkipTypes = ( - "1"=>{}, - "2"=>{} ); + +# Counter my %CheckedTypes; -my %TName_Tid; -my %EnumMembName_Id; -my %NestedNameSpaces = ( - "1"=>{}, - "2"=>{} ); +my %CheckedSymbols; + +# Classes my %VirtualTable; my %VirtualTable_Model; -my %ClassVTable; -my %ClassVTable_Content; my %VTableClass; my %AllocableClass; my %ClassMethods; my %ClassNames; -my %Class_SubClasses; my %OverriddenMethods; -my %TypedefToAnon; -my $MAX_ID = 0; - -my %CheckedTypeInfo; - -# Typedefs -my %Typedef_BaseName; -my %Typedef_Tr; -my %Typedef_Eq; -my %StdCxxTypedef; -my %MissedTypedef; -my %MissedBase; -my %MissedBase_R; -my %TypeTypedef; # Symbols -my %SymbolInfo; -my %tr_name; -my %mangled_name_gcc; -my %mangled_name; -my %SkipSymbols = ( - "1"=>{}, - "2"=>{} ); -my %SkipNameSpaces = ( - "1"=>{}, - "2"=>{} ); -my %AddNameSpaces = ( - "1"=>{}, - "2"=>{} ); -my %SymbolsList; -my %TypesList; -my %SymbolsList_App; -my %CheckedSymbols; -my %Symbol_Library = ( - "1"=>{}, - "2"=>{} ); -my %Library_Symbol = ( - "1"=>{}, - "2"=>{} ); -my %DepSymbol_Library = ( - "1"=>{}, - "2"=>{} ); -my %DepLibrary_Symbol = ( - "1"=>{}, - "2"=>{} ); -my %MangledNames; my %Func_ShortName; -my %AddIntParams; +my %AddSymbolParams; my %GlobalDataObject; -my %WeakSymbols; -my %Library_Needed= ( - "1"=>{}, - "2"=>{} ); - -# Extra Info -my %UndefinedSymbols; -my %PreprocessedHeaders; - -# Headers -my %Include_Preamble = ( - "1"=>[], - "2"=>[] ); -my %Registered_Headers; -my %Registered_Sources; -my %HeaderName_Paths; -my %Header_Dependency; -my %Include_Neighbors; -my %Include_Paths = ( - "1"=>[], - "2"=>[] ); -my %INC_PATH_AUTODETECT = ( - "1"=>1, - "2"=>1 ); -my %Add_Include_Paths = ( - "1"=>[], - "2"=>[] ); -my %Skip_Include_Paths; -my %RegisteredDirs; -my %Header_ErrorRedirect; -my %Header_Includes; -my %Header_Includes_R; -my %Header_ShouldNotBeUsed; -my %RecursiveIncludes; -my %Header_Include_Prefix; -my %SkipHeaders; -my %SkipHeadersList=( - "1"=>{}, - "2"=>{} ); -my %SkipLibs; -my %Include_Order; -my %TUnit_NameSpaces; -my %TUnit_Classes; -my %TUnit_Funcs; -my %TUnit_Vars; - -my %CppMode = ( - "1"=>0, - "2"=>0 ); -my %AutoPreambleMode = ( - "1"=>0, - "2"=>0 ); -my %MinGWMode = ( - "1"=>0, - "2"=>0 ); -my %Cpp0xMode = ( - "1"=>0, - "2"=>0 ); - -# Shared Objects -my %RegisteredObjects; -my %RegisteredObjects_Short; -my %RegisteredSONAMEs; -my %RegisteredObject_Dirs; - -my %CheckedArch; - -# System Objects -my %SystemObjects; -my @DefaultLibPaths; -my %DyLib_DefaultPath; - -# System Headers -my %SystemHeaders; -my @DefaultCppPaths; -my @DefaultGccPaths; -my @DefaultIncPaths; -my %DefaultCppHeader; -my %DefaultGccHeader; -my @UsersIncPath; # Merging -my %CompleteSignature; -my $Version; +my %CompSign; my %AddedInt; my %RemovedInt; my %AddedInt_Virt; @@ -1442,13 +872,20 @@ my %IncompleteRules; my %UnknownRules; my %VTableChanged_M; -my %ExtendedSymbols; my %ReturnedClass; my %ParamClass; my %SourceAlternative; my %SourceAlternative_B; my %SourceReplacement; -my $CurrentSymbol; # for debugging + +# Extra +my %ExtendedSymbols; + +#Report +my %TypeChanges; + +#Speedup +my %TypeProblemsIndex; # Calling Conventions my %UseConv_Real = ( @@ -1459,38 +896,11 @@ # ABI Dump my %UsedDump; -# Filters -my %TargetLibs; -my %TargetHeaders; - -# Format of objects -my $OStarget = $OSgroup; -my %TargetTools; - -# Compliance Report -my %Type_MaxSeverity; - # Recursion locks -my @RecurLib; my @RecurTypes; my @RecurTypes_Diff; -my @RecurInclude; my @RecurConstant; -# System -my %SystemPaths = ( - "include"=>[], - "lib"=>[], - "bin"=>[] -); -my @DefaultBinPaths; -my $GCC_PATH; - -# Symbols versioning -my %SymVer = ( - "1"=>{}, - "2"=>{} ); - # Problem descriptions my %CompatProblems; my %CompatProblems_Constants; @@ -1499,17 +909,13 @@ # Reports my $ContentID = 1; my $ContentSpanStart = "\n"; -my $ContentSpanStart_Affected = "\n"; -my $ContentSpanStart_Info = "\n"; +my $ContentSpanStart_Affected = "\n"; +my $ContentSpanStart_Info = "\n"; my $ContentSpanEnd = "\n"; my $ContentDivStart = "
\n"; my $ContentDivEnd = "
\n"; my $Content_Counter = 0; -# Modes -my $JoinReport = 1; -my $DoubleReport = 0; - my %Severity_Val=( "High"=>3, "Medium"=>2, @@ -1517,9 +923,9 @@ "Safe"=>-1 ); -sub get_Modules() +sub getModules() { - my $TOOL_DIR = get_dirname($0); + my $TOOL_DIR = dirname($0); if(not $TOOL_DIR) { # patch for MS Windows $TOOL_DIR = "."; @@ -1534,7 +940,7 @@ ); foreach my $DIR (@SEARCH_DIRS) { - if(not is_abs($DIR)) + if($DIR!~/\A(\/|\w+:[\/\\])/) { # relative path $DIR = abs_path($TOOL_DIR)."/".$DIR; } @@ -1542,11 +948,11 @@ return $DIR."/modules"; } } - exitStatus("Module_Error", "can't find modules"); + + print STDERR "ERROR: can't find modules (Did you installed the tool by 'make install' command?)\n"; + exit(9); # Module_Error } -my %LoadedModules = (); - sub loadModule($) { my $Name = $_[0]; @@ -1554,8 +960,10 @@ return; } my $Path = $MODULES_DIR."/Internals/$Name.pm"; - if(not -f $Path) { - exitStatus("Module_Error", "can't access \'$Path\'"); + if(not -f $Path) + { + print STDERR "can't access \'$Path\'\n"; + exit(2); } require $Path; $LoadedModules{$Name} = 1; @@ -1571,5971 +979,427 @@ return readFile($Path); } -sub showPos($) -{ - my $Number = $_[0]; - if(not $Number) { - $Number = 1; - } - else { - $Number = int($Number)+1; - } - if($Number>3) { - return $Number."th"; - } - elsif($Number==1) { - return "1st"; - } - elsif($Number==2) { - return "2nd"; - } - elsif($Number==3) { - return "3rd"; - } - else { - return $Number; - } -} - -sub search_Tools($) +sub selectSymbolNs($$) { - my $Name = $_[0]; - return "" if(not $Name); - if(my @Paths = keys(%TargetTools)) - { - foreach my $Path (@Paths) - { - if(-f join_P($Path, $Name)) { - return join_P($Path, $Name); - } - if($CrossPrefix) - { # user-defined prefix (arm-none-symbianelf, ...) - my $Candidate = join_P($Path, $CrossPrefix."-".$Name); - if(-f $Candidate) { - return $Candidate; - } - } - } - } - else { - return ""; - } -} - -sub synch_Cmd($) -{ - my $Name = $_[0]; - if(not $GCC_PATH) - { # GCC was not found yet - return ""; - } - my $Candidate = $GCC_PATH; - if($Candidate=~s/\bgcc(|\.\w+)\Z/$Name$1/) { - return $Candidate; - } - return ""; -} - -sub get_CmdPath($) -{ - my $Name = $_[0]; - return "" if(not $Name); - if(defined $Cache{"get_CmdPath"}{$Name}) { - return $Cache{"get_CmdPath"}{$Name}; - } - my %BinUtils = map {$_=>1} ( - "c++filt", - "objdump", - "readelf" - ); - if($BinUtils{$Name} and $GCC_PATH) - { - if(my $Dir = get_dirname($GCC_PATH)) { - $TargetTools{$Dir}=1; - } - } - my $Path = search_Tools($Name); - if(not $Path and $OSgroup eq "windows") { - $Path = search_Tools($Name.".exe"); - } - if(not $Path and $BinUtils{$Name}) + my ($Symbol, $LVer) = @_; + + my $NS = $CompSign{$LVer}{$Symbol}{"NameSpace"}; + if(not $NS) { - if($CrossPrefix) - { # user-defined prefix - $Path = search_Cmd($CrossPrefix."-".$Name); + if(my $Class = $CompSign{$LVer}{$Symbol}{"Class"}) { + $NS = $TypeInfo{$LVer}{$Class}{"NameSpace"}; } } - if(not $Path and $BinUtils{$Name}) + if($NS) { - if(my $Candidate = synch_Cmd($Name)) - { # synch with GCC - if($Candidate=~/[\/\\]/) - { # command path - if(-f $Candidate) { - $Path = $Candidate; - } - } - elsif($Candidate = search_Cmd($Candidate)) - { # command name - $Path = $Candidate; - } + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; } - } - if(not $Path) { - $Path = search_Cmd($Name); - } - if(not $Path and $OSgroup eq "windows") - { # search for *.exe file - $Path=search_Cmd($Name.".exe"); - } - if($Path=~/\s/) { - $Path = "\"".$Path."\""; - } - return ($Cache{"get_CmdPath"}{$Name}=$Path); -} - -sub search_Cmd($) -{ - my $Name = $_[0]; - return "" if(not $Name); - if(defined $Cache{"search_Cmd"}{$Name}) { - return $Cache{"search_Cmd"}{$Name}; - } - if(my $DefaultPath = get_CmdPath_Default($Name)) { - return ($Cache{"search_Cmd"}{$Name} = $DefaultPath); - } - foreach my $Path (@{$SystemPaths{"bin"}}) - { - my $CmdPath = join_P($Path,$Name); - if(-f $CmdPath) + else { - if($Name=~/gcc/) { - next if(not check_gcc($CmdPath, "3")); + while($NS=~s/::[^:]+\Z//) + { + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; + } } - return ($Cache{"search_Cmd"}{$Name} = $CmdPath); - } - } - return ($Cache{"search_Cmd"}{$Name} = ""); -} - -sub get_CmdPath_Default($) -{ # search in PATH - return "" if(not $_[0]); - if(defined $Cache{"get_CmdPath_Default"}{$_[0]}) { - return $Cache{"get_CmdPath_Default"}{$_[0]}; - } - return ($Cache{"get_CmdPath_Default"}{$_[0]} = get_CmdPath_Default_I($_[0])); -} - -sub get_CmdPath_Default_I($) -{ # search in PATH - my $Name = $_[0]; - if($Name=~/find/) - { # special case: search for "find" utility - if(`find \"$TMP_DIR\" -maxdepth 0 2>\"$TMP_DIR/null\"`) { - return "find"; - } - } - elsif($Name=~/gcc/) { - return check_gcc($Name, "3"); - } - if(checkCmd($Name)) { - return $Name; - } - if($OSgroup eq "windows") - { - if(`$Name /? 2>\"$TMP_DIR/null\"`) { - return $Name; - } - } - foreach my $Path (@DefaultBinPaths) - { - if(-f $Path."/".$Name) { - return join_P($Path, $Name); } } + return ""; } -sub classifyPath($) -{ - my $Path = $_[0]; - if($Path=~/[\*\[]/) - { # wildcard - $Path=~s/\*/.*/g; - $Path=~s/\\/\\\\/g; - return ($Path, "Pattern"); - } - elsif($Path=~/[\/\\]/) - { # directory or relative path - return (path_format($Path, $OSgroup), "Path"); - } - else { - return ($Path, "Name"); - } -} - -sub readDescriptor($$) +sub selectTypeNs($$) { - my ($LibVersion, $Content) = @_; - return if(not $LibVersion); - my $DName = $DumpAPI?"descriptor":"descriptor \"d$LibVersion\""; - if(not $Content) { - exitStatus("Error", "$DName is empty"); - } - if($Content!~/\//g; + my ($TypeName, $LVer) = @_; - $Descriptor{$LibVersion}{"Version"} = parseTag(\$Content, "version"); - if($TargetVersion{$LibVersion}) { - $Descriptor{$LibVersion}{"Version"} = $TargetVersion{$LibVersion}; - } - if(not $Descriptor{$LibVersion}{"Version"}) { - exitStatus("Error", "version in the $DName is not specified ( section)"); - } - if($Content=~/{RELPATH}/) + my $Tid = $TName_Tid{$LVer}{$TypeName}; + + if(my $NS = $TypeInfo{$LVer}{$Tid}{"NameSpace"}) { - if(my $RelDir = $RelativeDirectory{$LibVersion}) { - $Content =~ s/{RELPATH}/$RelDir/g; + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; } else { - my $NeedRelpath = $DumpAPI?"-relpath":"-relpath$LibVersion"; - exitStatus("Error", "you have not specified $NeedRelpath option, but the $DName contains {RELPATH} macro"); - } - } - - my $DHeaders = parseTag(\$Content, "headers"); - if(not $DHeaders) { - exitStatus("Error", "header files in the $DName are not specified ( section)"); - } - elsif(lc($DHeaders) ne "none") - { # append the descriptor headers list - if($Descriptor{$LibVersion}{"Headers"}) - { # multiple descriptors - $Descriptor{$LibVersion}{"Headers"} .= "\n".$DHeaders; - } - else { - $Descriptor{$LibVersion}{"Headers"} = $DHeaders; - } - foreach my $Path (split(/\s*\n\s*/, $DHeaders)) - { - if(not -e $Path) { - exitStatus("Access_Error", "can't access \'$Path\'"); - } - } - } - - if(not $CheckHeadersOnly_Opt) - { - my $DObjects = parseTag(\$Content, "libs"); - if(not $DObjects) { - exitStatus("Error", "$SLIB_TYPE libraries in the $DName are not specified ( section)"); - } - elsif(lc($DObjects) ne "none") - { # append the descriptor libraries list - if($Descriptor{$LibVersion}{"Libs"}) - { # multiple descriptors - $Descriptor{$LibVersion}{"Libs"} .= "\n".$DObjects; - } - else { - $Descriptor{$LibVersion}{"Libs"} .= $DObjects; - } - foreach my $Path (split(/\s*\n\s*/, $DObjects)) + while($NS=~s/::[^:]+\Z//) { - if(not -e $Path) { - exitStatus("Access_Error", "can't access \'$Path\'"); + if(defined $In::ABI{$LVer}{"NameSpaces"}{$NS}) { + return $NS; } } } } - foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers"))) - { - if(not -d $Path) { - exitStatus("Access_Error", "can't access directory \'$Path\'"); - } - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - push_U($SystemPaths{"include"}, $Path); - } - foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs"))) - { - if(not -d $Path) { - exitStatus("Access_Error", "can't access directory \'$Path\'"); - } - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - push_U($SystemPaths{"lib"}, $Path); - } - foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools"))) - { - if(not -d $Path) { - exitStatus("Access_Error", "can't access directory \'$Path\'"); - } - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - push_U($SystemPaths{"bin"}, $Path); - $TargetTools{$Path}=1; - } - if(my $Prefix = parseTag(\$Content, "cross_prefix")) { - $CrossPrefix = $Prefix; - } - $Descriptor{$LibVersion}{"IncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"IncludePaths"}); # perl 5.8 doesn't support //= - foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "include_paths"))) - { - if(not -d $Path) { - exitStatus("Access_Error", "can't access directory \'$Path\'"); - } - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - push(@{$Descriptor{$LibVersion}{"IncludePaths"}}, $Path); - } - $Descriptor{$LibVersion}{"AddIncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"AddIncludePaths"}); - foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "add_include_paths"))) - { - if(not -d $Path) { - exitStatus("Access_Error", "can't access directory \'$Path\'"); - } - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - push(@{$Descriptor{$LibVersion}{"AddIncludePaths"}}, $Path); - } - foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_include_paths"))) - { # skip some auto-generated include paths - if(not is_abs($Path)) - { - if(my $P = abs_path($Path)) { - $Path = $P; - } - } - $Skip_Include_Paths{$LibVersion}{path_format($Path)} = 1; - } - foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_including"))) - { # skip direct including of some headers - my ($CPath, $Type) = classifyPath($Path); - $SkipHeaders{$LibVersion}{$Type}{$CPath} = 2; - } - $Descriptor{$LibVersion}{"GccOptions"} = parseTag(\$Content, "gcc_options"); - foreach my $Option (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"GccOptions"})) - { - if($Option!~/\A\-(Wl|l|L)/) - { # skip linker options - $CompilerOptions{$LibVersion} .= " ".$Option; - } - } - $Descriptor{$LibVersion}{"SkipHeaders"} = parseTag(\$Content, "skip_headers"); - foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipHeaders"})) - { - $SkipHeadersList{$LibVersion}{$Path} = 1; - my ($CPath, $Type) = classifyPath($Path); - $SkipHeaders{$LibVersion}{$Type}{$CPath} = 1; - } - $Descriptor{$LibVersion}{"SkipLibs"} = parseTag(\$Content, "skip_libs"); - foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipLibs"})) - { - my ($CPath, $Type) = classifyPath($Path); - $SkipLibs{$LibVersion}{$Type}{$CPath} = 1; - } - if(my $DDefines = parseTag(\$Content, "defines")) - { - if($Descriptor{$LibVersion}{"Defines"}) - { # multiple descriptors - $Descriptor{$LibVersion}{"Defines"} .= "\n".$DDefines; - } - else { - $Descriptor{$LibVersion}{"Defines"} = $DDefines; - } - } - foreach my $Order (split(/\s*\n\s*/, parseTag(\$Content, "include_order"))) - { - if($Order=~/\A(.+):(.+)\Z/) { - $Include_Order{$LibVersion}{$1} = $2; - } - } - foreach my $Type_Name (split(/\s*\n\s*/, parseTag(\$Content, "opaque_types")), - split(/\s*\n\s*/, parseTag(\$Content, "skip_types"))) - { # opaque_types renamed to skip_types (1.23.4) - $SkipTypes{$LibVersion}{$Type_Name} = 1; - } - foreach my $Symbol (split(/\s*\n\s*/, parseTag(\$Content, "skip_interfaces")), - split(/\s*\n\s*/, parseTag(\$Content, "skip_symbols"))) - { # skip_interfaces renamed to skip_symbols (1.22.1) - $SkipSymbols{$LibVersion}{$Symbol} = 1; - } - foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "skip_namespaces"))) { - $SkipNameSpaces{$LibVersion}{$NameSpace} = 1; - } - foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "add_namespaces"))) { - $AddNameSpaces{$LibVersion}{$NameSpace} = 1; - } - foreach my $Constant (split(/\s*\n\s*/, parseTag(\$Content, "skip_constants"))) { - $SkipConstants{$LibVersion}{$Constant} = 1; - } - if(my $DIncPreamble = parseTag(\$Content, "include_preamble")) - { - if($Descriptor{$LibVersion}{"IncludePreamble"}) - { # multiple descriptors - $Descriptor{$LibVersion}{"IncludePreamble"} .= "\n".$DIncPreamble; - } - else { - $Descriptor{$LibVersion}{"IncludePreamble"} = $DIncPreamble; - } - } + return ""; } -sub parseTag(@) +sub getChargeLevel($$) { - my $CodeRef = shift(@_); - my $Tag = shift(@_); - if(not $Tag or not $CodeRef) { - return undef; - } - my $Sp = 0; - if(@_) { - $Sp = shift(@_); - } - my $Start = index(${$CodeRef}, "<$Tag>"); - if($Start!=-1) + my ($Symbol, $LVer) = @_; + + if(defined $CompSign{$LVer}{$Symbol} + and $CompSign{$LVer}{$Symbol}{"ShortName"}) { - my $End = index(${$CodeRef}, ""); - if($End!=-1) + if($CompSign{$LVer}{$Symbol}{"Constructor"}) { - my $TS = length($Tag)+3; - my $Content = substr(${$CodeRef}, $Start, $End-$Start+$TS, ""); - substr($Content, 0, $TS-1, ""); # cut start tag - substr($Content, -$TS, $TS, ""); # cut end tag - if(not $Sp) - { - $Content=~s/\A\s+//g; - $Content=~s/\s+\Z//g; + if($Symbol=~/C([1-4])[EI]/) + { # [in-charge] + # [not-in-charge] + return "[C".$1."]"; } - if(substr($Content, 0, 1) ne "<") { - $Content = xmlSpecChars_R($Content); + } + elsif($CompSign{$LVer}{$Symbol}{"Destructor"}) + { + if($Symbol=~/D([0-4])[EI]/) + { # [in-charge] + # [not-in-charge] + # [in-charge-deleting] + return "[D".$1."]"; } - return $Content; } } + return undef; } -sub getInfo($) +sub blackName($) { - my $DumpPath = $_[0]; - return if(not $DumpPath or not -f $DumpPath); - - readTUDump($DumpPath); - - # processing info - setTemplateParams_All(); - - if($ExtraDump) { - setAnonTypedef_All(); - } - - getTypeInfo_All(); - simplifyNames(); - simplifyConstants(); - getVarInfo_All(); - getSymbolInfo_All(); - - # clean memory - %LibInfo = (); - %TemplateInstance = (); - %BasicTemplate = (); - %MangledNames = (); - %TemplateDecl = (); - %StdCxxTypedef = (); - %MissedTypedef = (); - %Typedef_Tr = (); - %Typedef_Eq = (); - %TypedefToAnon = (); - - # clean cache - delete($Cache{"getTypeAttr"}); - delete($Cache{"getTypeDeclId"}); - - if($ExtraDump) - { - remove_Unused($Version, "Extra"); - } - else - { # remove unused types - if($BinaryOnly and not $ExtendedCheck) - { # --binary - remove_Unused($Version, "All"); - } - else { - remove_Unused($Version, "Extended"); - } - } - - if($CheckInfo) - { - foreach my $Tid (keys(%{$TypeInfo{$Version}})) { - check_Completeness($TypeInfo{$Version}{$Tid}, $Version); - } - - foreach my $Sid (keys(%{$SymbolInfo{$Version}})) { - check_Completeness($SymbolInfo{$Version}{$Sid}, $Version); - } - } - - if($Debug) { - # debugMangling($Version); - } + my $N = $_[0]; + return "".$N.""; } -sub readTUDump($) +sub highLight_ItalicColor($$) { - my $DumpPath = $_[0]; - - open(TU_DUMP, $DumpPath); - local $/ = undef; - my $Content = ; - close(TU_DUMP); - - unlink($DumpPath); - - $Content=~s/\n[ ]+/ /g; - my @Lines = split(/\n/, $Content); - - # clean memory - undef $Content; - - $MAX_ID = $#Lines+1; # number of lines == number of nodes - - foreach (0 .. $#Lines) - { - if($Lines[$_]=~/\A\@(\d+)[ ]+([a-z_]+)[ ]+(.+)\Z/i) - { # get a number and attributes of a node - next if(not $NodeType{$2}); - $LibInfo{$Version}{"info_type"}{$1}=$2; - $LibInfo{$Version}{"info"}{$1}=$3; - } - - # clean memory - delete($Lines[$_]); - } - - # clean memory - undef @Lines; + my ($Symbol, $LVer) = @_; + return getSignature($Symbol, $LVer, "Full|HTML|Italic|Color"); } -sub simplifyConstants() +sub getSignature($$$) { - foreach my $Constant (keys(%{$Constants{$Version}})) - { - if(defined $Constants{$Version}{$Constant}{"Header"}) - { - my $Value = $Constants{$Version}{$Constant}{"Value"}; - if(defined $EnumConstants{$Version}{$Value}) { - $Constants{$Version}{$Constant}{"Value"} = $EnumConstants{$Version}{$Value}{"Value"}; - } - } + my ($Symbol, $LVer, $Kind) = @_; + if($Cache{"getSignature"}{$LVer}{$Symbol}{$Kind}) { + return $Cache{"getSignature"}{$LVer}{$Symbol}{$Kind}; } -} - -sub simplifyNames() -{ - foreach my $Base (keys(%{$Typedef_Tr{$Version}})) - { - if($Typedef_Eq{$Version}{$Base}) { - next; - } - my @Translations = sort keys(%{$Typedef_Tr{$Version}{$Base}}); - if($#Translations==0) - { - if(length($Translations[0])<=length($Base)) { - $Typedef_Eq{$Version}{$Base} = $Translations[0]; - } - } - else - { # select most appropriate - foreach my $Tr (@Translations) - { - if($Base=~/\A\Q$Tr\E/) - { - $Typedef_Eq{$Version}{$Base} = $Tr; - last; - } - } - } + + # settings + my ($Html, $Simple, $Italic, $Color, $Full, $ShowClass, $ShowName, + $ShowParams, $ShowQuals, $ShowAttr, $Desc, $Target) = (); + + if($Kind=~/HTML/) { + $Html = 1; } - foreach my $TypeId (keys(%{$TypeInfo{$Version}})) - { - my $TypeName = $TypeInfo{$Version}{$TypeId}{"Name"}; - if(not $TypeName) { - next; - } - next if(index($TypeName,"<")==-1);# template instances only - if($TypeName=~/>(::\w+)+\Z/) - { # skip unused types - next; - } - foreach my $Base (sort {length($b)<=>length($a)} - sort {$b cmp $a} keys(%{$Typedef_Eq{$Version}})) - { - next if(not $Base); - next if(index($TypeName,$Base)==-1); - next if(length($TypeName) - length($Base) <= 3); - if(my $Typedef = $Typedef_Eq{$Version}{$Base}) - { - $TypeName=~s/(\<|\,)\Q$Base\E(\W|\Z)/$1$Typedef$2/g; - $TypeName=~s/(\<|\,)\Q$Base\E(\w|\Z)/$1$Typedef $2/g; - if(defined $TypeInfo{$Version}{$TypeId}{"TParam"}) - { - foreach my $TPos (keys(%{$TypeInfo{$Version}{$TypeId}{"TParam"}})) - { - if(my $TPName = $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"}) - { - $TPName=~s/\A\Q$Base\E(\W|\Z)/$Typedef$1/g; - $TPName=~s/\A\Q$Base\E(\w|\Z)/$Typedef $1/g; - $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"} = formatName($TPName, "T"); - } - } - } - } - } - $TypeName = formatName($TypeName, "T"); - $TypeInfo{$Version}{$TypeId}{"Name"} = $TypeName; - $TName_Tid{$Version}{$TypeName} = $TypeId; + if($Kind=~/Simple/) { + $Simple = 1; } -} - -sub setAnonTypedef_All() -{ - foreach my $InfoId (keys(%{$LibInfo{$Version}{"info"}})) - { - if($LibInfo{$Version}{"info_type"}{$InfoId} eq "type_decl") - { - if(isAnon(getNameByInfo($InfoId))) { - $TypedefToAnon{getTypeId($InfoId)} = 1; - } - } + if($Kind=~/Italic/) { + $Italic = 1; } -} - -sub setTemplateParams_All() -{ - foreach (keys(%{$LibInfo{$Version}{"info"}})) - { - if($LibInfo{$Version}{"info_type"}{$_} eq "template_decl") { - setTemplateParams($_); - } + if($Kind=~/Color/) { + $Color = 1; } -} - -sub setTemplateParams($) -{ - my $Tid = getTypeId($_[0]); - if(my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/(inst|spcs)[ ]*:[ ]*@(\d+) /) - { - my $TmplInst_Id = $2; - setTemplateInstParams($_[0], $TmplInst_Id); - while($TmplInst_Id = getNextElem($TmplInst_Id)) { - setTemplateInstParams($_[0], $TmplInst_Id); - } - } - - $BasicTemplate{$Version}{$Tid} = $_[0]; - - if(my $Prms = getTreeAttr_Prms($_[0])) - { - if(my $Valu = getTreeAttr_Valu($Prms)) - { - my $Vector = getTreeVec($Valu); - foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Vector})) - { - if(my $Val = getTreeAttr_Valu($Vector->{$Pos})) - { - if(my $Name = getNameByInfo($Val)) - { - $TemplateArg{$Version}{$_[0]}{$Pos} = $Name; - if($LibInfo{$Version}{"info_type"}{$Val} eq "parm_decl") { - $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = $Val; - } - else { - $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = getTreeAttr_Type($Val); - } - } - } - } - } - } + + if($Kind=~/Full/) { + $Full = 1; } - if(my $TypeId = getTreeAttr_Type($_[0])) - { - if(my $IType = $LibInfo{$Version}{"info_type"}{$TypeId}) - { - if($IType eq "record_type") { - $TemplateDecl{$Version}{$TypeId} = 1; - } - } + if($Kind=~/Class/) { + $ShowClass = 1; } -} - -sub setTemplateInstParams($$) -{ - my ($Tmpl, $Inst) = @_; - - if(my $Info = $LibInfo{$Version}{"info"}{$Inst}) - { - my ($Params_InfoId, $ElemId) = (); - if($Info=~/purp[ ]*:[ ]*@(\d+) /) { - $Params_InfoId = $1; - } - if($Info=~/valu[ ]*:[ ]*@(\d+) /) { - $ElemId = $1; - } - if($Params_InfoId and $ElemId) - { - my $Params_Info = $LibInfo{$Version}{"info"}{$Params_InfoId}; - while($Params_Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /) - { - my ($PPos, $PTypeId) = ($1, $2); - if(my $PType = $LibInfo{$Version}{"info_type"}{$PTypeId}) - { - if($PType eq "template_type_parm") { - $TemplateDecl{$Version}{$ElemId} = 1; - } - } - if($LibInfo{$Version}{"info_type"}{$ElemId} eq "function_decl") - { # functions - $TemplateInstance{$Version}{"Func"}{$ElemId}{$PPos} = $PTypeId; - $BasicTemplate{$Version}{$ElemId} = $Tmpl; - } - else - { # types - $TemplateInstance{$Version}{"Type"}{$ElemId}{$PPos} = $PTypeId; - $BasicTemplate{$Version}{$ElemId} = $Tmpl; - } - } - } + if($Kind=~/Name/) { + $ShowName = 1; } -} - -sub getTypeDeclId($) -{ - if($_[0]) - { - if(defined $Cache{"getTypeDeclId"}{$Version}{$_[0]}) { - return $Cache{"getTypeDeclId"}{$Version}{$_[0]}; - } - if(my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/name[ ]*:[ ]*@(\d+)/) { - return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = $1); - } - } + if($Kind=~/Param/) { + $ShowParams = 1; } - return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = 0); -} - -sub getTypeInfo_All() -{ - if(not check_gcc($GCC_PATH, "4.5")) - { # support for GCC < 4.5 - # missed typedefs: QStyle::State is typedef to QFlags - # but QStyleOption.state is of type QFlags in the TU dump - # FIXME: check GCC versions - addMissedTypes_Pre(); + if($Kind=~/Qual/) { + $ShowQuals = 1; } - - foreach (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}})) - { # forward order only - my $IType = $LibInfo{$Version}{"info_type"}{$_}; - if($IType=~/_type\Z/ and $IType ne "function_type" - and $IType ne "method_type") { - getTypeInfo("$_"); - } + if($Kind=~/Attr/) { + $ShowAttr = 1; + } + if($Kind=~/Desc/) { + $Desc = 1; } - # add "..." type - $TypeInfo{$Version}{"-1"} = { - "Name" => "...", - "Type" => "Intrinsic", - "Tid" => "-1" - }; - $TName_Tid{$Version}{"..."} = "-1"; - - if(not check_gcc($GCC_PATH, "4.5")) - { # support for GCC < 4.5 - addMissedTypes_Post(); + if($Kind=~/Target=(\d+)/) { + $Target = $1; } - if($ADD_TMPL_INSTANCES) + if($Full) { - # templates - foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}})) - { - if(defined $TemplateMap{$Version}{$Tid} - and not defined $TypeInfo{$Version}{$Tid}{"Template"}) - { - if(defined $TypeInfo{$Version}{$Tid}{"Memb"}) - { - foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Memb"}})) - { - if(my $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"}) - { - if(my %MAttr = getTypeAttr($MembTypeId)) - { - $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"algn"} = $MAttr{"Algn"}; - $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"} = instType($TemplateMap{$Version}{$Tid}, $MembTypeId, $Version); - } - } - } - } - if(defined $TypeInfo{$Version}{$Tid}{"Base"}) - { - foreach my $Bid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Base"}})) - { - my $NBid = instType($TemplateMap{$Version}{$Tid}, $Bid, $Version); - - if($NBid ne $Bid) - { - %{$TypeInfo{$Version}{$Tid}{"Base"}{$NBid}} = %{$TypeInfo{$Version}{$Tid}{"Base"}{$Bid}}; - delete($TypeInfo{$Version}{$Tid}{"Base"}{$Bid}); - } - } - } - } - } + $ShowName = 1; + $ShowClass = 1; } -} - -sub createType($$) -{ - my ($Attr, $LibVersion) = @_; - my $NewId = ++$MAX_ID; - - $Attr->{"Tid"} = $NewId; - $TypeInfo{$Version}{$NewId} = $Attr; - $TName_Tid{$Version}{formatName($Attr->{"Name"}, "T")} = $NewId; - - return "$NewId"; -} - -sub instType($$$) -{ # create template instances - my ($Map, $Tid, $LibVersion) = @_; - if(not $TypeInfo{$LibVersion}{$Tid}) { - return undef; - } - my $Attr = dclone($TypeInfo{$LibVersion}{$Tid}); + my ($MnglName, $VSpec, $SVer) = symbolParts($Symbol); - foreach my $Key (sort keys(%{$Map})) + if(index($Symbol, "_ZTV")==0) { - if(my $Val = $Map->{$Key}) + if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"}) { - $Attr->{"Name"}=~s/\b$Key\b/$Val/g; + my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + $ClassName=~s/\bstruct //g; - if(defined $Attr->{"NameSpace"}) { - $Attr->{"NameSpace"}=~s/\b$Key\b/$Val/g; - } - foreach (keys(%{$Attr->{"TParam"}})) { - $Attr->{"TParam"}{$_}{"name"}=~s/\b$Key\b/$Val/g; + if($Html) { + return "vtable for ".specChars($ClassName)." [data]"; } + + return "vtable for $ClassName [data]"; } else - { # remove absent - # _Traits, etc. - $Attr->{"Name"}=~s/,\s*\b$Key(,|>)/$1/g; - if(defined $Attr->{"NameSpace"}) { - $Attr->{"NameSpace"}=~s/,\s*\b$Key(,|>)/$1/g; - } - foreach (keys(%{$Attr->{"TParam"}})) - { - if($Attr->{"TParam"}{$_}{"name"} eq $Key) { - delete($Attr->{"TParam"}{$_}); - } - else { - $Attr->{"TParam"}{$_}{"name"}=~s/,\s*\b$Key(,|>)/$1/g; - } - } + { # failure + return undef; } } - my $Tmpl = 0; - - if(defined $Attr->{"TParam"}) - { - foreach (sort {int($a)<=>int($b)} keys(%{$Attr->{"TParam"}})) - { - my $PName = $Attr->{"TParam"}{$_}{"name"}; - - if(my $PTid = $TName_Tid{$LibVersion}{$PName}) - { - my %Base = get_BaseType($PTid, $LibVersion); - - if($Base{"Type"} eq "TemplateParam" - or defined $Base{"Template"}) - { - $Tmpl = 1; - last - } - } - } - } + my $Mngl = (index($Symbol, "_Z")==0 or index($Symbol, "?")==0); - if(my $Id = getTypeIdByName($Attr->{"Name"}, $LibVersion)) { - return "$Id"; - } - else + my $Signature = ""; + if($ShowName) { - if(not $Tmpl) { - delete($Attr->{"Template"}); - } - - my $New = createType($Attr, $LibVersion); - - my %EMap = (); - if(defined $TemplateMap{$LibVersion}{$Tid}) { - %EMap = %{$TemplateMap{$LibVersion}{$Tid}}; - } - foreach (keys(%{$Map})) { - $EMap{$_} = $Map->{$_}; - } - - if(defined $TypeInfo{$LibVersion}{$New}{"BaseType"}) { - $TypeInfo{$LibVersion}{$New}{"BaseType"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"BaseType"}, $LibVersion); - } - if(defined $TypeInfo{$LibVersion}{$New}{"Base"}) - { - foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$New}{"Base"}})) - { - my $NBid = instType(\%EMap, $Bid, $LibVersion); - - if($NBid ne $Bid) - { - %{$TypeInfo{$LibVersion}{$New}{"Base"}{$NBid}} = %{$TypeInfo{$LibVersion}{$New}{"Base"}{$Bid}}; - delete($TypeInfo{$LibVersion}{$New}{"Base"}{$Bid}); - } - } - } - - if(defined $TypeInfo{$LibVersion}{$New}{"Memb"}) - { - foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Memb"}})) - { - if(defined $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}) { - $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}, $LibVersion); - } - } - } + my $ShortName = $CompSign{$LVer}{$Symbol}{"ShortName"}; - if(defined $TypeInfo{$LibVersion}{$New}{"Param"}) - { - foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Param"}})) { - $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"}, $LibVersion); - } + if(not $Mngl and defined $CompSign{$LVer}{$Symbol}{"Alias"}) + { # alias symbol + $ShortName = $MnglName; } - if(defined $TypeInfo{$LibVersion}{$New}{"Return"}) { - $TypeInfo{$LibVersion}{$New}{"Return"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Return"}, $LibVersion); + if($Html) { + $ShortName = specChars($ShortName); } - return $New; - } -} - -sub addMissedTypes_Pre() -{ - my %MissedTypes = (); - foreach my $MissedTDid (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}})) - { # detecting missed typedefs - if($LibInfo{$Version}{"info_type"}{$MissedTDid} eq "type_decl") - { - my $TypeId = getTreeAttr_Type($MissedTDid); - next if(not $TypeId); - my $TypeType = getTypeType($TypeId); - if($TypeType eq "Unknown") - { # template_type_parm - next; - } - my $TypeDeclId = getTypeDeclId($TypeId); - next if($TypeDeclId eq $MissedTDid);#or not $TypeDeclId - my $TypedefName = getNameByInfo($MissedTDid); - next if(not $TypedefName); - next if($TypedefName eq "__float80"); - next if(isAnon($TypedefName)); - if(not $TypeDeclId - or getNameByInfo($TypeDeclId) ne $TypedefName) { - $MissedTypes{$Version}{$TypeId}{$MissedTDid} = 1; - } - } - } - my %AddTypes = (); - foreach my $Tid (keys(%{$MissedTypes{$Version}})) - { # add missed typedefs - my @Missed = keys(%{$MissedTypes{$Version}{$Tid}}); - if(not @Missed or $#Missed>=1) { - next; - } - my $MissedTDid = $Missed[0]; - my ($TypedefName, $TypedefNS) = getTrivialName($MissedTDid, $Tid); - if(not $TypedefName) { - next; - } - my $NewId = ++$MAX_ID; - my %MissedInfo = ( # typedef info - "Name" => $TypedefName, - "NameSpace" => $TypedefNS, - "BaseType" => $Tid, - "Type" => "Typedef", - "Tid" => "$NewId" ); - my ($H, $L) = getLocation($MissedTDid); - $MissedInfo{"Header"} = $H; - $MissedInfo{"Line"} = $L; - if($TypedefName=~/\*|\&|\s/) - { # other types - next; - } - if($TypedefName=~/>(::\w+)+\Z/) - { # QFlags::enum_type - next; - } - if(getTypeType($Tid)=~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/) - { # double-check for the name of typedef - my ($TName, $TNS) = getTrivialName(getTypeDeclId($Tid), $Tid); # base type info - next if(not $TName); - if(length($TypedefName)>=length($TName)) - { # too long typedef - next; - } - if($TName=~/\A\Q$TypedefName\Eint($b)} keys(%{$TemplateInstance{$Version}{$Kind}{$TypeId}}); - foreach my $Pos (@Positions) - { - my $Param_TypeId = $TemplateInstance{$Version}{$Kind}{$TypeId}{$Pos}; - my $NodeType = $LibInfo{$Version}{"info_type"}{$Param_TypeId}; - if(not $NodeType) - { # typename_type - return (); - } - if($NodeType eq "tree_vec") - { - if($Pos!=$#Positions) - { # select last vector of parameters ( ns::type ) - next; - } - } - my @Params = get_TemplateParam($Pos, $Param_TypeId); - foreach my $P (@Params) - { - if($P eq "") { - return (); - } - elsif($P ne "\@skip\@") { - @TmplParams = (@TmplParams, $P); - } - } - } - return @TmplParams; -} - -sub getTypeAttr($) -{ - my $TypeId = $_[0]; - my %TypeAttr = (); - if(defined $TypeInfo{$Version}{$TypeId} - and $TypeInfo{$Version}{$TypeId}{"Name"}) - { # already created - return %{$TypeInfo{$Version}{$TypeId}}; - } - elsif($Cache{"getTypeAttr"}{$Version}{$TypeId}) - { # incomplete type - return (); - } - $Cache{"getTypeAttr"}{$Version}{$TypeId} = 1; - - my $TypeDeclId = getTypeDeclId($TypeId); - $TypeAttr{"Tid"} = $TypeId; - - if(not $MissedBase{$Version}{$TypeId} and isTypedef($TypeId)) - { - if(my $Info = $LibInfo{$Version}{"info"}{$TypeId}) - { - if($Info=~/qual[ ]*:/) - { - my $NewId = ++$MAX_ID; - - $MissedBase{$Version}{$TypeId} = "$NewId"; - $MissedBase_R{$Version}{$NewId} = $TypeId; - $LibInfo{$Version}{"info"}{$NewId} = $LibInfo{$Version}{"info"}{$TypeId}; - $LibInfo{$Version}{"info_type"}{$NewId} = $LibInfo{$Version}{"info_type"}{$TypeId}; - } - } - $TypeAttr{"Type"} = "Typedef"; - } - else { - $TypeAttr{"Type"} = getTypeType($TypeId); - } - - if(my $ScopeId = getTreeAttr_Scpe($TypeDeclId)) - { - if($LibInfo{$Version}{"info_type"}{$ScopeId} eq "function_decl") - { # local code - return (); - } - } - - if($TypeAttr{"Type"} eq "Unknown") { - return (); - } - elsif($TypeAttr{"Type"}=~/(Func|Method|Field)Ptr/) - { - %TypeAttr = getMemPtrAttr(pointTo($TypeId), $TypeId, $TypeAttr{"Type"}); - if(my $TName = $TypeAttr{"Name"}) - { - %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr; - $TName_Tid{$Version}{$TName} = $TypeId; - return %TypeAttr; - } - else { - return (); - } - } - elsif($TypeAttr{"Type"} eq "Array") - { - my ($BTid, $BTSpec) = selectBaseType($TypeId); - if(not $BTid) { - return (); - } - if(my $Algn = getAlgn($TypeId)) { - $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE; - } - $TypeAttr{"BaseType"} = $BTid; - if(my %BTAttr = getTypeAttr($BTid)) - { - if(not $BTAttr{"Name"}) { - return (); - } - if(my $NElems = getArraySize($TypeId, $BTAttr{"Name"})) - { - if(my $Size = getSize($TypeId)) { - $TypeAttr{"Size"} = $Size/$BYTE_SIZE; - } - if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) { - $TypeAttr{"Name"} = $1."[$NElems]".$2; - } - else { - $TypeAttr{"Name"} = $BTAttr{"Name"}."[$NElems]"; - } - } - else - { - $TypeAttr{"Size"} = $WORD_SIZE{$Version}; # pointer - if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) { - $TypeAttr{"Name"} = $1."[]".$2; - } - else { - $TypeAttr{"Name"} = $BTAttr{"Name"}."[]"; - } - } - $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T"); - if($BTAttr{"Header"}) { - $TypeAttr{"Header"} = $BTAttr{"Header"}; - } - %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr; - $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId; - return %TypeAttr; - } - return (); - } - elsif($TypeAttr{"Type"}=~/\A(Intrinsic|Union|Struct|Enum|Class|Vector)\Z/) - { - %TypeAttr = getTrivialTypeAttr($TypeId); - if($TypeAttr{"Name"}) - { - %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr; - if(not defined $IntrinsicNames{$TypeAttr{"Name"}} - or getTypeDeclId($TypeAttr{"Tid"})) - { # NOTE: register only one int: with built-in decl - if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) { - $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId; - } - } - return %TypeAttr; - } - else { - return (); - } - } - elsif($TypeAttr{"Type"}=~/TemplateParam|TypeName/) - { - %TypeAttr = getTrivialTypeAttr($TypeId); - if($TypeAttr{"Name"}) - { - %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr; - if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) { - $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId; - } - return %TypeAttr; - } - else { - return (); - } - } - elsif($TypeAttr{"Type"} eq "SizeOf") - { - $TypeAttr{"BaseType"} = getTreeAttr_Type($TypeId); - my %BTAttr = getTypeAttr($TypeAttr{"BaseType"}); - $TypeAttr{"Name"} = "sizeof(".$BTAttr{"Name"}.")"; - if($TypeAttr{"Name"}) - { - %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr; - return %TypeAttr; - } - else { - return (); - } - } - else - { # derived types - my ($BTid, $BTSpec) = selectBaseType($TypeId); - if(not $BTid) { - return (); - } - $TypeAttr{"BaseType"} = $BTid; - if(defined $MissedTypedef{$Version}{$BTid}) - { - if(my $MissedTDid = $MissedTypedef{$Version}{$BTid}{"TDid"}) - { - if($MissedTDid ne $TypeDeclId) { - $TypeAttr{"BaseType"} = $MissedTypedef{$Version}{$BTid}{"Tid"}; - } - } - } - my %BTAttr = getTypeAttr($TypeAttr{"BaseType"}); - if(not $BTAttr{"Name"}) - { # templates - return (); - } - if($BTAttr{"Type"} eq "Typedef") - { # relinking typedefs - my %BaseBase = get_Type($BTAttr{"BaseType"}, $Version); - if($BTAttr{"Name"} eq $BaseBase{"Name"}) { - $TypeAttr{"BaseType"} = $BaseBase{"Tid"}; - } - } - if($BTSpec) - { - if($TypeAttr{"Type"} eq "Pointer" - and $BTAttr{"Name"}=~/\([\*]+\)/) - { - $TypeAttr{"Name"} = $BTAttr{"Name"}; - $TypeAttr{"Name"}=~s/\(([*]+)\)/($1*)/g; - } - else { - $TypeAttr{"Name"} = $BTAttr{"Name"}." ".$BTSpec; - } - } - else { - $TypeAttr{"Name"} = $BTAttr{"Name"}; - } - if($TypeAttr{"Type"} eq "Typedef") - { - $TypeAttr{"Name"} = getNameByInfo($TypeDeclId); - - if(index($TypeAttr{"Name"}, "tmp_add_type")==0) { - return (); - } - - if(isAnon($TypeAttr{"Name"})) - { # anon typedef to anon type: ._N - return (); - } - - if($LibInfo{$Version}{"info"}{$TypeDeclId}=~/ artificial /i) - { # artificial typedef of "struct X" to "X" - $TypeAttr{"Artificial"} = 1; - } - - if(my $NS = getNameSpace($TypeDeclId)) + if($ShowClass) { - my $TypeName = $TypeAttr{"Name"}; - if($NS=~/\A(struct |union |class |)((.+)::|)\Q$TypeName\E\Z/) - { # "some_type" is the typedef to "struct some_type" in C++ - if($3) { - $TypeAttr{"Name"} = $3."::".$TypeName; - } - } - else + if($ClassId) { - $TypeAttr{"NameSpace"} = $NS; - $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"}; + my $Class = $TypeInfo{$LVer}{$ClassId}{"Name"}; + $Class=~s/\bstruct //g; - if($TypeAttr{"NameSpace"}=~/\Astd(::|\Z)/ - and $TypeAttr{"Name"}!~/>(::\w+)+\Z/) - { - if($BTAttr{"NameSpace"} - and $BTAttr{"NameSpace"}=~/\Astd(::|\Z)/ and $BTAttr{"Name"}=~/" are - # not covered by typedefs in the TU dump - # so trying to add such typedefs manually - $StdCxxTypedef{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1; - if(length($TypeAttr{"Name"})<=length($BTAttr{"Name"})) - { - if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) - { # skip "other" in "std" and "type" in "boost" - $Typedef_Eq{$Version}{$BTAttr{"Name"}} = $TypeAttr{"Name"}; - } - } - } - } - } - } - if($TypeAttr{"Name"} ne $BTAttr{"Name"} and not $TypeAttr{"Artificial"} - and $TypeAttr{"Name"}!~/>(::\w+)+\Z/ and $BTAttr{"Name"}!~/>(::\w+)+\Z/) - { - if(not defined $Typedef_BaseName{$Version}{$TypeAttr{"Name"}}) - { # typedef int*const TYPEDEF; // first - # int foo(TYPEDEF p); // const is optimized out - $Typedef_BaseName{$Version}{$TypeAttr{"Name"}} = $BTAttr{"Name"}; - if($BTAttr{"Name"}=~/int($b)} keys(%{$Vector})) - { - foreach my $P2 (get_TemplateParam($Pos, $Vector->{$P1})) { - push(@Params, $P2); - } - } - return @Params; - } - elsif($NodeType eq "parm_decl") - { - (getNameByInfo($Type_Id)); - } - else - { - my %ParamAttr = getTypeAttr($Type_Id); - my $PName = $ParamAttr{"Name"}; - if(not $PName) { - return (); - } - if($PName=~/\>/) - { - if(my $Cover = cover_stdcxx_typedef($PName)) { - $PName = $Cover; - } - } - if($Pos>=1 and - $PName=~/\A$DEFAULT_STD_PARMS\ > - # template - # template > - # template > - # template > - # template > - return ("\@skip\@"); - } - return ($PName); - } -} - -sub cover_stdcxx_typedef($) -{ - my $TypeName = $_[0]; - if(my @Covers = sort {length($a)<=>length($b)} - sort keys(%{$StdCxxTypedef{$Version}{$TypeName}})) - { # take the shortest typedef - # FIXME: there may be more than - # one typedefs to the same type - return $Covers[0]; - } - my $Covered = $TypeName; - while($TypeName=~s/(>)[ ]*(const|volatile|restrict| |\*|\&)\Z/$1/g){}; - if(my @Covers = sort {length($a)<=>length($b)} sort keys(%{$StdCxxTypedef{$Version}{$TypeName}})) - { - if(my $Cover = $Covers[0]) - { - $Covered=~s/\b\Q$TypeName\E(\W|\Z)/$Cover$1/g; - $Covered=~s/\b\Q$TypeName\E(\w|\Z)/$Cover $1/g; - } - } - return formatName($Covered, "T"); -} - -sub getNodeIntCst($) -{ - my $CstId = $_[0]; - my $CstTypeId = getTreeAttr_Type($CstId); - if($EnumMembName_Id{$Version}{$CstId}) { - return $EnumMembName_Id{$Version}{$CstId}; - } - elsif((my $Value = getTreeValue($CstId)) ne "") - { - if($Value eq "0") - { - if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") { - return "false"; - } - else { - return "0"; - } - } - elsif($Value eq "1") - { - if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") { - return "true"; - } - else { - return "1"; - } - } - else { - return $Value; - } - } - return ""; -} - -sub getNodeStrCst($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/strg[ ]*: (.+) lngt:[ ]*(\d+)/) - { - if($LibInfo{$Version}{"info_type"}{$_[0]} eq "string_cst") - { # string length is N-1 because of the null terminator - return substr($1, 0, $2-1); - } - else - { # identifier_node - return substr($1, 0, $2); - } - } - } - return ""; -} - -sub getMemPtrAttr($$$) -{ # function, method and field pointers - my ($PtrId, $TypeId, $Type) = @_; - my $MemInfo = $LibInfo{$Version}{"info"}{$PtrId}; - if($Type eq "FieldPtr") { - $MemInfo = $LibInfo{$Version}{"info"}{$TypeId}; - } - my $MemInfo_Type = $LibInfo{$Version}{"info_type"}{$PtrId}; - my $MemPtrName = ""; - my %TypeAttr = ("Size"=>$WORD_SIZE{$Version}, "Type"=>$Type, "Tid"=>$TypeId); - if($Type eq "MethodPtr") - { # size of "method pointer" may be greater than WORD size - if(my $Size = getSize($TypeId)) - { - $Size/=$BYTE_SIZE; - $TypeAttr{"Size"} = "$Size"; - } - } - if(my $Algn = getAlgn($TypeId)) { - $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE; - } - # Return - if($Type eq "FieldPtr") - { - my %ReturnAttr = getTypeAttr($PtrId); - if($ReturnAttr{"Name"}) { - $MemPtrName .= $ReturnAttr{"Name"}; - } - $TypeAttr{"Return"} = $PtrId; - } - else - { - if($MemInfo=~/retn[ ]*:[ ]*\@(\d+) /) - { - my $ReturnTypeId = $1; - my %ReturnAttr = getTypeAttr($ReturnTypeId); - if(not $ReturnAttr{"Name"}) - { # templates - return (); - } - $MemPtrName .= $ReturnAttr{"Name"}; - $TypeAttr{"Return"} = $ReturnTypeId; - } - } - # Class - if($MemInfo=~/(clas|cls)[ ]*:[ ]*@(\d+) /) - { - $TypeAttr{"Class"} = $2; - my %Class = getTypeAttr($TypeAttr{"Class"}); - if($Class{"Name"}) { - $MemPtrName .= " (".$Class{"Name"}."\:\:*)"; - } - else { - $MemPtrName .= " (*)"; - } - } - else { - $MemPtrName .= " (*)"; - } - # Parameters - if($Type eq "FuncPtr" - or $Type eq "MethodPtr") - { - my @ParamTypeName = (); - if($MemInfo=~/prms[ ]*:[ ]*@(\d+) /) - { - my $PTypeInfoId = $1; - my ($Pos, $PPos) = (0, 0); - while($PTypeInfoId) - { - my $PTypeInfo = $LibInfo{$Version}{"info"}{$PTypeInfoId}; - if($PTypeInfo=~/valu[ ]*:[ ]*@(\d+) /) - { - my $PTypeId = $1; - my %ParamAttr = getTypeAttr($PTypeId); - if(not $ParamAttr{"Name"}) - { # templates (template_type_parm), etc. - return (); - } - if($ParamAttr{"Name"} eq "void") { - last; - } - if($Pos!=0 or $Type ne "MethodPtr") - { - $TypeAttr{"Param"}{$PPos++}{"type"} = $PTypeId; - push(@ParamTypeName, $ParamAttr{"Name"}); - } - if($PTypeInfoId = getNextElem($PTypeInfoId)) { - $Pos+=1; - } - else { - last; + if($Html) { + $Class = specChars($Class); } + + $Signature = $Class."::".$Signature; } - else { - last; + elsif(my $NameSpace = $CompSign{$LVer}{$Symbol}{"NameSpace"}) { + $Signature = $NameSpace."::".$Signature; } } } - $MemPtrName .= " (".join(", ", @ParamTypeName).")"; - } - $TypeAttr{"Name"} = formatName($MemPtrName, "T"); - return %TypeAttr; -} - -sub getTreeTypeName($) -{ - my $TypeId = $_[0]; - if(my $Info = $LibInfo{$Version}{"info"}{$TypeId}) - { - if($LibInfo{$Version}{"info_type"}{$_[0]} eq "integer_type") - { - if(my $Name = getNameByInfo($TypeId)) - { # bit_size_type - return $Name; - } - elsif($Info=~/unsigned/) { - return "unsigned int"; - } - else { - return "int"; - } - } - elsif($Info=~/name[ ]*:[ ]*@(\d+) /) { - return getNameByInfo($1); - } - } - return ""; -} - -sub isFuncPtr($) -{ - my $Ptd = pointTo($_[0]); - return 0 if(not $Ptd); - if(my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/unql[ ]*:/ and $Info!~/qual[ ]*:/) { - return 0; - } - } - if(my $InfoT1 = $LibInfo{$Version}{"info_type"}{$_[0]} - and my $InfoT2 = $LibInfo{$Version}{"info_type"}{$Ptd}) - { - if($InfoT1 eq "pointer_type" - and $InfoT2 eq "function_type") { - return 1; - } - } - return 0; -} - -sub isMethodPtr($) -{ - my $Ptd = pointTo($_[0]); - return 0 if(not $Ptd); - if(my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($LibInfo{$Version}{"info_type"}{$_[0]} eq "record_type" - and $LibInfo{$Version}{"info_type"}{$Ptd} eq "method_type" - and $Info=~/ ptrmem /) { - return 1; - } - } - return 0; -} - -sub isFieldPtr($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($LibInfo{$Version}{"info_type"}{$_[0]} eq "offset_type" - and $Info=~/ ptrmem /) { - return 1; - } - } - return 0; -} - -sub pointTo($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/ptd[ ]*:[ ]*@(\d+)/) { - return $1; - } - } - return ""; -} - -sub getTypeTypeByTypeId($) -{ - my $TypeId = $_[0]; - if(my $TType = $LibInfo{$Version}{"info_type"}{$TypeId}) - { - my $NType = $NodeType{$TType}; - if($NType eq "Intrinsic") { - return $NType; - } - elsif(isFuncPtr($TypeId)) { - return "FuncPtr"; - } - elsif(isMethodPtr($TypeId)) { - return "MethodPtr"; - } - elsif(isFieldPtr($TypeId)) { - return "FieldPtr"; - } - elsif($NType ne "Other") { - return $NType; - } - } - return "Unknown"; -} - -my %UnQual = ( - "r"=>"restrict", - "v"=>"volatile", - "c"=>"const", - "cv"=>"const volatile" -); - -sub getQual($) -{ - my $TypeId = $_[0]; - if(my $Info = $LibInfo{$Version}{"info"}{$TypeId}) - { - my ($Qual, $To) = (); - if($Info=~/qual[ ]*:[ ]*(r|c|v|cv) /) { - $Qual = $UnQual{$1}; - } - if($Info=~/unql[ ]*:[ ]*\@(\d+)/) { - $To = $1; - } - if($Qual and $To) { - return ($Qual, $To); - } - } - return (); -} - -sub getQualType($) -{ - if($_[0] eq "const volatile") { - return "ConstVolatile"; - } - return ucfirst($_[0]); -} - -sub getTypeType($) -{ - my $TypeId = $_[0]; - my $TypeDeclId = getTypeDeclId($TypeId); - if(defined $MissedTypedef{$Version}{$TypeId}) - { # support for old GCC versions - if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq $TypeDeclId) { - return "Typedef"; - } - } - my $Info = $LibInfo{$Version}{"info"}{$TypeId}; - my ($Qual, $To) = getQual($TypeId); - if(($Qual or $To) and $TypeDeclId - and (getTypeId($TypeDeclId) ne $TypeId)) - { # qualified types (special) - return getQualType($Qual); - } - elsif(not $MissedBase_R{$Version}{$TypeId} - and isTypedef($TypeId)) { - return "Typedef"; - } - elsif($Qual) - { # qualified types - return getQualType($Qual); - } - - if($Info=~/unql[ ]*:[ ]*\@(\d+)/) - { # typedef struct { ... } name - $TypeTypedef{$Version}{$TypeId} = $1; } - my $TypeType = getTypeTypeByTypeId($TypeId); - if($TypeType eq "Struct") + my @Params = (); + if(defined $CompSign{$LVer}{$Symbol}{"Param"}) { - if($TypeDeclId - and $LibInfo{$Version}{"info_type"}{$TypeDeclId} eq "template_decl") { - return "Template"; - } - } - return $TypeType; -} - -sub isTypedef($) -{ - if($_[0]) - { - if($LibInfo{$Version}{"info_type"}{$_[0]} eq "vector_type") - { # typedef float La_x86_64_xmm __attribute__ ((__vector_size__ (16))); - return 0; - } - if(my $Info = $LibInfo{$Version}{"info"}{$_[0]}) + foreach my $PPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) { - if(my $TDid = getTypeDeclId($_[0])) - { - if(getTypeId($TDid) eq $_[0] - and getNameByInfo($TDid)) - { - if($Info=~/unql[ ]*:[ ]*\@(\d+) /) { - return $1; - } - } + my $PTid = $CompSign{$LVer}{$Symbol}{"Param"}{$PPos}{"type"}; + if(not $PTid) { + next; } - } - } - return 0; -} - -sub selectBaseType($) -{ - my $TypeId = $_[0]; - if(defined $MissedTypedef{$Version}{$TypeId}) - { # add missed typedefs - if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq getTypeDeclId($TypeId)) { - return ($TypeId, ""); - } - } - my $Info = $LibInfo{$Version}{"info"}{$TypeId}; - my $InfoType = $LibInfo{$Version}{"info_type"}{$TypeId}; - - my $MB_R = $MissedBase_R{$Version}{$TypeId}; - my $MB = $MissedBase{$Version}{$TypeId}; - - my ($Qual, $To) = getQual($TypeId); - if(($Qual or $To) and $Info=~/name[ ]*:[ ]*\@(\d+) / - and (getTypeId($1) ne $TypeId) - and (not $MB_R or getTypeId($1) ne $MB_R)) - { # qualified types (special) - return (getTypeId($1), $Qual); - } - elsif($MB) - { # add base - return ($MB, ""); - } - elsif(not $MB_R and my $Bid = isTypedef($TypeId)) - { # typedefs - return ($Bid, ""); - } - elsif($Qual or $To) - { # qualified types - return ($To, $Qual); - } - elsif($InfoType eq "reference_type") - { - if($Info=~/refd[ ]*:[ ]*@(\d+) /) { - return ($1, "&"); - } - } - elsif($InfoType eq "array_type") - { - if($Info=~/elts[ ]*:[ ]*@(\d+) /) { - return ($1, ""); - } - } - elsif($InfoType eq "pointer_type") - { - if($Info=~/ptd[ ]*:[ ]*@(\d+) /) { - return ($1, "*"); - } - } - - return (0, ""); -} - -sub getSymbolInfo_All() -{ - foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}})) - { # reverse order - if($LibInfo{$Version}{"info_type"}{$_} eq "function_decl") { - getSymbolInfo($_); - } - } - - if($ADD_TMPL_INSTANCES) - { - # templates - foreach my $Sid (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$Version}})) - { - my %Map = (); - if(my $ClassId = $SymbolInfo{$Version}{$Sid}{"Class"}) + if(my $PTName = $TypeInfo{$LVer}{$PTid}{"Name"}) { - if(defined $TemplateMap{$Version}{$ClassId}) + foreach my $Typedef (keys(%ChangedTypedef)) { - foreach (keys(%{$TemplateMap{$Version}{$ClassId}})) { - $Map{$_} = $TemplateMap{$Version}{$ClassId}{$_}; + if(index($PTName, $Typedef)!=-1) + { + if($PTName=~/\b\Q$Typedef\E\b/) + { + $PTName = uncoverTypedefs($PTName, $LVer); + last; + } } } - } - - if(defined $TemplateMap{$Version}{$Sid}) - { - foreach (keys(%{$TemplateMap{$Version}{$Sid}})) { - $Map{$_} = $TemplateMap{$Version}{$Sid}{$_}; - } - } - - if(defined $SymbolInfo{$Version}{$Sid}{"Param"}) - { - foreach (keys(%{$SymbolInfo{$Version}{$Sid}{"Param"}})) - { - my $PTid = $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"}; - $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"} = instType(\%Map, $PTid, $Version); - } - } - if(my $Return = $SymbolInfo{$Version}{$Sid}{"Return"}) { - $SymbolInfo{$Version}{$Sid}{"Return"} = instType(\%Map, $Return, $Version); - } - } - } -} - -sub getVarInfo_All() -{ - foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}})) - { # reverse order - if($LibInfo{$Version}{"info_type"}{$_} eq "var_decl") { - getVarInfo($_); - } - } -} - -sub isBuiltIn($) { - return ($_[0] and $_[0]=~/\|\|\A\./); -} - -sub getVarInfo($) -{ - my $InfoId = $_[0]; - if(my $NSid = getTreeAttr_Scpe($InfoId)) - { - my $NSInfoType = $LibInfo{$Version}{"info_type"}{$NSid}; - if($NSInfoType and $NSInfoType eq "function_decl") { - return; - } - } - ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId); - if(not $SymbolInfo{$Version}{$InfoId}{"Header"} - or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"})) { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - my $ShortName = getTreeStr(getTreeAttr_Name($InfoId)); - if(not $ShortName) { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - if($ShortName=~/\Atmp_add_class_\d+\Z/) { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - $SymbolInfo{$Version}{$InfoId}{"ShortName"} = $ShortName; - if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId))) - { - if($OSgroup eq "windows") - { # cut the offset - $MnglName=~s/\@\d+\Z//g; - } - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName; - } - if($SymbolInfo{$Version}{$InfoId}{"MnglName"} - and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0) - { # validate mangled name - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"} - and index($ShortName, "_Z")==0) - { # _ZTS, etc. - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName; - } - if(isPrivateData($SymbolInfo{$Version}{$InfoId}{"MnglName"})) - { # non-public global data - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - $SymbolInfo{$Version}{$InfoId}{"Data"} = 1; - if(my $Rid = getTypeId($InfoId)) - { - if(not defined $TypeInfo{$Version}{$Rid} - or not $TypeInfo{$Version}{$Rid}{"Name"}) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - $SymbolInfo{$Version}{$InfoId}{"Return"} = $Rid; - my $Val = getDataVal($InfoId, $Rid); - if(defined $Val) { - $SymbolInfo{$Version}{$InfoId}{"Value"} = $Val; - } - } - set_Class_And_Namespace($InfoId); - if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - if(not defined $TypeInfo{$Version}{$ClassId} - or not $TypeInfo{$Version}{$ClassId}{"Name"}) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - } - if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i) - { # extern "C" - $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C"; - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName; - } - if($UserLang and $UserLang eq "C") - { # --lang=C option - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName; - } - if(not $CheckHeadersOnly) - { - if(not $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"} - or not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps")) - { - if(link_symbol($ShortName, $Version, "-Deps")) - { # "const" global data is mangled as _ZL... in the TU dump - # but not mangled when compiling a C shared library - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName; - } - } - } - } - if($COMMON_LANGUAGE{$Version} eq "C++") - { - if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { # for some symbols (_ZTI) the short name is the mangled name - if(index($ShortName, "_Z")==0) { - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName; - } - } - if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { # try to mangle symbol (link with libraries) - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = linkSymbol($InfoId); - } - if($OStarget eq "windows") - { - if(my $Mangled = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")}) - { # link MS C++ symbols from library with GCC symbols from headers - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled; - } - } - } - if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) { - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName; - } - if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { - if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version)) - { # non-target symbols - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - } - if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"}) - { - if(defined $MissedTypedef{$Version}{$Rid}) - { - if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) { - $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid; - } - } - } - setFuncAccess($InfoId); - if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_ZTV")==0) { - delete($SymbolInfo{$Version}{$InfoId}{"Return"}); - } - if($ShortName=~/\A(_Z|\?)/) { - delete($SymbolInfo{$Version}{$InfoId}{"ShortName"}); - } - - if($ExtraDump) { - $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId); - } -} - -sub isConstType($$) -{ - my ($TypeId, $LibVersion) = @_; - my %Base = get_Type($TypeId, $LibVersion); - while(defined $Base{"Type"} and $Base{"Type"} eq "Typedef") { - %Base = get_OneStep_BaseType($Base{"Tid"}, $TypeInfo{$LibVersion}); - } - return ($Base{"Type"} eq "Const"); -} - -sub getTrivialName($$) -{ - my ($TypeInfoId, $TypeId) = @_; - my %TypeAttr = (); - $TypeAttr{"Name"} = getNameByInfo($TypeInfoId); - if(not $TypeAttr{"Name"}) { - $TypeAttr{"Name"} = getTreeTypeName($TypeId); - } - ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId); - $TypeAttr{"Type"} = getTypeType($TypeId); - $TypeAttr{"Name"}=~s/<(.+)\Z//g; # GCC 3.4.4 add template params to the name - if(isAnon($TypeAttr{"Name"})) - { - my $NameSpaceId = $TypeId; - while(my $NSId = getTreeAttr_Scpe(getTypeDeclId($NameSpaceId))) - { # searching for a first not anon scope - if($NSId eq $NameSpaceId) { - last; - } - else - { - $TypeAttr{"NameSpace"} = getNameSpace(getTypeDeclId($TypeId)); - if(not $TypeAttr{"NameSpace"} - or not isAnon($TypeAttr{"NameSpace"})) { - last; - } - } - $NameSpaceId = $NSId; - } - } - else - { - if(my $NameSpaceId = getTreeAttr_Scpe($TypeInfoId)) - { - if($NameSpaceId ne $TypeId) { - $TypeAttr{"NameSpace"} = getNameSpace($TypeInfoId); - } - } - } - if($TypeAttr{"NameSpace"} and not isAnon($TypeAttr{"Name"})) { - $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"}; - } - $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T"); - if(isAnon($TypeAttr{"Name"})) - { # anon-struct-header.h-line - $TypeAttr{"Name"} = "anon-".lc($TypeAttr{"Type"})."-"; - $TypeAttr{"Name"} .= $TypeAttr{"Header"}."-".$TypeAttr{"Line"}; - if($TypeAttr{"NameSpace"}) { - $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"}; - } - } - if(defined $TemplateInstance{$Version}{"Type"}{$TypeId} - and getTypeDeclId($TypeId) eq $TypeInfoId) - { - if(my @TParams = getTParams($TypeId, "Type")) { - $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."< ".join(", ", @TParams)." >", "T"); - } - else { - $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."<...>", "T"); - } - } - return ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}); -} - -sub getTrivialTypeAttr($) -{ - my $TypeId = $_[0]; - my $TypeInfoId = getTypeDeclId($_[0]); - - my %TypeAttr = (); - - if($TemplateDecl{$Version}{$TypeId}) - { # template_decl - $TypeAttr{"Template"} = 1; - } - - setTypeAccess($TypeId, \%TypeAttr); - ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId); - if(isBuiltIn($TypeAttr{"Header"})) - { - delete($TypeAttr{"Header"}); - delete($TypeAttr{"Line"}); - } - - $TypeAttr{"Type"} = getTypeType($TypeId); - ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}) = getTrivialName($TypeInfoId, $TypeId); - if(not $TypeAttr{"Name"}) { - return (); - } - if(not $TypeAttr{"NameSpace"}) { - delete($TypeAttr{"NameSpace"}); - } - - if($TypeAttr{"Type"} eq "Intrinsic") - { - if(defined $TypeAttr{"Header"}) - { - if($TypeAttr{"Header"}=~/\Adump[1-2]\.[ih]\Z/) - { # support for SUSE 11.2 - # integer_type has srcp dump{1-2}.i - delete($TypeAttr{"Header"}); - } - } - } - - my $Tmpl = undef; - - if(defined $TemplateInstance{$Version}{"Type"}{$TypeId}) - { - $Tmpl = $BasicTemplate{$Version}{$TypeId}; - - if(my @TParams = getTParams($TypeId, "Type")) - { - foreach my $Pos (0 .. $#TParams) - { - my $Val = $TParams[$Pos]; - $TypeAttr{"TParam"}{$Pos}{"name"} = $Val; - if(not defined $TypeAttr{"Template"}) - { - my %Base = get_BaseType($TemplateInstance{$Version}{"Type"}{$TypeId}{$Pos}, $Version); - - if($Base{"Type"} eq "TemplateParam" - or defined $Base{"Template"}) { - $TypeAttr{"Template"} = 1; - } + $PTName=~s/\(kind=\d+\)//g; + + if($Html) { + $PTName = specChars($PTName); } - if($Tmpl) - { - if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos}) - { - $TemplateMap{$Version}{$TypeId}{$Arg} = $Val; - - if($Val eq $Arg) { - $TypeAttr{"Template"} = 1; - } - } + my $PName = $CompSign{$LVer}{$Symbol}{"Param"}{$PPos}{"name"}; + if($Mngl and ($PName eq "this" or $PName eq "__in_chrg" or $PName eq "__vtt_parm")) + { # do NOT show first hidded "this"-parameter + next; } - } - - if($Tmpl) - { - foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}})) + + if($PName and ($Full or $ShowParams)) { - if($Pos>$#TParams) - { - my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos}; - $TemplateMap{$Version}{$TypeId}{$Arg} = ""; + if($Simple) { + $PName = "$PName"; } - } - } - } - - if($ADD_TMPL_INSTANCES) - { - if($Tmpl) - { - if(my $MainInst = getTreeAttr_Type($Tmpl)) - { - if(not getTreeAttr_Flds($TypeId)) + elsif($Html) { - if(my $Flds = getTreeAttr_Flds($MainInst)) { - $LibInfo{$Version}{"info"}{$TypeId} .= " flds: \@$Flds "; + if(defined $Target + and $Target==adjustParamPos($PPos, $Symbol, $LVer)) { + $PName = "$PName"; } - } - if(not getTreeAttr_Binf($TypeId)) - { - if(my $Binf = getTreeAttr_Binf($MainInst)) { - $LibInfo{$Version}{"info"}{$TypeId} .= " binf: \@$Binf "; + elsif($Color) { + $PName = "$PName"; + } + elsif($Italic) { + $PName = "$PName"; } } + + push(@Params, createMemDecl($PTName, $PName)); + } + else { + push(@Params, $PTName); } } } } - my $StaticFields = setTypeMemb($TypeId, \%TypeAttr); + if($Simple) { + $Signature = "".$Signature.""; + } - if(my $Size = getSize($TypeId)) + if($CompSign{$LVer}{$Symbol}{"Data"}) { - $Size = $Size/$BYTE_SIZE; - $TypeAttr{"Size"} = "$Size"; + $Signature .= " [data]"; } else { - if($ExtraDump) + if($Full or $ShowAttr) { - if(not defined $TypeAttr{"Memb"} - and not $Tmpl) - { # declaration only - $TypeAttr{"Forward"} = 1; + if($Mngl) + { + if(my $ChargeLevel = getChargeLevel($Symbol, $LVer)) { + $Signature .= " ".$ChargeLevel; + } } } - } - - if($TypeAttr{"Type"} eq "Struct" - and ($StaticFields or detect_lang($TypeId))) - { - $TypeAttr{"Type"} = "Class"; - $TypeAttr{"Copied"} = 1; # default, will be changed in getSymbolInfo() - } - if($TypeAttr{"Type"} eq "Struct" - or $TypeAttr{"Type"} eq "Class") - { - my $Skip = setBaseClasses($TypeId, \%TypeAttr); - if($Skip) { - return (); - } - } - if(my $Algn = getAlgn($TypeId)) { - $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE; - } - setSpec($TypeId, \%TypeAttr); - - if($TypeAttr{"Type"}=~/\A(Struct|Union|Enum)\Z/) - { - if(not $TypedefToAnon{$TypeId} - and not defined $TemplateInstance{$Version}{"Type"}{$TypeId}) + + if($Html and not $Simple) { - if(not isAnon($TypeAttr{"Name"})) { - $TypeAttr{"Name"} = lc($TypeAttr{"Type"})." ".$TypeAttr{"Name"}; + $Signature .= " "; + + if($Desc) { + $Signature .= ""; } - } - } - - $TypeAttr{"Tid"} = $TypeId; - if(my $VTable = $ClassVTable_Content{$Version}{$TypeAttr{"Name"}}) - { - my @Entries = split(/\n/, $VTable); - foreach (1 .. $#Entries) - { - my $Entry = $Entries[$_]; - if($Entry=~/\A(\d+)\s+(.+)\Z/) { - $TypeAttr{"VTable"}{$1} = simplifyVTable($2); + else { + $Signature .= ""; } - } - } - - if($TypeAttr{"Type"} eq "Enum") - { - if(not $TypeAttr{"NameSpace"}) - { - foreach my $Pos (keys(%{$TypeAttr{"Memb"}})) + if(@Params) { - my $MName = $TypeAttr{"Memb"}{$Pos}{"name"}; - my $MVal = $TypeAttr{"Memb"}{$Pos}{"value"}; - $EnumConstants{$Version}{$MName} = { - "Value"=>$MVal, - "Header"=>$TypeAttr{"Header"} - }; - if(isAnon($TypeAttr{"Name"})) + foreach my $Pos (0 .. $#Params) { - if($ExtraDump - or is_target_header($TypeAttr{"Header"}, $Version)) - { - %{$Constants{$Version}{$MName}} = ( - "Value" => $MVal, - "Header" => $TypeAttr{"Header"} - ); + my $Name = ""; + + if($Pos==0) { + $Name .= "( "; } - } - } - } - } - if($ExtraDump) - { - if(defined $TypedefToAnon{$TypeId}) { - $TypeAttr{"AnonTypedef"} = 1; - } - } - - return %TypeAttr; -} - -sub simplifyVTable($) -{ - my $Content = $_[0]; - if($Content=~s/ \[with (.+)]//) - { # std::basic_streambuf<_CharT, _Traits>::imbue [with _CharT = char, _Traits = std::char_traits] - if(my @Elems = separate_Params($1, 0, 0)) - { - foreach my $Elem (@Elems) - { - if($Elem=~/\A(.+?)\s*=\s*(.+?)\Z/) - { - my ($Arg, $Val) = ($1, $2); - if(defined $DEFAULT_STD_ARGS{$Arg}) { - $Content=~s/,\s*$Arg\b//g; + $Name .= $Params[$Pos]; + + $Name = "".$Name.""; + + if($Pos==$#Params) { + $Name .= " )"; } else { - $Content=~s/\b$Arg\b/$Val/g; + $Name .= ", "; } + + $Signature .= $Name; } } - } - } - - return $Content; -} - -sub detect_lang($) -{ - my $TypeId = $_[0]; - my $Info = $LibInfo{$Version}{"info"}{$TypeId}; - if(check_gcc($GCC_PATH, "4")) - { # GCC 4 fncs-node points to only non-artificial methods - return ($Info=~/(fncs)[ ]*:[ ]*@(\d+) /); - } - else - { # GCC 3 - my $Fncs = getTreeAttr_Fncs($TypeId); - while($Fncs) - { - if($LibInfo{$Version}{"info"}{$Fncs}!~/artificial/) { - return 1; - } - $Fncs = getTreeAttr_Chan($Fncs); - } - } - return 0; -} - -sub setSpec($$) -{ - my ($TypeId, $TypeAttr) = @_; - my $Info = $LibInfo{$Version}{"info"}{$TypeId}; - if($Info=~/\s+spec\s+/) { - $TypeAttr->{"Spec"} = 1; - } -} - -sub setBaseClasses($$) -{ - my ($TypeId, $TypeAttr) = @_; - my $Info = $LibInfo{$Version}{"info"}{$TypeId}; - if(my $Binf = getTreeAttr_Binf($TypeId)) - { - my $Info = $LibInfo{$Version}{"info"}{$Binf}; - my $Pos = 0; - while($Info=~s/(pub|public|prot|protected|priv|private|)[ ]+binf[ ]*:[ ]*@(\d+) //) - { - my ($Access, $BInfoId) = ($1, $2); - my $ClassId = getBinfClassId($BInfoId); - - if($ClassId==$TypeId) - { # class A:public A - next; - } - - my $CType = $LibInfo{$Version}{"info_type"}{$ClassId}; - if(not $CType or $CType eq "template_type_parm" - or $CType eq "typename_type") - { # skip - # return 1; - } - my $BaseInfo = $LibInfo{$Version}{"info"}{$BInfoId}; - if($Access=~/prot/) { - $TypeAttr->{"Base"}{$ClassId}{"access"} = "protected"; - } - elsif($Access=~/priv/) { - $TypeAttr->{"Base"}{$ClassId}{"access"} = "private"; - } - $TypeAttr->{"Base"}{$ClassId}{"pos"} = "$Pos"; - if($BaseInfo=~/virt/) - { # virtual base - $TypeAttr->{"Base"}{$ClassId}{"virtual"} = 1; - } - $Class_SubClasses{$Version}{$ClassId}{$TypeId}=1; - $Pos += 1; - } - } - return 0; -} - -sub getBinfClassId($) -{ - my $Info = $LibInfo{$Version}{"info"}{$_[0]}; - $Info=~/type[ ]*:[ ]*@(\d+) /; - return $1; -} - -sub unmangledFormat($$) -{ - my ($Name, $LibVersion) = @_; - $Name = uncover_typedefs($Name, $LibVersion); - while($Name=~s/([^\w>*])(const|volatile)(,|>|\Z)/$1$3/g){}; - $Name=~s/\(\w+\)(\d)/$1/; - return $Name; -} - -sub modelUnmangled($$) -{ - my ($InfoId, $Compiler) = @_; - if($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId}) { - return $Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId}; - } - my $PureSignature = $SymbolInfo{$Version}{$InfoId}{"ShortName"}; - if($SymbolInfo{$Version}{$InfoId}{"Destructor"}) { - $PureSignature = "~".$PureSignature; - } - if(not $SymbolInfo{$Version}{$InfoId}{"Data"}) - { - my (@Params, @ParamTypes) = (); - if(defined $SymbolInfo{$Version}{$InfoId}{"Param"} - and not $SymbolInfo{$Version}{$InfoId}{"Destructor"}) { - @Params = keys(%{$SymbolInfo{$Version}{$InfoId}{"Param"}}); - } - foreach my $ParamPos (sort {int($a) <=> int($b)} @Params) - { # checking parameters - my $PId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"type"}; - my $PName = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"name"}; - my %PType = get_PureType($PId, $TypeInfo{$Version}); - my $PTName = unmangledFormat($PType{"Name"}, $Version); - - if($PName eq "this" - and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method") - { - next; - } - - $PTName=~s/\b(restrict|register)\b//g; - if($Compiler eq "MSVC") { - $PTName=~s/\blong long\b/__int64/; - } - @ParamTypes = (@ParamTypes, $PTName); - } - if(@ParamTypes) { - $PureSignature .= "(".join(", ", @ParamTypes).")"; - } - else - { - if($Compiler eq "MSVC") - { - $PureSignature .= "(void)"; - } - else - { # GCC - $PureSignature .= "()"; - } - } - $PureSignature = delete_keywords($PureSignature); - } - if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - my $ClassName = unmangledFormat($TypeInfo{$Version}{$ClassId}{"Name"}, $Version); - $PureSignature = $ClassName."::".$PureSignature; - } - elsif(my $NS = $SymbolInfo{$Version}{$InfoId}{"NameSpace"}) { - $PureSignature = $NS."::".$PureSignature; - } - if($SymbolInfo{$Version}{$InfoId}{"Const"}) { - $PureSignature .= " const"; - } - if($SymbolInfo{$Version}{$InfoId}{"Volatile"}) { - $PureSignature .= " volatile"; - } - my $ShowReturn = 0; - if($Compiler eq "MSVC" - and $SymbolInfo{$Version}{$InfoId}{"Data"}) - { - $ShowReturn=1; - } - elsif(defined $TemplateInstance{$Version}{"Func"}{$InfoId} - and keys(%{$TemplateInstance{$Version}{"Func"}{$InfoId}})) - { - $ShowReturn=1; - } - if($ShowReturn) - { # mangled names for template function specializations include return value - if(my $ReturnId = $SymbolInfo{$Version}{$InfoId}{"Return"}) - { - my %RType = get_PureType($ReturnId, $TypeInfo{$Version}); - my $ReturnName = unmangledFormat($RType{"Name"}, $Version); - $PureSignature = $ReturnName." ".$PureSignature; - } - } - return ($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId} = formatName($PureSignature, "S")); -} - -sub mangle_symbol($$$) -{ # mangling for simple methods - # see gcc-4.6.0/gcc/cp/mangle.c - my ($InfoId, $LibVersion, $Compiler) = @_; - if($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler}) { - return $Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler}; - } - my $Mangled = ""; - if($Compiler eq "GCC") { - $Mangled = mangle_symbol_GCC($InfoId, $LibVersion); - } - elsif($Compiler eq "MSVC") { - $Mangled = mangle_symbol_MSVC($InfoId, $LibVersion); - } - return ($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler} = $Mangled); -} - -sub mangle_symbol_MSVC($$) -{ # TODO - my ($InfoId, $LibVersion) = @_; - return ""; -} - -sub mangle_symbol_GCC($$) -{ # see gcc-4.6.0/gcc/cp/mangle.c - my ($InfoId, $LibVersion) = @_; - my ($Mangled, $ClassId, $NameSpace) = ("_Z", 0, ""); - my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"}; - my %Repl = ();# SN_ replacements - if($ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"}) - { - my $MangledClass = mangle_param($ClassId, $LibVersion, \%Repl); - if($MangledClass!~/\AN/) { - $MangledClass = "N".$MangledClass; - } - else { - $MangledClass=~s/E\Z//; - } - if($SymbolInfo{$LibVersion}{$InfoId}{"Volatile"}) { - $MangledClass=~s/\AN/NV/; - } - if($SymbolInfo{$LibVersion}{$InfoId}{"Const"}) { - $MangledClass=~s/\AN/NK/; - } - $Mangled .= $MangledClass; - } - elsif($NameSpace = $SymbolInfo{$LibVersion}{$InfoId}{"NameSpace"}) - { # mangled by name due to the absence of structured info - my $MangledNS = mangle_ns($NameSpace, $LibVersion, \%Repl); - if($MangledNS!~/\AN/) { - $MangledNS = "N".$MangledNS; - } - else { - $MangledNS=~s/E\Z//; - } - $Mangled .= $MangledNS; - } - my ($ShortName, $TmplParams) = template_Base($SymbolInfo{$LibVersion}{$InfoId}{"ShortName"}); - my @TParams = (); - if(my @TPos = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}})) - { # parsing mode - foreach (@TPos) { - push(@TParams, $SymbolInfo{$LibVersion}{$InfoId}{"TParam"}{$_}{"name"}); - } - } - elsif($TmplParams) - { # remangling mode - # support for old ABI dumps - @TParams = separate_Params($TmplParams, 0, 0); - } - if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"}) { - $Mangled .= "C1"; - } - elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) { - $Mangled .= "D0"; - } - elsif($ShortName) - { - if($SymbolInfo{$LibVersion}{$InfoId}{"Data"}) - { - if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"} - and isConstType($Return, $LibVersion)) - { # "const" global data is mangled as _ZL... - $Mangled .= "L"; - } - } - if($ShortName=~/\Aoperator(\W.*)\Z/) - { - my $Op = $1; - $Op=~s/\A[ ]+//g; - if(my $OpMngl = $OperatorMangling{$Op}) { - $Mangled .= $OpMngl; - } - else { # conversion operator - $Mangled .= "cv".mangle_param(getTypeIdByName($Op, $LibVersion), $LibVersion, \%Repl); - } - } - else { - $Mangled .= length($ShortName).$ShortName; - } - if(@TParams) - { # templates - $Mangled .= "I"; - foreach my $TParam (@TParams) { - $Mangled .= mangle_template_param($TParam, $LibVersion, \%Repl); - } - $Mangled .= "E"; - } - if(not $ClassId and @TParams) { - add_substitution($ShortName, \%Repl, 0); - } - } - if($ClassId or $NameSpace) { - $Mangled .= "E"; - } - if(@TParams) - { - if($Return) { - $Mangled .= mangle_param($Return, $LibVersion, \%Repl); - } - } - if(not $SymbolInfo{$LibVersion}{$InfoId}{"Data"}) - { - my @Params = (); - if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"} - and not $SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) { - @Params = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}}); - } - foreach my $ParamPos (sort {int($a) <=> int($b)} @Params) - { # checking parameters - my $ParamType_Id = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$ParamPos}{"type"}; - $Mangled .= mangle_param($ParamType_Id, $LibVersion, \%Repl); - } - if(not @Params) { - $Mangled .= "v"; - } - } - $Mangled = correct_incharge($InfoId, $LibVersion, $Mangled); - $Mangled = write_stdcxx_substitution($Mangled); - if($Mangled eq "_Z") { - return ""; - } - return $Mangled; -} - -sub correct_incharge($$$) -{ - my ($InfoId, $LibVersion, $Mangled) = @_; - if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"}) - { - if($MangledNames{$LibVersion}{$Mangled}) { - $Mangled=~s/C1([EI])/C2$1/; - } - } - elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) - { - if($MangledNames{$LibVersion}{$Mangled}) { - $Mangled=~s/D0([EI])/D1$1/; - } - if($MangledNames{$LibVersion}{$Mangled}) { - $Mangled=~s/D1([EI])/D2$1/; - } - } - return $Mangled; -} - -sub template_Base($) -{ # NOTE: std::_Vector_base::_Vector_impl - # NOTE: operators: >>, << - my $Name = $_[0]; - if($Name!~/>\Z/ or $Name!~/ - $TParams = substr($TParams, $CPos); - } - if($TParams=~s/\A<(.+)>\Z/$1/) { - $Name=~s/<\Q$TParams\E>\Z//; - } - else - { # error - $TParams = ""; - } - return ($Name, $TParams); -} - -sub get_sub_ns($) -{ - my $Name = $_[0]; - my @NS = (); - while(my $CPos = find_center($Name, ":")) - { - push(@NS, substr($Name, 0, $CPos)); - $Name = substr($Name, $CPos); - $Name=~s/\A:://; - } - return (join("::", @NS), $Name); -} - -sub mangle_ns($$$) -{ - my ($Name, $LibVersion, $Repl) = @_; - if(my $Tid = $TName_Tid{$LibVersion}{$Name}) - { - my $Mangled = mangle_param($Tid, $LibVersion, $Repl); - $Mangled=~s/\AN(.+)E\Z/$1/; - return $Mangled; - - } - else - { - my ($MangledNS, $SubNS) = ("", ""); - ($SubNS, $Name) = get_sub_ns($Name); - if($SubNS) { - $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl); - } - $MangledNS .= length($Name).$Name; - add_substitution($MangledNS, $Repl, 0); - return $MangledNS; - } -} - -sub mangle_param($$$) -{ - my ($PTid, $LibVersion, $Repl) = @_; - my ($MPrefix, $Mangled) = ("", ""); - my %ReplCopy = %{$Repl}; - my %BaseType = get_BaseType($PTid, $LibVersion); - my $BaseType_Name = $BaseType{"Name"}; - $BaseType_Name=~s/\A(struct|union|enum) //g; - if(not $BaseType_Name) { - return ""; - } - my ($ShortName, $TmplParams) = template_Base($BaseType_Name); - my $Suffix = get_BaseTypeQual($PTid, $LibVersion); - while($Suffix=~s/\s*(const|volatile|restrict)\Z//g){}; - while($Suffix=~/(&|\*|const)\Z/) - { - if($Suffix=~s/[ ]*&\Z//) { - $MPrefix .= "R"; - } - if($Suffix=~s/[ ]*\*\Z//) { - $MPrefix .= "P"; - } - if($Suffix=~s/[ ]*const\Z//) - { - if($MPrefix=~/R|P/ - or $Suffix=~/&|\*/) { - $MPrefix .= "K"; - } - } - if($Suffix=~s/[ ]*volatile\Z//) { - $MPrefix .= "V"; - } - #if($Suffix=~s/[ ]*restrict\Z//) { - #$MPrefix .= "r"; - #} - } - if(my $Token = $IntrinsicMangling{$BaseType_Name}) { - $Mangled .= $Token; - } - elsif($BaseType{"Type"}=~/(Class|Struct|Union|Enum)/) - { - my @TParams = (); - if(my @TPos = keys(%{$BaseType{"TParam"}})) - { # parsing mode - foreach (@TPos) { - push(@TParams, $BaseType{"TParam"}{$_}{"name"}); - } - } - elsif($TmplParams) - { # remangling mode - # support for old ABI dumps - @TParams = separate_Params($TmplParams, 0, 0); - } - my $MangledNS = ""; - my ($SubNS, $SName) = get_sub_ns($ShortName); - if($SubNS) { - $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl); - } - $MangledNS .= length($SName).$SName; - if(@TParams) { - add_substitution($MangledNS, $Repl, 0); - } - $Mangled .= "N".$MangledNS; - if(@TParams) - { # templates - $Mangled .= "I"; - foreach my $TParam (@TParams) { - $Mangled .= mangle_template_param($TParam, $LibVersion, $Repl); - } - $Mangled .= "E"; - } - $Mangled .= "E"; - } - elsif($BaseType{"Type"}=~/(FuncPtr|MethodPtr)/) - { - if($BaseType{"Type"} eq "MethodPtr") { - $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl)."F"; - } - else { - $Mangled .= "PF"; - } - $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl); - my @Params = keys(%{$BaseType{"Param"}}); - foreach my $Num (sort {int($a)<=>int($b)} @Params) { - $Mangled .= mangle_param($BaseType{"Param"}{$Num}{"type"}, $LibVersion, $Repl); - } - if(not @Params) { - $Mangled .= "v"; - } - $Mangled .= "E"; - } - elsif($BaseType{"Type"} eq "FieldPtr") - { - $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl); - $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl); - } - $Mangled = $MPrefix.$Mangled;# add prefix (RPK) - if(my $Optimized = write_substitution($Mangled, \%ReplCopy)) - { - if($Mangled eq $Optimized) - { - if($ShortName!~/::/) - { # remove "N ... E" - if($MPrefix) { - $Mangled=~s/\A($MPrefix)N(.+)E\Z/$1$2/g; - } - else { - $Mangled=~s/\AN(.+)E\Z/$1/g; - } + else { + $Signature .= "( )"; } - } - else { - $Mangled = $Optimized; - } - } - add_substitution($Mangled, $Repl, 1); - return $Mangled; -} - -sub mangle_template_param($$$) -{ # types + literals - my ($TParam, $LibVersion, $Repl) = @_; - if(my $TPTid = $TName_Tid{$LibVersion}{$TParam}) { - return mangle_param($TPTid, $LibVersion, $Repl); - } - elsif($TParam=~/\A(\d+)(\w+)\Z/) - { # class_name<1u>::method(...) - return "L".$IntrinsicMangling{$ConstantSuffixR{$2}}.$1."E"; - } - elsif($TParam=~/\A\(([\w ]+)\)(\d+)\Z/) - { # class_name<(signed char)1>::method(...) - return "L".$IntrinsicMangling{$1}.$2."E"; - } - elsif($TParam eq "true") - { # class_name::method(...) - return "Lb1E"; - } - elsif($TParam eq "false") - { # class_name::method(...) - return "Lb0E"; - } - else { # internal error - return length($TParam).$TParam; - } -} - -sub add_substitution($$$) -{ - my ($Value, $Repl, $Rec) = @_; - if($Rec) - { # subtypes - my @Subs = ($Value); - while($Value=~s/\A(R|P|K)//) { - push(@Subs, $Value); - } - foreach (reverse(@Subs)) { - add_substitution($_, $Repl, 0); - } - return; - } - return if($Value=~/\AS(\d*)_\Z/); - $Value=~s/\AN(.+)E\Z/$1/g; - return if(defined $Repl->{$Value}); - return if(length($Value)<=1); - return if($StdcxxMangling{$Value}); - # check for duplicates - my $Base = $Value; - foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl})) - { - my $Num = $Repl->{$Type}; - my $Replace = macro_mangle($Num); - $Base=~s/\Q$Replace\E/$Type/; - } - if(my $OldNum = $Repl->{$Base}) - { - $Repl->{$Value} = $OldNum; - return; - } - my @Repls = sort {$b<=>$a} values(%{$Repl}); - if(@Repls) { - $Repl->{$Value} = $Repls[0]+1; - } - else { - $Repl->{$Value} = -1; - } - # register duplicates - # upward - $Base = $Value; - foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl})) - { - next if($Base eq $Type); - my $Num = $Repl->{$Type}; - my $Replace = macro_mangle($Num); - $Base=~s/\Q$Type\E/$Replace/; - $Repl->{$Base} = $Repl->{$Value}; - } -} - -sub macro_mangle($) -{ - my $Num = $_[0]; - if($Num==-1) { - return "S_"; - } - else - { - my $Code = ""; - if($Num<10) - { # S0_, S1_, S2_, ... - $Code = $Num; - } - elsif($Num>=10 and $Num<=35) - { # SA_, SB_, SC_, ... - $Code = chr(55+$Num); + $Signature .= ""; } else - { # S10_, S11_, S12_ - $Code = $Num-26; # 26 is length of english alphabet - } - return "S".$Code."_"; - } -} - -sub write_stdcxx_substitution($) -{ - my $Mangled = $_[0]; - if($StdcxxMangling{$Mangled}) { - return $StdcxxMangling{$Mangled}; - } - else - { - my @Repls = keys(%StdcxxMangling); - @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls; - foreach my $MangledType (@Repls) - { - my $Replace = $StdcxxMangling{$MangledType}; - #if($Mangled!~/$Replace/) { - $Mangled=~s/N\Q$MangledType\EE/$Replace/g; - $Mangled=~s/\Q$MangledType\E/$Replace/g; - #} - } - } - return $Mangled; -} - -sub write_substitution($$) -{ - my ($Mangled, $Repl) = @_; - if(defined $Repl->{$Mangled} - and my $MnglNum = $Repl->{$Mangled}) { - $Mangled = macro_mangle($MnglNum); - } - else - { - my @Repls = keys(%{$Repl}); - #@Repls = sort {$Repl->{$a}<=>$Repl->{$b}} @Repls; - # FIXME: how to apply replacements? by num or by pos - @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls; - foreach my $MangledType (@Repls) - { - my $Replace = macro_mangle($Repl->{$MangledType}); - if($Mangled!~/$Replace/) { - $Mangled=~s/N\Q$MangledType\EE/$Replace/g; - $Mangled=~s/\Q$MangledType\E/$Replace/g; - } - } - } - return $Mangled; -} - -sub delete_keywords($) -{ - my $TypeName = $_[0]; - $TypeName=~s/\b(enum|struct|union|class) //g; - return $TypeName; -} - -sub uncover_typedefs($$) -{ - my ($TypeName, $LibVersion) = @_; - return "" if(not $TypeName); - if(defined $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName}) { - return $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName}; - } - my ($TypeName_New, $TypeName_Pre) = (formatName($TypeName, "T"), ""); - while($TypeName_New ne $TypeName_Pre) - { - $TypeName_Pre = $TypeName_New; - my $TypeName_Copy = $TypeName_New; - my %Words = (); - while($TypeName_Copy=~s/\b([a-z_]([\w:]*\w|))\b//io) - { - if(not $Intrinsic_Keywords{$1}) { - $Words{$1} = 1; - } - } - foreach my $Word (keys(%Words)) { - my $BaseType_Name = $Typedef_BaseName{$LibVersion}{$Word}; - next if(not $BaseType_Name); - next if($TypeName_New=~/\b(struct|union|enum)\s\Q$Word\E\b/); - if($BaseType_Name=~/\([\*]+\)/) - { # FuncPtr - if($TypeName_New=~/\Q$Word\E(.*)\Z/) - { - my $Type_Suffix = $1; - $TypeName_New = $BaseType_Name; - if($TypeName_New=~s/\(([\*]+)\)/($1 $Type_Suffix)/) { - $TypeName_New = formatName($TypeName_New, "T"); - } - } - } - else - { - if($TypeName_New=~s/\b\Q$Word\E\b/$BaseType_Name/g) { - $TypeName_New = formatName($TypeName_New, "T"); - } - } - } - } - return ($Cache{"uncover_typedefs"}{$LibVersion}{$TypeName} = $TypeName_New); -} - -sub isInternal($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/mngl[ ]*:[ ]*@(\d+) /) - { - if($LibInfo{$Version}{"info"}{$1}=~/\*[ ]*INTERNAL[ ]*\*/) - { # _ZN7mysqlpp8DateTimeC1ERKS0_ *INTERNAL* - return 1; - } - } - } - return 0; -} - -sub getDataVal($$) -{ - my ($InfoId, $TypeId) = @_; - if(my $Info = $LibInfo{$Version}{"info"}{$InfoId}) - { - if($Info=~/init[ ]*:[ ]*@(\d+) /) - { - if(defined $LibInfo{$Version}{"info_type"}{$1} - and $LibInfo{$Version}{"info_type"}{$1} eq "nop_expr") - { - if(my $Nop = getTreeAttr_Op($1)) - { - if(defined $LibInfo{$Version}{"info_type"}{$Nop} - and $LibInfo{$Version}{"info_type"}{$Nop} eq "addr_expr") - { - if(my $Addr = getTreeAttr_Op($1)) { - return getInitVal($Addr, $TypeId); - } - } - } + if(@Params) { + $Signature .= " ( ".join(", ", @Params)." )"; } else { - return getInitVal($1, $TypeId); + $Signature .= " ( )"; } } - } - return undef; -} - -sub getInitVal($$) -{ - my ($InfoId, $TypeId) = @_; - if(my $Info = $LibInfo{$Version}{"info"}{$InfoId}) - { - if(my $InfoType = $LibInfo{$Version}{"info_type"}{$InfoId}) - { - if($InfoType eq "integer_cst") - { - my $Val = getNodeIntCst($InfoId); - if($TypeId and $TypeInfo{$Version}{$TypeId}{"Name"}=~/\Achar(| const)\Z/) - { # characters - $Val = chr($Val); - } - return $Val; - } - elsif($InfoType eq "string_cst") { - return getNodeStrCst($InfoId); - } - elsif($InfoType eq "var_decl") - { - if(my $Name = getNodeStrCst(getTreeAttr_Mngl($InfoId))) { - return $Name; - } - } - } - } - return undef; -} - -sub set_Class_And_Namespace($) -{ - my $InfoId = $_[0]; - if(my $Info = $LibInfo{$Version}{"info"}{$InfoId}) - { - if($Info=~/scpe[ ]*:[ ]*@(\d+) /) - { - my $NSInfoId = $1; - if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId}) - { - if($InfoType eq "namespace_decl") { - $SymbolInfo{$Version}{$InfoId}{"NameSpace"} = getNameSpace($InfoId); - } - elsif($InfoType eq "record_type") { - $SymbolInfo{$Version}{$InfoId}{"Class"} = $NSInfoId; - } - } - } - } - if($SymbolInfo{$Version}{$InfoId}{"Class"} - or $SymbolInfo{$Version}{$InfoId}{"NameSpace"}) - { - if($COMMON_LANGUAGE{$Version} ne "C++") - { # skip - return 1; - } - } - - return 0; -} - -sub debugMangling($) -{ - my $LibVersion = $_[0]; - my %Mangled = (); - foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}})) - { - if(my $Mngl = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"}) - { - if($Mngl=~/\A(_Z|\?)/) { - $Mangled{$Mngl}=$InfoId; - } - } - } - translateSymbols(keys(%Mangled), $LibVersion); - foreach my $Mngl (keys(%Mangled)) - { - my $U1 = modelUnmangled($Mangled{$Mngl}, "GCC"); - my $U2 = $tr_name{$Mngl}; - if($U1 ne $U2) { - printMsg("INFO", "INCORRECT MANGLING:\n $Mngl\n $U1\n $U2\n"); - } - } -} - -sub linkSymbol($) -{ # link symbols from shared libraries - # with the symbols from header files - my $InfoId = $_[0]; - # try to mangle symbol - if((not check_gcc($GCC_PATH, "4") and $SymbolInfo{$Version}{$InfoId}{"Class"}) - or (check_gcc($GCC_PATH, "4") and not $SymbolInfo{$Version}{$InfoId}{"Class"}) - or $EMERGENCY_MODE_48) - { # GCC 3.x doesn't mangle class methods names in the TU dump (only functions and global data) - # GCC 4.x doesn't mangle C++ functions in the TU dump (only class methods) except extern "C" functions - # GCC 4.8 doesn't mangle anything - if(not $CheckHeadersOnly) - { - if(my $Mangled = $mangled_name_gcc{modelUnmangled($InfoId, "GCC")}) { - return correct_incharge($InfoId, $Version, $Mangled); - } - } - if($CheckHeadersOnly - or not $BinaryOnly - or $EMERGENCY_MODE_48) - { # 1. --headers-only mode - # 2. not mangled src-only symbols - if(my $Mangled = mangle_symbol($InfoId, $Version, "GCC")) { - return $Mangled; - } - } - } - return ""; -} - -sub setLanguage($$) -{ - my ($LibVersion, $Lang) = @_; - if(not $UserLang) { - $COMMON_LANGUAGE{$LibVersion} = $Lang; - } -} - -sub getSymbolInfo($) -{ - my $InfoId = $_[0]; - if(isInternal($InfoId)) { - return; - } - ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId); - if(not $SymbolInfo{$Version}{$InfoId}{"Header"} - or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"})) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - setFuncAccess($InfoId); - setFuncKind($InfoId); - if($SymbolInfo{$Version}{$InfoId}{"PseudoTemplate"}) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - - $SymbolInfo{$Version}{$InfoId}{"Type"} = getFuncType($InfoId); - if(my $Return = getFuncReturn($InfoId)) - { - if(not defined $TypeInfo{$Version}{$Return} - or not $TypeInfo{$Version}{$Return}{"Name"}) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - $SymbolInfo{$Version}{$InfoId}{"Return"} = $Return; - } - if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"}) - { - if(defined $MissedTypedef{$Version}{$Rid}) - { - if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) { - $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid; - } - } - } - if(not $SymbolInfo{$Version}{$InfoId}{"Return"}) { - delete($SymbolInfo{$Version}{$InfoId}{"Return"}); - } - my $Orig = getFuncOrig($InfoId); - $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getFuncShortName($Orig); - if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "\._")!=-1) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - - if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "tmp_add_func")==0) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - - if(defined $TemplateInstance{$Version}{"Func"}{$Orig}) - { - my $Tmpl = $BasicTemplate{$Version}{$InfoId}; - my @TParams = getTParams($Orig, "Func"); - if(not @TParams) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - foreach my $Pos (0 .. $#TParams) - { - my $Val = $TParams[$Pos]; - $SymbolInfo{$Version}{$InfoId}{"TParam"}{$Pos}{"name"} = $Val; - - if($Tmpl) - { - if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos}) - { - $TemplateMap{$Version}{$InfoId}{$Arg} = $Val; - } - } - } - if($Tmpl) + if($Full or $ShowQuals) { - foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}})) - { - if($Pos>$#TParams) - { - my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos}; - $TemplateMap{$Version}{$InfoId}{$Arg} = ""; - } - } - } - - if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\Aoperator\W+\Z/) - { # operator<< , operator>> - $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= " "; - } - if(@TParams) { - $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<".join(", ", @TParams).">"; - } - else { - $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<...>"; - } - $SymbolInfo{$Version}{$InfoId}{"ShortName"} = formatName($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "S"); - } - else - { # support for GCC 3.4 - $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//; - } - if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId))) - { - if($OSgroup eq "windows") - { # cut the offset - $MnglName=~s/\@\d+\Z//g; - } - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName; - - # NOTE: mangling of some symbols may change depending on GCC version - # GCC 4.6: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2IT_EERKS_IT_E - # GCC 4.7: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2ERKS1_ - } - - if($SymbolInfo{$Version}{$InfoId}{"MnglName"} - and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - if(not $SymbolInfo{$Version}{$InfoId}{"Destructor"}) - { # destructors have an empty parameter list - my $Skip = setFuncParams($InfoId); - if($Skip) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - } - if($LibInfo{$Version}{"info"}{$InfoId}=~/ artificial /i) { - $SymbolInfo{$Version}{$InfoId}{"Artificial"} = 1; - } - - if(set_Class_And_Namespace($InfoId)) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - - if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - if(not defined $TypeInfo{$Version}{$ClassId} - or not $TypeInfo{$Version}{$ClassId}{"Name"}) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - } - if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i) - { # extern "C" - $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C"; - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"}; - } - if($UserLang and $UserLang eq "C") - { # --lang=C option - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"}; - } - if($COMMON_LANGUAGE{$Version} eq "C++") - { # correct mangled & short names - # C++ or --headers-only mode - if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\A__(comp|base|deleting)_(c|d)tor\Z/) - { # support for old GCC versions: reconstruct real names for constructors and destructors - $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getNameByInfo(getTypeDeclId($SymbolInfo{$Version}{$InfoId}{"Class"})); - $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//; - } - if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { # try to mangle symbol (link with libraries) - if(my $Mangled = linkSymbol($InfoId)) { - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled; - } - } - if($OStarget eq "windows") - { # link MS C++ symbols from library with GCC symbols from headers - if(my $Mangled1 = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")}) - { # exported symbols - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled1; - } - elsif(my $Mangled2 = mangle_symbol($InfoId, $Version, "MSVC")) - { # pure virtual symbols - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled2; - } - } - } - else - { # not mangled in C - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"}; - } - if(not $CheckHeadersOnly - and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function" - and not $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - my $Incorrect = 0; - - if($SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { - if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")==0 - and not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps")) - { # mangled in the TU dump, but not mangled in the library - $Incorrect = 1; - } - } - else - { - if($SymbolInfo{$Version}{$InfoId}{"Lang"} ne "C") - { # all C++ functions are not mangled in the TU dump - $Incorrect = 1; - } - } - if($Incorrect) - { - if(link_symbol($SymbolInfo{$Version}{$InfoId}{"ShortName"}, $Version, "-Deps")) { - $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"}; - } - } - } - if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { # can't detect symbol name - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - if(not $SymbolInfo{$Version}{$InfoId}{"Constructor"} - and my $Spec = getVirtSpec($Orig)) - { # identify virtual and pure virtual functions - # NOTE: constructors cannot be virtual - # NOTE: in GCC 4.7 D1 destructors have no virtual spec - # in the TU dump, so taking it from the original symbol - if(not ($SymbolInfo{$Version}{$InfoId}{"Destructor"} - and $SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/D2E/)) - { # NOTE: D2 destructors are not present in a v-table - $SymbolInfo{$Version}{$InfoId}{$Spec} = 1; - } - } - if(isInline($InfoId)) { - $SymbolInfo{$Version}{$InfoId}{"InLine"} = 1; - } - if(hasThrow($InfoId)) { - $SymbolInfo{$Version}{$InfoId}{"Throw"} = 1; - } - if($SymbolInfo{$Version}{$InfoId}{"Constructor"} - and my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - if(not $SymbolInfo{$Version}{$InfoId}{"InLine"} - and not $SymbolInfo{$Version}{$InfoId}{"Artificial"}) - { # inline or auto-generated constructor - delete($TypeInfo{$Version}{$ClassId}{"Copied"}); - } - } - if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { - if(not $ExtraDump) - { - if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version)) - { # non-target symbols - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - } - } - if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method" - or $SymbolInfo{$Version}{$InfoId}{"Constructor"} - or $SymbolInfo{$Version}{$InfoId}{"Destructor"} - or $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - if($SymbolInfo{$Version}{$InfoId}{"MnglName"}!~/\A(_Z|\?)/) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - } - if($SymbolInfo{$Version}{$InfoId}{"MnglName"}) - { - if($MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}}) - { # one instance for one mangled name only - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - else { - $MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}} = 1; - } - } - if($SymbolInfo{$Version}{$InfoId}{"Constructor"} - or $SymbolInfo{$Version}{$InfoId}{"Destructor"}) { - delete($SymbolInfo{$Version}{$InfoId}{"Return"}); - } - if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/ - and $SymbolInfo{$Version}{$InfoId}{"Class"}) - { - if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function") - { # static methods - $SymbolInfo{$Version}{$InfoId}{"Static"} = 1; - } - } - if(getFuncLink($InfoId) eq "Static") { - $SymbolInfo{$Version}{$InfoId}{"Static"} = 1; - } - if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/) - { - if(my $Unmangled = $tr_name{$SymbolInfo{$Version}{$InfoId}{"MnglName"}}) - { - if($Unmangled=~/\.\_\d/) - { - delete($SymbolInfo{$Version}{$InfoId}); - return; - } - } - } - - if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(V|)K/) { - $SymbolInfo{$Version}{$InfoId}{"Const"} = 1; - } - if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(K|)V/) { - $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1; - } - - if($WeakSymbols{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}}) { - $SymbolInfo{$Version}{$InfoId}{"Weak"} = 1; - } - - if($ExtraDump) { - $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId); - } -} - -sub guessHeader($) -{ - my $InfoId = $_[0]; - my $ShortName = $SymbolInfo{$Version}{$InfoId}{"ShortName"}; - my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"}; - my $ClassName = $ClassId?get_ShortClass($ClassId, $Version):""; - my $Header = $SymbolInfo{$Version}{$InfoId}{"Header"}; - if(my $HPath = $SymbolHeader{$Version}{$ClassName}{$ShortName}) - { - if(get_filename($HPath) eq $Header) - { - my $HDir = get_filename(get_dirname($HPath)); - if($HDir ne "include" - and $HDir=~/\A[a-z]+\Z/i) { - return join_P($HDir, $Header); - } - } - } - return $Header; -} - -sub isInline($) -{ # "body: undefined" in the tree - # -fkeep-inline-functions GCC option should be specified - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/ undefined /i) { - return 0; - } - } - return 1; -} - -sub hasThrow($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/type[ ]*:[ ]*@(\d+) /) { - return getTreeAttr_Unql($1, "unql"); - } - } - return 1; -} - -sub getTypeId($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/type[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub setTypeMemb($$) -{ - my ($TypeId, $TypeAttr) = @_; - my $TypeType = $TypeAttr->{"Type"}; - my ($Pos, $UnnamedPos) = (0, 0); - my $StaticFields = 0; - if($TypeType eq "Enum") - { - my $MInfoId = getTreeAttr_Csts($TypeId); - while($MInfoId) - { - $TypeAttr->{"Memb"}{$Pos}{"value"} = getEnumMembVal($MInfoId); - my $MembName = getTreeStr(getTreeAttr_Purp($MInfoId)); - $TypeAttr->{"Memb"}{$Pos}{"name"} = $MembName; - $EnumMembName_Id{$Version}{getTreeAttr_Valu($MInfoId)} = ($TypeAttr->{"NameSpace"})?$TypeAttr->{"NameSpace"}."::".$MembName:$MembName; - $MInfoId = getNextElem($MInfoId); - $Pos += 1; - } - } - elsif($TypeType=~/\A(Struct|Class|Union)\Z/) - { - my $MInfoId = getTreeAttr_Flds($TypeId); - while($MInfoId) - { - my $IType = $LibInfo{$Version}{"info_type"}{$MInfoId}; - my $MInfo = $LibInfo{$Version}{"info"}{$MInfoId}; - if(not $IType or $IType ne "field_decl") - { # search for fields, skip other stuff in the declaration - - if($IType eq "var_decl") - { # static field - $StaticFields = 1; - } - - $MInfoId = getNextElem($MInfoId); - next; - } - my $StructMembName = getTreeStr(getTreeAttr_Name($MInfoId)); - if(index($StructMembName, "_vptr.")==0) - { # virtual tables - $StructMembName = "_vptr"; - } - if(not $StructMembName) - { # unnamed fields - if(index($TypeAttr->{"Name"}, "_type_info_pseudo")==-1) - { - my $UnnamedTid = getTreeAttr_Type($MInfoId); - my $UnnamedTName = getNameByInfo(getTypeDeclId($UnnamedTid)); - if(isAnon($UnnamedTName)) - { # rename unnamed fields to unnamed0, unnamed1, ... - $StructMembName = "unnamed".($UnnamedPos++); - } - } - } - if(not $StructMembName) - { # unnamed fields and base classes - $MInfoId = getNextElem($MInfoId); - next; - } - my $MembTypeId = getTreeAttr_Type($MInfoId); - if(defined $MissedTypedef{$Version}{$MembTypeId}) - { - if(my $AddedTid = $MissedTypedef{$Version}{$MembTypeId}{"Tid"}) { - $MembTypeId = $AddedTid; - } - } - - $TypeAttr->{"Memb"}{$Pos}{"type"} = $MembTypeId; - $TypeAttr->{"Memb"}{$Pos}{"name"} = $StructMembName; - if((my $Access = getTreeAccess($MInfoId)) ne "public") - { # marked only protected and private, public by default - $TypeAttr->{"Memb"}{$Pos}{"access"} = $Access; - } - if($MInfo=~/spec:\s*mutable /) - { # mutable fields - $TypeAttr->{"Memb"}{$Pos}{"mutable"} = 1; - } - if(my $Algn = getAlgn($MInfoId)) { - $TypeAttr->{"Memb"}{$Pos}{"algn"} = $Algn; - } - if(my $BFSize = getBitField($MInfoId)) - { # in bits - $TypeAttr->{"Memb"}{$Pos}{"bitfield"} = $BFSize; - } - else - { # in bytes - if($TypeAttr->{"Memb"}{$Pos}{"algn"}==1) - { # template - delete($TypeAttr->{"Memb"}{$Pos}{"algn"}); - } - else { - $TypeAttr->{"Memb"}{$Pos}{"algn"} /= $BYTE_SIZE; - } + if($CompSign{$LVer}{$Symbol}{"Const"} + or $Symbol=~/\A_ZN(V|)K/) { + $Signature .= " const"; } - $MInfoId = getNextElem($MInfoId); - $Pos += 1; - } - } - - return $StaticFields; -} - -sub setFuncParams($) -{ - my $InfoId = $_[0]; - my $ParamInfoId = getTreeAttr_Args($InfoId); - - my $FType = getFuncType($InfoId); - - if($FType eq "Method") - { # check type of "this" pointer - my $ObjectTypeId = getTreeAttr_Type($ParamInfoId); - if(my $ObjectName = $TypeInfo{$Version}{$ObjectTypeId}{"Name"}) - { - if($ObjectName=~/\bconst(| volatile)\*const\b/) { - $SymbolInfo{$Version}{$InfoId}{"Const"} = 1; - } - if($ObjectName=~/\bvolatile\b/) { - $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1; - } - } - else - { # skip - return 1; - } - # skip "this"-parameter - # $ParamInfoId = getNextElem($ParamInfoId); - } - my ($Pos, $PPos, $Vtt_Pos) = (0, 0, -1); - while($ParamInfoId) - { # formal args - my $ParamTypeId = getTreeAttr_Type($ParamInfoId); - my $ParamName = getTreeStr(getTreeAttr_Name($ParamInfoId)); - if(not $ParamName) - { # unnamed - $ParamName = "p".($PPos+1); - } - if(defined $MissedTypedef{$Version}{$ParamTypeId}) - { - if(my $AddedTid = $MissedTypedef{$Version}{$ParamTypeId}{"Tid"}) { - $ParamTypeId = $AddedTid; - } - } - my $PType = $TypeInfo{$Version}{$ParamTypeId}{"Type"}; - if(not $PType or $PType eq "Unknown") { - return 1; - } - my $PTName = $TypeInfo{$Version}{$ParamTypeId}{"Name"}; - if(not $PTName) { - return 1; - } - if($PTName eq "void") { - last; - } - if($ParamName eq "__vtt_parm" - and $TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void const**") - { - $Vtt_Pos = $Pos; - $ParamInfoId = getNextElem($ParamInfoId); - next; - } - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId; - - if(my %Base = get_BaseType($ParamTypeId, $Version)) - { - if(defined $Base{"Template"}) { - return 1; + if($CompSign{$LVer}{$Symbol}{"Volatile"} + or $Symbol=~/\A_ZN(K|)V/) { + $Signature .= " volatile"; } } - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = $ParamName; - if(my $Algn = getAlgn($ParamInfoId)) { - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"algn"} = $Algn/$BYTE_SIZE; - } - if($LibInfo{$Version}{"info"}{$ParamInfoId}=~/spec:\s*register /) - { # foo(register type arg) - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"reg"} = 1; - } - $ParamInfoId = getNextElem($ParamInfoId); - $Pos += 1; - if($ParamName ne "this" or $FType ne "Method") { - $PPos += 1; - } - } - if(setFuncArgs($InfoId, $Vtt_Pos)) { - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = "-1"; - } - return 0; -} - -sub setFuncArgs($$) -{ - my ($InfoId, $Vtt_Pos) = @_; - my $FuncTypeId = getFuncTypeId($InfoId); - my $ParamListElemId = getTreeAttr_Prms($FuncTypeId); - my $FType = getFuncType($InfoId); - - if($FType eq "Method") - { - # skip "this"-parameter - # $ParamListElemId = getNextElem($ParamListElemId); - } - if(not $ParamListElemId) - { # foo(...) - return 1; - } - my $HaveVoid = 0; - my ($Pos, $PPos) = (0, 0); - while($ParamListElemId) - { # actual params: may differ from formal args - # formal int*const - # actual: int* - if($Vtt_Pos!=-1 and $Pos==$Vtt_Pos) - { - $Vtt_Pos=-1; - $ParamListElemId = getNextElem($ParamListElemId); - next; - } - my $ParamTypeId = getTreeAttr_Valu($ParamListElemId); - if($TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void") - { - $HaveVoid = 1; - last; - } - else - { - if(not defined $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"}) - { - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId; - if(not $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"}) - { # unnamed - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = "p".($PPos+1); - } - } - elsif(my $OldId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"}) - { - if($Pos>0 or getFuncType($InfoId) ne "Method") - { # params - if($OldId ne $ParamTypeId) - { - my %Old_Pure = get_PureType($OldId, $TypeInfo{$Version}); - my %New_Pure = get_PureType($ParamTypeId, $TypeInfo{$Version}); - - if($Old_Pure{"Name"} ne $New_Pure{"Name"}) { - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId; - } - } - } - } - } - if(my $PurpId = getTreeAttr_Purp($ParamListElemId)) - { # default arguments - if(my $PurpType = $LibInfo{$Version}{"info_type"}{$PurpId}) - { - if($PurpType eq "nop_expr") - { # func ( const char* arg = (const char*)(void*)0 ) - $PurpId = getTreeAttr_Op($PurpId); - } - my $Val = getInitVal($PurpId, $ParamTypeId); - if(defined $Val) { - $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"default"} = $Val; - } - } - } - $ParamListElemId = getNextElem($ParamListElemId); - if($Pos!=0 or $FType ne "Method") { - $PPos += 1; - } - $Pos += 1; - } - return ($Pos>=1 and not $HaveVoid); -} - -sub getTreeAttr_Chan($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/chan[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Chain($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/chain[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Unql($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/unql[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Scpe($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/scpe[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Type($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/type[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Name($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/name[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Mngl($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/mngl[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Prms($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/prms[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Fncs($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/fncs[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Csts($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/csts[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Purp($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/purp[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Op($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/op 0[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Valu($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/valu[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Flds($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/flds[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Binf($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/binf[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAttr_Args($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/args[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return ""; -} - -sub getTreeValue($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/low[ ]*:[ ]*([^ ]+) /) { - return $1; - } - } - return ""; -} - -sub getTreeAccess($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/accs[ ]*:[ ]*([a-zA-Z]+) /) - { - my $Access = $1; - if($Access eq "prot") { - return "protected"; - } - elsif($Access eq "priv") { - return "private"; - } - } - elsif($Info=~/ protected /) - { # support for old GCC versions - return "protected"; - } - elsif($Info=~/ private /) - { # support for old GCC versions - return "private"; - } - } - return "public"; -} - -sub setFuncAccess($) -{ - my $Access = getTreeAccess($_[0]); - if($Access eq "protected") { - $SymbolInfo{$Version}{$_[0]}{"Protected"} = 1; - } - elsif($Access eq "private") { - $SymbolInfo{$Version}{$_[0]}{"Private"} = 1; - } -} - -sub setTypeAccess($$) -{ - my ($TypeId, $TypeAttr) = @_; - my $Access = getTreeAccess($TypeId); - if($Access eq "protected") { - $TypeAttr->{"Protected"} = 1; - } - elsif($Access eq "private") { - $TypeAttr->{"Private"} = 1; - } -} - -sub setFuncKind($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/pseudo tmpl/) { - $SymbolInfo{$Version}{$_[0]}{"PseudoTemplate"} = 1; - } - elsif($Info=~/ constructor /) { - $SymbolInfo{$Version}{$_[0]}{"Constructor"} = 1; - } - elsif($Info=~/ destructor /) { - $SymbolInfo{$Version}{$_[0]}{"Destructor"} = 1; - } - } -} - -sub getVirtSpec($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/spec[ ]*:[ ]*pure /) { - return "PureVirt"; - } - elsif($Info=~/spec[ ]*:[ ]*virt /) { - return "Virt"; - } - elsif($Info=~/ pure\s+virtual /) - { # support for old GCC versions - return "PureVirt"; - } - elsif($Info=~/ virtual /) - { # support for old GCC versions - return "Virt"; - } - } - return ""; -} - -sub getFuncLink($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/link[ ]*:[ ]*static /) { - return "Static"; - } - elsif($Info=~/link[ ]*:[ ]*([a-zA-Z]+) /) { - return $1; - } - } - return ""; -} - -sub select_Symbol_NS($$) -{ - my ($Symbol, $LibVersion) = @_; - return "" if(not $Symbol or not $LibVersion); - my $NS = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"}; - if(not $NS) - { - if(my $Class = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) { - $NS = $TypeInfo{$LibVersion}{$Class}{"NameSpace"}; - } - } - if($NS) - { - if(defined $NestedNameSpaces{$LibVersion}{$NS}) { - return $NS; - } - else + if($Full or $ShowAttr) { - while($NS=~s/::[^:]+\Z//) - { - if(defined $NestedNameSpaces{$LibVersion}{$NS}) { - return $NS; - } + if($CompSign{$LVer}{$Symbol}{"Static"} + and $Mngl) { + $Signature .= " [static]"; } } } - return ""; -} - -sub select_Type_NS($$) -{ - my ($TypeName, $LibVersion) = @_; - return "" if(not $TypeName or not $LibVersion); - if(my $NS = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$TypeName}}{"NameSpace"}) + if($Full) { - if(defined $NestedNameSpaces{$LibVersion}{$NS}) { - return $NS; - } - else + if(defined $In::Opt{"ShowRetVal"}) { - while($NS=~s/::[^:]+\Z//) + if(my $ReturnId = $CompSign{$LVer}{$Symbol}{"Return"}) { - if(defined $NestedNameSpaces{$LibVersion}{$NS}) { - return $NS; + my $RName = $TypeInfo{$LVer}{$ReturnId}{"Name"}; + if($Simple) { + $Signature .= " : ".specChars($RName); } - } - } - } - return ""; -} - -sub getNameSpace($) -{ - my $InfoId = $_[0]; - if(my $NSInfoId = getTreeAttr_Scpe($InfoId)) - { - if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId}) - { - if($InfoType eq "namespace_decl") - { - if($LibInfo{$Version}{"info"}{$NSInfoId}=~/name[ ]*:[ ]*@(\d+) /) - { - my $NameSpace = getTreeStr($1); - if($NameSpace eq "::") - { # global namespace - return ""; - } - if(my $BaseNameSpace = getNameSpace($NSInfoId)) { - $NameSpace = $BaseNameSpace."::".$NameSpace; - } - $NestedNameSpaces{$Version}{$NameSpace} = 1; - return $NameSpace; + elsif($Html) { + $Signature .= "  :  ".specChars($RName).""; } else { - return ""; - } - } - elsif($InfoType ne "function_decl") - { # inside data type - my ($Name, $NameNS) = getTrivialName(getTypeDeclId($NSInfoId), $NSInfoId); - return $Name; - } - } - } - return ""; -} - -sub getEnumMembVal($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/valu[ ]*:[ ]*\@(\d+)/) - { - if(my $VInfo = $LibInfo{$Version}{"info"}{$1}) - { - if($VInfo=~/cnst[ ]*:[ ]*\@(\d+)/) - { # in newer versions of GCC the value is in the "const_decl->cnst" node - return getTreeValue($1); - } - else - { # some old versions of GCC (3.3) have the value in the "integer_cst" node - return getTreeValue($1); + $Signature .= " : ".$RName; } } } - } - return ""; -} - -sub getSize($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/size[ ]*:[ ]*\@(\d+)/) { - return getTreeValue($1); - } - } - return 0; -} - -sub getAlgn($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/algn[ ]*:[ ]*(\d+) /) { - return $1; - } - } - return ""; -} - -sub getBitField($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/ bitfield /) { - return getSize($_[0]); - } - } - return 0; -} - -sub getNextElem($) -{ - if(my $Chan = getTreeAttr_Chan($_[0])) { - return $Chan; - } - elsif(my $Chain = getTreeAttr_Chain($_[0])) { - return $Chain; - } - return ""; -} - -sub registerHeader($$) -{ # input: absolute path of header, relative path or name - my ($Header, $LibVersion) = @_; - if(not $Header) { - return ""; - } - if(is_abs($Header) and not -f $Header) - { # incorrect absolute path - exitStatus("Access_Error", "can't access \'$Header\'"); - } - if(skipHeader($Header, $LibVersion)) - { # skip - return ""; - } - if(my $Header_Path = identifyHeader($Header, $LibVersion)) - { - detect_header_includes($Header_Path, $LibVersion); - - if(defined $Tolerance and $Tolerance=~/3/) - { # 3 - skip headers that include non-Linux headers - if($OSgroup ne "windows") - { - foreach my $Inc (keys(%{$Header_Includes{$LibVersion}{$Header_Path}})) - { - if(specificHeader($Inc, "windows")) { - return ""; - } - } - } - } - - if(my $RHeader_Path = $Header_ErrorRedirect{$LibVersion}{$Header_Path}) - { # redirect - if($Registered_Headers{$LibVersion}{$RHeader_Path}{"Identity"} - or skipHeader($RHeader_Path, $LibVersion)) - { # skip - return ""; - } - $Header_Path = $RHeader_Path; - } - elsif($Header_ShouldNotBeUsed{$LibVersion}{$Header_Path}) - { # skip - return ""; - } - - if(my $HName = get_filename($Header_Path)) - { # register - $Registered_Headers{$LibVersion}{$Header_Path}{"Identity"} = $HName; - $HeaderName_Paths{$LibVersion}{$HName}{$Header_Path} = 1; - } - - if(($Header=~/\.(\w+)\Z/ and $1 ne "h") - or $Header!~/\.(\w+)\Z/) - { # hpp, hh, etc. - setLanguage($LibVersion, "C++"); - $CPP_HEADERS = 1; - } - - if($CheckHeadersOnly - and $Header=~/(\A|\/)c\+\+(\/|\Z)/) - { # /usr/include/c++/4.6.1/... - $STDCXX_TESTING = 1; - } - return $Header_Path; - } - return ""; -} - -sub registerDir($$$) -{ - my ($Dir, $WithDeps, $LibVersion) = @_; - $Dir=~s/[\/\\]+\Z//g; - return if(not $LibVersion or not $Dir or not -d $Dir); - $Dir = get_abs_path($Dir); - - my $Mode = "All"; - if($WithDeps) - { - if($RegisteredDirs{$LibVersion}{$Dir}{1}) { - return; - } - elsif($RegisteredDirs{$LibVersion}{$Dir}{0}) { - $Mode = "DepsOnly"; - } - } - else - { - if($RegisteredDirs{$LibVersion}{$Dir}{1} - or $RegisteredDirs{$LibVersion}{$Dir}{0}) { - return; - } - } - $Header_Dependency{$LibVersion}{$Dir} = 1; - $RegisteredDirs{$LibVersion}{$Dir}{$WithDeps} = 1; - if($Mode eq "DepsOnly") - { - foreach my $Path (cmd_find($Dir,"d")) { - $Header_Dependency{$LibVersion}{$Path} = 1; - } - return; - } - foreach my $Path (sort {length($b)<=>length($a)} cmd_find($Dir,"f")) - { - if($WithDeps) - { - my $SubDir = $Path; - while(($SubDir = get_dirname($SubDir)) ne $Dir) - { # register all sub directories - $Header_Dependency{$LibVersion}{$SubDir} = 1; - } - } - next if(is_not_header($Path)); - next if(ignore_path($Path)); - # Neighbors - foreach my $Part (get_prefixes($Path)) { - $Include_Neighbors{$LibVersion}{$Part} = $Path; - } - } - if(get_filename($Dir) eq "include") - { # search for "lib/include/" directory - my $LibDir = $Dir; - if($LibDir=~s/([\/\\])include\Z/$1lib/g and -d $LibDir) { - registerDir($LibDir, $WithDeps, $LibVersion); - } - } -} - -sub parse_redirect($$$) -{ - my ($Content, $Path, $LibVersion) = @_; - my @Errors = (); - while($Content=~s/#\s*error\s+([^\n]+?)\s*(\n|\Z)//) { - push(@Errors, $1); - } - my $Redirect = ""; - foreach (@Errors) - { - s/\s{2,}/ /g; - if(/(only|must\ include - |update\ to\ include - |replaced\ with - |replaced\ by|renamed\ to - |\ is\ in|\ use)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))/ix) - { - $Redirect = $2; - last; - } - elsif(/(include|use|is\ in)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))\ instead/i) - { - $Redirect = $2; - last; - } - elsif(/this\ header\ should\ not\ be\ used - |programs\ should\ not\ directly\ include - |you\ should\ not\ (include|be\ (including|using)\ this\ (file|header)) - |is\ not\ supported\ API\ for\ general\ use - |do\ not\ use - |should\ not\ be\ (used|using) - |cannot\ be\ included\ directly/ix and not /\ from\ /i) { - $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1; - } - } - if($Redirect) - { - $Redirect=~s/\A\Z//g; - } - return $Redirect; -} - -sub parse_includes($$) -{ - my ($Content, $Path) = @_; - my %Includes = (); - while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]*([<"].+?[">])[ \t]*//m) - { # C/C++: include, Objective C/C++: import directive - my $Header = $2; - my $Method = substr($Header, 0, 1, ""); - substr($Header, length($Header)-1, 1, ""); - $Header = path_format($Header, $OSgroup); - if($Method eq "\"" or is_abs($Header)) + if($SVer) { - if(-e join_P(get_dirname($Path), $Header)) - { # relative path exists - $Includes{$Header} = -1; - } - else - { # include "..." that doesn't exist is equal to include <...> - $Includes{$Header} = 2; - } - } - else { - $Includes{$Header} = 1; - } - } - if($ExtraInfo) - { - while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]+(\w+)[ \t]*//m) - { # FT_FREETYPE_H - $Includes{$2} = 0; - } - } - return \%Includes; -} - -sub ignore_path($) -{ - my $Path = $_[0]; - if($Path=~/\~\Z/) - {# skipping system backup files - return 1; - } - if($Path=~/(\A|[\/\\]+)(\.(svn|git|bzr|hg)|CVS)([\/\\]+|\Z)/) - {# skipping hidden .svn, .git, .bzr, .hg and CVS directories - return 1; - } - return 0; -} - -sub sortByWord($$) -{ - my ($ArrRef, $W) = @_; - return if(length($W)<2); - @{$ArrRef} = sort {get_filename($b)=~/\Q$W\E/i<=>get_filename($a)=~/\Q$W\E/i} @{$ArrRef}; -} - -sub sortHeaders($$) -{ - my ($H1, $H2) = @_; - - $H1=~s/\.[a-z]+\Z//ig; - $H2=~s/\.[a-z]+\Z//ig; - - my $Hname1 = get_filename($H1); - my $Hname2 = get_filename($H2); - my $HDir1 = get_dirname($H1); - my $HDir2 = get_dirname($H2); - my $Dirname1 = get_filename($HDir1); - my $Dirname2 = get_filename($HDir2); - - $HDir1=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/; - $HDir2=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/; - - if($_[0] eq $_[1] - or $H1 eq $H2) { - return 0; - } - elsif($H1=~/\A\Q$H2\E/) { - return 1; - } - elsif($H2=~/\A\Q$H1\E/) { - return -1; - } - elsif($HDir1=~/\Q$Hname1\E/i - and $HDir2!~/\Q$Hname2\E/i) - { # include/glib-2.0/glib.h - return -1; - } - elsif($HDir2=~/\Q$Hname2\E/i - and $HDir1!~/\Q$Hname1\E/i) - { # include/glib-2.0/glib.h - return 1; - } - elsif($Hname1=~/\Q$Dirname1\E/i - and $Hname2!~/\Q$Dirname2\E/i) - { # include/hildon-thumbnail/hildon-thumbnail-factory.h - return -1; - } - elsif($Hname2=~/\Q$Dirname2\E/i - and $Hname1!~/\Q$Dirname1\E/i) - { # include/hildon-thumbnail/hildon-thumbnail-factory.h - return 1; - } - elsif($Hname1=~/(config|lib|util)/i - and $Hname2!~/(config|lib|util)/i) - { # include/alsa/asoundlib.h - return -1; - } - elsif($Hname2=~/(config|lib|util)/i - and $Hname1!~/(config|lib|util)/i) - { # include/alsa/asoundlib.h - return 1; - } - else - { - my $R1 = checkRelevance($H1); - my $R2 = checkRelevance($H2); - if($R1 and not $R2) - { # libebook/e-book.h - return -1; - } - elsif($R2 and not $R1) - { # libebook/e-book.h - return 1; - } - else - { - return (lc($H1) cmp lc($H2)); - } - } -} - -sub searchForHeaders($) -{ - my $LibVersion = $_[0]; - - # gcc standard include paths - registerGccHeaders(); - - if($COMMON_LANGUAGE{$LibVersion} eq "C++" and not $STDCXX_TESTING) - { # c++ standard include paths - registerCppHeaders(); - } - - # processing header paths - foreach my $Path (@{$Descriptor{$LibVersion}{"IncludePaths"}}, - @{$Descriptor{$LibVersion}{"AddIncludePaths"}}) - { - my $IPath = $Path; - if($SystemRoot) - { - if(is_abs($Path)) { - $Path = $SystemRoot.$Path; - } - } - if(not -e $Path) { - exitStatus("Access_Error", "can't access \'$Path\'"); - } - elsif(-f $Path) { - exitStatus("Access_Error", "\'$Path\' - not a directory"); - } - elsif(-d $Path) - { - $Path = get_abs_path($Path); - registerDir($Path, 0, $LibVersion); - if(grep {$IPath eq $_} @{$Descriptor{$LibVersion}{"AddIncludePaths"}}) { - push(@{$Add_Include_Paths{$LibVersion}}, $Path); - } - else { - push(@{$Include_Paths{$LibVersion}}, $Path); - } - } - } - if(@{$Include_Paths{$LibVersion}}) { - $INC_PATH_AUTODETECT{$LibVersion} = 0; - } - - # registering directories - foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"})) - { - next if(not -e $Path); - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - if(-d $Path) { - registerDir($Path, 1, $LibVersion); - } - elsif(-f $Path) - { - my $Dir = get_dirname($Path); - if(not grep { $Dir eq $_ } (@{$SystemPaths{"include"}}) - and not $LocalIncludes{$Dir}) - { - registerDir($Dir, 1, $LibVersion); - # if(my $OutDir = get_dirname($Dir)) - # { # registering the outer directory - # if(not grep { $OutDir eq $_ } (@{$SystemPaths{"include"}}) - # and not $LocalIncludes{$OutDir}) { - # registerDir($OutDir, 0, $LibVersion); - # } - # } - } - } - } - - # clean memory - %RegisteredDirs = (); - - # registering headers - my $Position = 0; - foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"})) - { - if(is_abs($Dest) and not -e $Dest) { - exitStatus("Access_Error", "can't access \'$Dest\'"); - } - $Dest = path_format($Dest, $OSgroup); - if(is_header($Dest, 1, $LibVersion)) - { - if(my $HPath = registerHeader($Dest, $LibVersion)) { - $Registered_Headers{$LibVersion}{$HPath}{"Pos"} = $Position++; - } - } - elsif(-d $Dest) - { - my @Registered = (); - foreach my $Path (cmd_find($Dest,"f")) - { - next if(ignore_path($Path)); - next if(not is_header($Path, 0, $LibVersion)); - if(my $HPath = registerHeader($Path, $LibVersion)) { - push(@Registered, $HPath); - } - } - @Registered = sort {sortHeaders($a, $b)} @Registered; - sortByWord(\@Registered, $TargetLibraryShortName); - foreach my $Path (@Registered) { - $Registered_Headers{$LibVersion}{$Path}{"Pos"} = $Position++; - } - } - else { - exitStatus("Access_Error", "can't identify \'$Dest\' as a header file"); - } - } - - if(defined $Tolerance and $Tolerance=~/4/) - { # 4 - skip headers included by others - foreach my $Path (keys(%{$Registered_Headers{$LibVersion}})) - { - if(defined $Header_Includes_R{$LibVersion}{$Path}) { - delete($Registered_Headers{$LibVersion}{$Path}); - } - } - } - - if(my $HList = $Descriptor{$LibVersion}{"IncludePreamble"}) - { # preparing preamble headers - foreach my $Header (split(/\s*\n\s*/, $HList)) - { - if(is_abs($Header) and not -f $Header) { - exitStatus("Access_Error", "can't access file \'$Header\'"); - } - $Header = path_format($Header, $OSgroup); - if(my $Header_Path = is_header($Header, 1, $LibVersion)) - { - next if(skipHeader($Header_Path, $LibVersion)); - push_U($Include_Preamble{$LibVersion}, $Header_Path); + if($Html) { + $Signature .= " $VSpec $SVer"; } else { - exitStatus("Access_Error", "can't identify \'$Header\' as a header file"); - } - } - } - foreach my $Header_Name (keys(%{$HeaderName_Paths{$LibVersion}})) - { # set relative paths (for duplicates) - if(keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})>=2) - { # search for duplicates - my $FirstPath = (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))[0]; - my $Prefix = get_dirname($FirstPath); - while($Prefix=~/\A(.+)[\/\\]+[^\/\\]+\Z/) - { # detect a shortest distinguishing prefix - my $NewPrefix = $1; - my %Identity = (); - foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})) - { - if($Path=~/\A\Q$Prefix\E[\/\\]+(.*)\Z/) { - $Identity{$Path} = $1; - } - } - if(keys(%Identity)==keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})) - { # all names are different with current prefix - foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})) { - $Registered_Headers{$LibVersion}{$Path}{"Identity"} = $Identity{$Path}; - } - last; - } - $Prefix = $NewPrefix; # increase prefix - } - } - } - - # clean memory - %HeaderName_Paths = (); - - foreach my $HeaderName (keys(%{$Include_Order{$LibVersion}})) - { # ordering headers according to descriptor - my $PairName = $Include_Order{$LibVersion}{$HeaderName}; - my ($Pos, $PairPos) = (-1, -1); - my ($Path, $PairPath) = (); - my @Paths = keys(%{$Registered_Headers{$LibVersion}}); - @Paths = sort {int($Registered_Headers{$LibVersion}{$a}{"Pos"})<=>int($Registered_Headers{$LibVersion}{$b}{"Pos"})} @Paths; - foreach my $Header_Path (@Paths) - { - if(get_filename($Header_Path) eq $PairName) - { - $PairPos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"}; - $PairPath = $Header_Path; - } - if(get_filename($Header_Path) eq $HeaderName) - { - $Pos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"}; - $Path = $Header_Path; - } - } - if($PairPos!=-1 and $Pos!=-1 - and int($PairPos)) - { - if(/#\s+\d+\s+"([^"]+)"[\s\d]*\n/) - { - my $Include = path_format($1, $OSgroup); - if($Include=~/\<(built\-in|internal|command(\-|\s)line)\>|\A\./) { - next; - } - if($Include eq $AbsPath) { - next; - } - $RecursiveIncludes{$LibVersion}{$AbsPath}{$Include} = 1; - } - } - close(PREPROC); - return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}); -} - -sub detect_header_includes($$) -{ - my ($Path, $LibVersion) = @_; - return if(not $LibVersion or not $Path); - if(defined $Cache{"detect_header_includes"}{$LibVersion}{$Path}) { - return; - } - $Cache{"detect_header_includes"}{$LibVersion}{$Path}=1; - - if(not -e $Path) { - return; - } - - my $Content = readFile($Path); - if(my $Redirect = parse_redirect($Content, $Path, $LibVersion)) - { # detect error directive in headers - if(my $RedirectPath = identifyHeader($Redirect, $LibVersion)) - { - if($RedirectPath=~/\/usr\/include\// and $Path!~/\/usr\/include\//) { - $RedirectPath = identifyHeader(get_filename($Redirect), $LibVersion); + $Signature .= $VSpec.$SVer; } - if($RedirectPath ne $Path) { - $Header_ErrorRedirect{$LibVersion}{$Path} = $RedirectPath; - } - } - else - { # can't find - $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1; - } - } - if(my $Inc = parse_includes($Content, $Path)) - { - foreach my $Include (keys(%{$Inc})) - { # detect includes - $Header_Includes{$LibVersion}{$Path}{$Include} = $Inc->{$Include}; - - if(defined $Tolerance and $Tolerance=~/4/) - { - if(my $HPath = identifyHeader($Include, $LibVersion)) - { - $Header_Includes_R{$LibVersion}{$HPath}{$Path} = 1; - } - } - } - } -} - -sub fromLibc($) -{ # system GLIBC header - my $Path = $_[0]; - my ($Dir, $Name) = separate_path($Path); - if($OStarget eq "symbian") - { - if(get_filename($Dir) eq "libc" and $GlibcHeader{$Name}) - { # epoc32/include/libc/{stdio, ...}.h - return 1; - } - } - else - { - if($Dir eq "/usr/include" and $GlibcHeader{$Name}) - { # /usr/include/{stdio, ...}.h - return 1; - } - } - return 0; -} - -sub isLibcDir($) -{ # system GLIBC directory - my $Dir = $_[0]; - my ($OutDir, $Name) = separate_path($Dir); - if($OStarget eq "symbian") - { - if(get_filename($OutDir) eq "libc" - and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name})) - { # epoc32/include/libc/{sys,bits,asm,asm-*}/*.h - return 1; - } - } - else - { # linux - if($OutDir eq "/usr/include" - and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name})) - { # /usr/include/{sys,bits,asm,asm-*}/*.h - return 1; - } - } - return 0; -} - -sub detect_recursive_includes($$) -{ - my ($AbsPath, $LibVersion) = @_; - return () if(not $AbsPath); - if(isCyclical(\@RecurInclude, $AbsPath)) { - return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}); - } - my ($AbsDir, $Name) = separate_path($AbsPath); - if(isLibcDir($AbsDir)) - { # system GLIBC internals - return () if(not $ExtraInfo); - } - if(keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) { - return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}); - } - return () if($OSgroup ne "windows" and $Name=~/windows|win32|win64/i); - - if($MAIN_CPP_DIR and $AbsPath=~/\A\Q$MAIN_CPP_DIR\E/ and not $STDCXX_TESTING) - { # skip /usr/include/c++/*/ headers - return () if(not $ExtraInfo); - } - - push(@RecurInclude, $AbsPath); - if(grep { $AbsDir eq $_ } @DefaultGccPaths - or (grep { $AbsDir eq $_ } @DefaultIncPaths and fromLibc($AbsPath))) - { # check "real" (non-"model") include paths - my @Paths = detect_real_includes($AbsPath, $LibVersion); - pop(@RecurInclude); - return @Paths; - } - if(not keys(%{$Header_Includes{$LibVersion}{$AbsPath}})) { - detect_header_includes($AbsPath, $LibVersion); - } - foreach my $Include (keys(%{$Header_Includes{$LibVersion}{$AbsPath}})) - { - my $IncType = $Header_Includes{$LibVersion}{$AbsPath}{$Include}; - my $HPath = ""; - if($IncType<0) - { # for #include "..." - my $Candidate = join_P($AbsDir, $Include); - if(-f $Candidate) { - $HPath = realpath($Candidate); - } - } - elsif($IncType>0 - and $Include=~/[\/\\]/) # and not find_in_defaults($Include) - { # search for the nearest header - # QtCore/qabstractanimation.h includes - my $Candidate = join_P(get_dirname($AbsDir), $Include); - if(-f $Candidate) { - $HPath = $Candidate; - } - } - if(not $HPath) { - $HPath = identifyHeader($Include, $LibVersion); - } - next if(not $HPath); - if($HPath eq $AbsPath) { - next; - } - - if($Debug) - { # boundary headers -# if($HPath=~/vtk/ and $AbsPath!~/vtk/) -# { -# print STDERR "$AbsPath -> $HPath\n"; -# } - } - - $RecursiveIncludes{$LibVersion}{$AbsPath}{$HPath} = $IncType; - if($IncType>0) - { # only include <...>, skip include "..." prefixes - $Header_Include_Prefix{$LibVersion}{$AbsPath}{$HPath}{get_dirname($Include)} = 1; } - foreach my $IncPath (detect_recursive_includes($HPath, $LibVersion)) - { - if($IncPath eq $AbsPath) { - next; - } - my $RIncType = $RecursiveIncludes{$LibVersion}{$HPath}{$IncPath}; - if($RIncType==-1) - { # include "..." - $RIncType = $IncType; - } - elsif($RIncType==2) - { - if($IncType!=-1) { - $RIncType = $IncType; - } - } - $RecursiveIncludes{$LibVersion}{$AbsPath}{$IncPath} = $RIncType; - foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$HPath}{$IncPath}})) { - $Header_Include_Prefix{$LibVersion}{$AbsPath}{$IncPath}{$Prefix} = 1; - } - } - foreach my $Dep (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}})) - { - if($GlibcHeader{get_filename($Dep)} and keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}})>=2 - and defined $Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""}) - { # distinguish math.h from glibc and math.h from the tested library - delete($Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""}); - last; - } - } - } - pop(@RecurInclude); - return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}); -} - -sub find_in_framework($$$) -{ - my ($Header, $Framework, $LibVersion) = @_; - return "" if(not $Header or not $Framework or not $LibVersion); - if(defined $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header}) { - return $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header}; - } - foreach my $Dependency (sort {get_depth($a)<=>get_depth($b)} keys(%{$Header_Dependency{$LibVersion}})) - { - if(get_filename($Dependency) eq $Framework - and -f get_dirname($Dependency)."/".$Header) { - return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = get_dirname($Dependency)); - } - } - return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = ""); -} - -sub find_in_defaults($) -{ - my $Header = $_[0]; - return "" if(not $Header); - if(defined $Cache{"find_in_defaults"}{$Header}) { - return $Cache{"find_in_defaults"}{$Header}; - } - foreach my $Dir (@DefaultIncPaths, - @DefaultGccPaths, - @DefaultCppPaths, - @UsersIncPath) - { - next if(not $Dir); - if(-f $Dir."/".$Header) { - return ($Cache{"find_in_defaults"}{$Header}=$Dir); - } - } - return ($Cache{"find_in_defaults"}{$Header}=""); -} - -sub cmp_paths($$) -{ - my ($Path1, $Path2) = @_; - my @Parts1 = split(/[\/\\]/, $Path1); - my @Parts2 = split(/[\/\\]/, $Path2); - foreach my $Num (0 .. $#Parts1) - { - my $Part1 = $Parts1[$Num]; - my $Part2 = $Parts2[$Num]; - if($GlibcDir{$Part1} - and not $GlibcDir{$Part2}) { - return 1; - } - elsif($GlibcDir{$Part2} - and not $GlibcDir{$Part1}) { - return -1; - } - elsif($Part1=~/glib/ - and $Part2!~/glib/) { - return 1; - } - elsif($Part1!~/glib/ - and $Part2=~/glib/) { - return -1; - } - elsif(my $CmpRes = ($Part1 cmp $Part2)) { - return $CmpRes; - } - } - return 0; -} - -sub checkRelevance($) -{ - my $Path = $_[0]; - return 0 if(not $Path); - - if($SystemRoot) { - $Path = cut_path_prefix($Path, $SystemRoot); } - my $Name = lc(get_filename($Path)); - my $Dir = lc(get_dirname($Path)); - - $Name=~s/\.\w+\Z//g; # remove extension (.h) - - foreach my $Token (split(/[_\d\W]+/, $Name)) - { - my $Len = length($Token); - next if($Len<=1); - if($Dir=~/(\A|lib|[_\d\W])\Q$Token\E([_\d\W]|lib|\Z)/) - { # include/evolution-data-server-1.4/libebook/e-book.h - return 1; - } - if($Len>=4 and index($Dir, $Token)!=-1) - { # include/gupnp-1.0/libgupnp/gupnp-context.h - return 1; - } - } - return 0; -} - -sub checkFamily(@) -{ - my @Paths = @_; - return 1 if($#Paths<=0); - my %Prefix = (); - foreach my $Path (@Paths) - { - if($SystemRoot) { - $Path = cut_path_prefix($Path, $SystemRoot); - } - if(my $Dir = get_dirname($Path)) - { - $Dir=~s/(\/[^\/]+?)[\d\.\-\_]+\Z/$1/g; # remove version suffix - $Prefix{$Dir} += 1; - $Prefix{get_dirname($Dir)} += 1; - } - } - foreach (sort keys(%Prefix)) - { - if(get_depth($_)>=3 - and $Prefix{$_}==$#Paths+1) { - return 1; - } - } - return 0; -} - -sub isAcceptable($$$) -{ - my ($Header, $Candidate, $LibVersion) = @_; - my $HName = get_filename($Header); - if(get_dirname($Header)) - { # with prefix - return 1; - } - if($HName=~/config|setup/i and $Candidate=~/[\/\\]lib\d*[\/\\]/) - { # allow to search for glibconfig.h in /usr/lib/glib-2.0/include/ - return 1; - } - if(checkRelevance($Candidate)) - { # allow to search for atk.h in /usr/include/atk-1.0/atk/ - return 1; - } - if(checkFamily(getSystemHeaders($HName, $LibVersion))) - { # /usr/include/qt4/QtNetwork/qsslconfiguration.h - # /usr/include/qt4/Qt/qsslconfiguration.h - return 1; - } - if($OStarget eq "symbian") - { - if($Candidate=~/[\/\\]stdapis[\/\\]/) { - return 1; - } - } - return 0; -} - -sub isRelevant($$$) -{ # disallow to search for "abstract" headers in too deep directories - my ($Header, $Candidate, $LibVersion) = @_; - my $HName = get_filename($Header); - if($OStarget eq "symbian") - { - if($Candidate=~/[\/\\](tools|stlportv5)[\/\\]/) { - return 0; - } - } - if($OStarget ne "bsd") - { - if($Candidate=~/[\/\\]include[\/\\]bsd[\/\\]/) - { # openssh: skip /usr/lib/bcc/include/bsd/signal.h - return 0; - } - } - if($OStarget ne "windows") - { - if($Candidate=~/[\/\\](wine|msvcrt|windows)[\/\\]/) - { # skip /usr/include/wine/msvcrt - return 0; - } - } - if(not get_dirname($Header) - and $Candidate=~/[\/\\]wx[\/\\]/) - { # do NOT search in system /wx/ directory - # for headers without a prefix: sstream.h - return 0; - } - if($Candidate=~/c\+\+[\/\\]\d+/ and $MAIN_CPP_DIR - and $Candidate!~/\A\Q$MAIN_CPP_DIR\E/) - { # skip ../c++/3.3.3/ if using ../c++/4.5/ - return 0; - } - if($Candidate=~/[\/\\]asm-/ - and (my $Arch = getArch($LibVersion)) ne "unknown") - { # arch-specific header files - if($Candidate!~/[\/\\]asm-\Q$Arch\E/) - {# skip ../asm-arm/ if using x86 architecture - return 0; - } - } - my @Candidates = getSystemHeaders($HName, $LibVersion); - if($#Candidates==1) - { # unique header - return 1; - } - my @SCandidates = getSystemHeaders($Header, $LibVersion); - if($#SCandidates==1) - { # unique name - return 1; - } - my $SystemDepth = $SystemRoot?get_depth($SystemRoot):0; - if(get_depth($Candidate)-$SystemDepth>=5) - { # abstract headers in too deep directories - # sstream.h or typeinfo.h in /usr/include/wx-2.9/wx/ - if(not isAcceptable($Header, $Candidate, $LibVersion)) { - return 0; - } - } - if($Header eq "parser.h" - and $Candidate!~/\/libxml2\//) - { # select parser.h from xml2 library - return 0; - } - if(not get_dirname($Header) - and keys(%{$SystemHeaders{$HName}})>=3) - { # many headers with the same name - # like thread.h included without a prefix - if(not checkFamily(@Candidates)) { - return 0; - } - } - return 1; -} - -sub selectSystemHeader($$) -{ # cache function - if(defined $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}) { - return $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}; - } - return ($Cache{"selectSystemHeader"}{$_[1]}{$_[0]} = selectSystemHeader_I(@_)); -} - -sub selectSystemHeader_I($$) -{ - my ($Header, $LibVersion) = @_; - if(-f $Header) { - return $Header; - } - if(is_abs($Header) and not -f $Header) - { # incorrect absolute path - return ""; - } - if(defined $ConfHeaders{lc($Header)}) - { # too abstract configuration headers - return ""; - } - my $HName = get_filename($Header); - if($OSgroup ne "windows") - { - if(defined $WinHeaders{lc($HName)} - or $HName=~/windows|win32|win64/i) - { # windows headers - return ""; - } - } - if($OSgroup ne "macos") - { - if($HName eq "fp.h") - { # pngconf.h includes fp.h in Mac OS - return ""; - } + if($Html) { + $Signature=~s!(\[C\d\]|\[D\d\]|\[static\]|\[data\])!$1!g; } - if(defined $ObsoleteHeaders{$HName}) - { # obsolete headers - return ""; + if($Simple) { + $Signature=~s/\[\]/\[ \]/g; } - if($OSgroup eq "linux" or $OSgroup eq "bsd") + elsif($Html) { - if(defined $AlienHeaders{$HName} - or defined $AlienHeaders{$Header}) - { # alien headers from other systems - return ""; - } + $Signature=~s!\[\]![ ]!g; + $Signature=~s!operator=!operator =!g; } - foreach my $Path (@{$SystemPaths{"include"}}) - { # search in default paths - if(-f $Path."/".$Header) { - return join_P($Path,$Header); - } - } - if(not defined $Cache{"checkSystemFiles"}) - { # register all headers in system include dirs - checkSystemFiles(); - } - foreach my $Candidate (sort {get_depth($a)<=>get_depth($b)} - sort {cmp_paths($b, $a)} getSystemHeaders($Header, $LibVersion)) - { - if(isRelevant($Header, $Candidate, $LibVersion)) { - return $Candidate; - } - } - # error - return ""; -} - -sub getSystemHeaders($$) -{ - my ($Header, $LibVersion) = @_; - my @Candidates = (); - foreach my $Candidate (sort keys(%{$SystemHeaders{$Header}})) - { - if(skipHeader($Candidate, $LibVersion)) { - next; - } - push(@Candidates, $Candidate); - } - return @Candidates; + return ($Cache{"getSignature"}{$LVer}{$Symbol}{$Kind} = $Signature); } -sub cut_path_prefix($$) -{ - my ($Path, $Prefix) = @_; - return $Path if(not $Prefix); - $Prefix=~s/[\/\\]+\Z//; - $Path=~s/\A\Q$Prefix\E([\/\\]+|\Z)//; - return $Path; -} - -sub is_default_include_dir($) -{ - my $Dir = $_[0]; - $Dir=~s/[\/\\]+\Z//; - return grep { $Dir eq $_ } (@DefaultGccPaths, @DefaultCppPaths, @DefaultIncPaths); -} - -sub identifyHeader($$) -{ # cache function - my ($Header, $LibVersion) = @_; - if(not $Header) { - return ""; - } - $Header=~s/\A(\.\.[\\\/])+//g; - if(defined $Cache{"identifyHeader"}{$LibVersion}{$Header}) { - return $Cache{"identifyHeader"}{$LibVersion}{$Header}; - } - return ($Cache{"identifyHeader"}{$LibVersion}{$Header} = identifyHeader_I($Header, $LibVersion)); -} - -sub identifyHeader_I($$) -{ # search for header by absolute path, relative path or name - my ($Header, $LibVersion) = @_; - if(-f $Header) - { # it's relative or absolute path - return get_abs_path($Header); - } - elsif($GlibcHeader{$Header} and not $GLIBC_TESTING - and my $HeaderDir = find_in_defaults($Header)) - { # search for libc headers in the /usr/include - # for non-libc target library before searching - # in the library paths - return join_P($HeaderDir,$Header); - } - elsif(my $Path = $Include_Neighbors{$LibVersion}{$Header}) - { # search in the target library paths - return $Path; - } - elsif(defined $DefaultGccHeader{$Header}) - { # search in the internal GCC include paths - return $DefaultGccHeader{$Header}; - } - elsif(my $DefaultDir = find_in_defaults($Header)) - { # search in the default GCC include paths - return join_P($DefaultDir,$Header); - } - elsif(defined $DefaultCppHeader{$Header}) - { # search in the default G++ include paths - return $DefaultCppHeader{$Header}; - } - elsif(my $AnyPath = selectSystemHeader($Header, $LibVersion)) - { # search everywhere in the system - return $AnyPath; - } - elsif($OSgroup eq "macos") - { # search in frameworks: "OpenGL/gl.h" is "OpenGL.framework/Headers/gl.h" - if(my $Dir = get_dirname($Header)) - { - my $RelPath = "Headers\/".get_filename($Header); - if(my $HeaderDir = find_in_framework($RelPath, $Dir.".framework", $LibVersion)) { - return join_P($HeaderDir, $RelPath); - } - } - } - # cannot find anything - return ""; -} - -sub getLocation($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/srcp[ ]*:[ ]*([\w\-\<\>\.\+\/\\]+):(\d+) /) { - return (path_format($1, $OSgroup), $2); - } - } - return (); -} - -sub getNameByInfo($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/name[ ]*:[ ]*@(\d+) /) - { - if(my $NInfo = $LibInfo{$Version}{"info"}{$1}) - { - if($NInfo=~/strg[ ]*:[ ]*(.*?)[ ]+lngt/) - { # short unsigned int (may include spaces) - my $Str = $1; - if($CppMode{$Version} - and $Str=~/\Ac99_(.+)\Z/) - { - if($CppKeywords_A{$1}) { - $Str=$1; - } - } - return $Str; - } - } - } - } - return ""; -} - -sub getTreeStr($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/strg[ ]*:[ ]*([^ ]*)/) - { - my $Str = $1; - if($CppMode{$Version} - and $Str=~/\Ac99_(.+)\Z/) - { - if($CppKeywords_A{$1}) { - $Str=$1; - } - } - return $Str; - } - } - return ""; -} - -sub getFuncShortName($) -{ - if(my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if(index($Info, " operator ")!=-1) - { - if(index($Info, " conversion ")!=-1) - { - if(my $Rid = $SymbolInfo{$Version}{$_[0]}{"Return"}) - { - if(my $RName = $TypeInfo{$Version}{$Rid}{"Name"}) { - return "operator ".$RName; - } - } - } - else - { - if($Info=~/ operator[ ]+([a-zA-Z]+) /) - { - if(my $Ind = $Operator_Indication{$1}) { - return "operator".$Ind; - } - elsif(not $UnknownOperator{$1}) - { - printMsg("WARNING", "unknown operator $1"); - $UnknownOperator{$1} = 1; - } - } - } - } - else - { - if($Info=~/name[ ]*:[ ]*@(\d+) /) { - return getTreeStr($1); - } - } - } - return ""; -} - -sub getFuncReturn($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/type[ ]*:[ ]*@(\d+) /) - { - if($LibInfo{$Version}{"info"}{$1}=~/retn[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - } - return ""; -} - -sub getFuncOrig($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/orig[ ]*:[ ]*@(\d+) /) { - return $1; - } - } - return $_[0]; -} - -sub unmangleArray(@) -{ - if($_[0]=~/\A\?/) - { # MSVC mangling - my $UndNameCmd = get_CmdPath("undname"); - if(not $UndNameCmd) { - exitStatus("Not_Found", "can't find \"undname\""); - } - writeFile("$TMP_DIR/unmangle", join("\n", @_)); - return split(/\n/, `$UndNameCmd 0x8386 \"$TMP_DIR/unmangle\"`); - } - else - { # GCC mangling - my $CppFiltCmd = get_CmdPath("c++filt"); - if(not $CppFiltCmd) { - exitStatus("Not_Found", "can't find c++filt in PATH"); - } - if(not defined $CPPFILT_SUPPORT_FILE) - { - my $Info = `$CppFiltCmd -h 2>&1`; - $CPPFILT_SUPPORT_FILE = $Info=~/\@/; - } - my $NoStrip = ($OSgroup=~/macos|windows/)?"-n":""; - if($CPPFILT_SUPPORT_FILE) - { # new versions of c++filt can take a file - if($#_>$MAX_CPPFILT_FILE_SIZE) - { # c++filt <= 2.22 may crash on large files (larger than 8mb) - # this is fixed in the oncoming version of Binutils - my @Half = splice(@_, 0, ($#_+1)/2); - return (unmangleArray(@Half), unmangleArray(@_)) - } - else - { - writeFile("$TMP_DIR/unmangle", join("\n", @_)); - my $Res = `$CppFiltCmd $NoStrip \@\"$TMP_DIR/unmangle\"`; - if($?==139) - { # segmentation fault - printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_CPPFILT_FILE_SIZE constant"); - } - return split(/\n/, $Res); - } - } - else - { # old-style unmangling - if($#_>$MAX_COMMAND_LINE_ARGUMENTS) - { - my @Half = splice(@_, 0, ($#_+1)/2); - return (unmangleArray(@Half), unmangleArray(@_)) - } - else - { - my $Strings = join(" ", @_); - my $Res = `$CppFiltCmd $NoStrip $Strings`; - if($?==139) - { # segmentation fault - printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_COMMAND_LINE_ARGUMENTS constant"); - } - return split(/\n/, $Res); - } - } - } -} - -sub get_ChargeLevel($$) -{ - my ($Symbol, $LibVersion) = @_; - return "" if($Symbol!~/\A(_Z|\?)/); - if(defined $CompleteSignature{$LibVersion}{$Symbol} - and $CompleteSignature{$LibVersion}{$Symbol}{"Header"}) - { - if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"}) - { - if($Symbol=~/C1[EI]/) { - return "[in-charge]"; - } - elsif($Symbol=~/C2[EI]/) { - return "[not-in-charge]"; - } - } - elsif($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}) - { - if($Symbol=~/D1[EI]/) { - return "[in-charge]"; - } - elsif($Symbol=~/D2[EI]/) { - return "[not-in-charge]"; - } - elsif($Symbol=~/D0[EI]/) { - return "[in-charge-deleting]"; - } - } - } - else - { - if($Symbol=~/C1[EI]/) { - return "[in-charge]"; - } - elsif($Symbol=~/C2[EI]/) { - return "[not-in-charge]"; - } - elsif($Symbol=~/D1[EI]/) { - return "[in-charge]"; - } - elsif($Symbol=~/D2[EI]/) { - return "[not-in-charge]"; - } - elsif($Symbol=~/D0[EI]/) { - return "[in-charge-deleting]"; - } - } - return ""; -} - -sub get_Signature_M($$) -{ - my ($Symbol, $LibVersion) = @_; - my $Signature_M = $tr_name{$Symbol}; - if(my $RTid = $CompleteSignature{$LibVersion}{$Symbol}{"Return"}) - { # add return type name - $Signature_M = $TypeInfo{$LibVersion}{$RTid}{"Name"}." ".$Signature_M; - } - return $Signature_M; -} - -sub get_Signature($$) -{ - my ($Symbol, $LibVersion) = @_; - if($Cache{"get_Signature"}{$LibVersion}{$Symbol}) { - return $Cache{"get_Signature"}{$LibVersion}{$Symbol}; - } - my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol); - my ($Signature, @Param_Types_FromUnmangledName) = (); - - my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"}; - if($Symbol=~/\A(_Z|\?)/) - { - if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) - { - my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}; - $ClassName=~s/\bstruct //g; - $Signature .= $ClassName."::"; - if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}) { - $Signature .= "~"; - } - $Signature .= $ShortName; - } - elsif(my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"}) { - $Signature .= $NameSpace."::".$ShortName; - } - else { - $Signature .= $ShortName; - } - my ($Short, $Params) = split_Signature($tr_name{$MnglName}); - @Param_Types_FromUnmangledName = separate_Params($Params, 0, 1); - } - else - { - $Signature .= $MnglName; - } - my @ParamArray = (); - foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}})) - { - next if($Pos eq ""); - my $ParamTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"}; - next if(not $ParamTypeId); - my $ParamTypeName = $TypeInfo{$LibVersion}{$ParamTypeId}{"Name"}; - if(not $ParamTypeName) { - $ParamTypeName = $Param_Types_FromUnmangledName[$Pos]; - } - foreach my $Typedef (keys(%ChangedTypedef)) - { - if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) { - $ParamTypeName=~s/\b\Q$Typedef\E\b/$Base/g; - } - } - if(my $ParamName = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"name"}) - { - if($ParamName eq "this" - and $Symbol=~/\A(_Z|\?)/) - { # do NOT show first hidded "this"-parameter - next; - } - push(@ParamArray, create_member_decl($ParamTypeName, $ParamName)); - } - else { - push(@ParamArray, $ParamTypeName); - } - } - if($CompleteSignature{$LibVersion}{$Symbol}{"Data"} - or $GlobalDataObject{$LibVersion}{$Symbol}) { - $Signature .= " [data]"; - } - else - { - if(my $ChargeLevel = get_ChargeLevel($Symbol, $LibVersion)) - { # add [in-charge] - $Signature .= " ".$ChargeLevel; - } - $Signature .= " (".join(", ", @ParamArray).")"; - if($CompleteSignature{$LibVersion}{$Symbol}{"Const"} - or $Symbol=~/\A_ZN(V|)K/) { - $Signature .= " const"; - } - if($CompleteSignature{$LibVersion}{$Symbol}{"Volatile"} - or $Symbol=~/\A_ZN(K|)V/) { - $Signature .= " volatile"; - } - if($CompleteSignature{$LibVersion}{$Symbol}{"Static"} - and $Symbol=~/\A(_Z|\?)/) - { # for static methods - $Signature .= " [static]"; - } - } - if(defined $ShowRetVal - and my $ReturnTId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"}) { - $Signature .= ":".$TypeInfo{$LibVersion}{$ReturnTId}{"Name"}; - } - if($SymbolVersion) { - $Signature .= $VersionSpec.$SymbolVersion; - } - return ($Cache{"get_Signature"}{$LibVersion}{$Symbol} = $Signature); -} - -sub create_member_decl($$) +sub createMemDecl($$) { my ($TName, $Member) = @_; if($TName=~/\([\*]+\)/) @@ -7553,1833 +1417,161 @@ } } -sub getFuncType($) -{ - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/type[ ]*:[ ]*@(\d+) /) - { - if(my $Type = $LibInfo{$Version}{"info_type"}{$1}) - { - if($Type eq "method_type") { - return "Method"; - } - elsif($Type eq "function_type") { - return "Function"; - } - else { - return "Other"; - } - } - } - } - return ""; -} - -sub getFuncTypeId($) +sub prepareSymbols($) { - if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]}) - { - if($Info=~/type[ ]*:[ ]*@(\d+)( |\Z)/) { - return $1; - } - } - return 0; -} - -sub isAnon($) -{ # "._N" or "$_N" in older GCC versions - return ($_[0] and $_[0]=~/(\.|\$)\_\d+|anon\-/); -} - -sub formatName($$) -{ # type name correction - if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) { - return $Cache{"formatName"}{$_[1]}{$_[0]}; - } - - my $N = $_[0]; - - if($_[1] ne "S") - { - $N=~s/\A[ ]+//g; - $N=~s/[ ]+\Z//g; - $N=~s/[ ]{2,}/ /g; - } + my $LVer = $_[0]; - $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string const - - $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const" - - $N=~s/\bvolatile const\b/const volatile/g; - - $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g; - $N=~s/\b(short|long) int\b/$1/g; - - $N=~s/([\)\]])(const|volatile)\b/$1 $2/g; - - while($N=~s/>>/> >/g) {}; - - if($_[1] eq "S") - { - if(index($N, "operator")!=-1) { - $N=~s/\b(operator[ ]*)> >/$1>>/; - } - } - - $N=~s/,/, /g; - - return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N); -} - -sub get_HeaderDeps($$) -{ - my ($AbsPath, $LibVersion) = @_; - return () if(not $AbsPath or not $LibVersion); - if(defined $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}) { - return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}}; - } - my %IncDir = (); - detect_recursive_includes($AbsPath, $LibVersion); - foreach my $HeaderPath (keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) - { - next if(not $HeaderPath); - next if($MAIN_CPP_DIR and $HeaderPath=~/\A\Q$MAIN_CPP_DIR\E([\/\\]|\Z)/); - my $Dir = get_dirname($HeaderPath); - foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$HeaderPath}})) + if(not keys(%{$SymbolInfo{$LVer}})) + { # check if input is valid + if(not $In::Opt{"ExtendedCheck"}) { - my $Dep = $Dir; - if($Prefix) - { - if($OSgroup eq "windows") - { # case insensitive seach on windows - if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//ig) { - next; - } - } - elsif($OSgroup eq "macos") - { # seach in frameworks - if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g) - { - if($HeaderPath=~/(.+\.framework)\/Headers\/([^\/]+)/) - {# frameworks - my ($HFramework, $HName) = ($1, $2); - $Dep = $HFramework; - } - else - {# mismatch - next; - } - } - } - elsif(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g) - { # Linux, FreeBSD - next; - } - } - if(not $Dep) - { # nothing to include - next; - } - if(is_default_include_dir($Dep)) - { # included by the compiler - next; - } - if(get_depth($Dep)==1) - { # too short - next; - } - if(isLibcDir($Dep)) - { # do NOT include /usr/include/{sys,bits} - next; + if($In::Opt{"CheckHeadersOnly"}) { + exitStatus("Empty_Set", "the set of public symbols is empty"); } - $IncDir{$Dep} = 1; - } - } - $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath} = sortIncPaths([keys(%IncDir)], $LibVersion); - return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}}; -} - -sub sortIncPaths($$) -{ - my ($ArrRef, $LibVersion) = @_; - if(not $ArrRef or $#{$ArrRef}<0) { - return $ArrRef; - } - @{$ArrRef} = sort {$b cmp $a} @{$ArrRef}; - @{$ArrRef} = sort {get_depth($a)<=>get_depth($b)} @{$ArrRef}; - @{$ArrRef} = sort {sortDeps($b, $a, $LibVersion)} @{$ArrRef}; - return $ArrRef; -} - -sub sortDeps($$$) -{ - if($Header_Dependency{$_[2]}{$_[0]} - and not $Header_Dependency{$_[2]}{$_[1]}) { - return 1; - } - elsif(not $Header_Dependency{$_[2]}{$_[0]} - and $Header_Dependency{$_[2]}{$_[1]}) { - return -1; - } - return 0; -} - -sub join_P($$) -{ - my $S = "/"; - if($OSgroup eq "windows") { - $S = "\\"; - } - return join($S, @_); -} - -sub get_namespace_additions($) -{ - my $NameSpaces = $_[0]; - my ($Additions, $AddNameSpaceId) = ("", 1); - foreach my $NS (sort {$a=~/_/ <=> $b=~/_/} sort {lc($a) cmp lc($b)} keys(%{$NameSpaces})) - { - next if($SkipNameSpaces{$Version}{$NS}); - next if(not $NS or $NameSpaces->{$NS}==-1); - next if($NS=~/(\A|::)iterator(::|\Z)/i); - next if($NS=~/\A__/i); - next if(($NS=~/\Astd::/ or $NS=~/\A(std|tr1|rel_ops|fcntl)\Z/) and not $STDCXX_TESTING); - $NestedNameSpaces{$Version}{$NS} = 1; # for future use in reports - my ($TypeDecl_Prefix, $TypeDecl_Suffix) = (); - my @NS_Parts = split(/::/, $NS); - next if($#NS_Parts==-1); - next if($NS_Parts[0]=~/\A(random|or)\Z/); - foreach my $NS_Part (@NS_Parts) - { - $TypeDecl_Prefix .= "namespace $NS_Part\{"; - $TypeDecl_Suffix .= "}"; - } - my $TypeDecl = $TypeDecl_Prefix."typedef int tmp_add_type_".$AddNameSpaceId.";".$TypeDecl_Suffix; - my $FuncDecl = "$NS\:\:tmp_add_type_$AddNameSpaceId tmp_add_func_$AddNameSpaceId(){return 0;};"; - $Additions.=" $TypeDecl\n $FuncDecl\n"; - $AddNameSpaceId+=1; - } - return $Additions; -} - -sub path_format($$) -{ - my ($Path, $Fmt) = @_; - $Path=~s/[\/\\]+\.?\Z//g; - if($Fmt eq "windows") - { - $Path=~s/\//\\/g; - $Path=lc($Path); - } - else - { # forward slash to pass into MinGW GCC - $Path=~s/\\/\//g; - } - return $Path; -} - -sub inc_opt($$) -{ - my ($Path, $Style) = @_; - if($Style eq "GCC") - { # GCC options - if($OSgroup eq "windows") - { # to MinGW GCC - return "-I\"".path_format($Path, "unix")."\""; - } - elsif($OSgroup eq "macos" - and $Path=~/\.framework\Z/) - { # to Apple's GCC - return "-F".esc(get_dirname($Path)); - } - else { - return "-I".esc($Path); - } - } - elsif($Style eq "CL") { - return "/I \"".$Path."\""; - } - return ""; -} - -sub platformSpecs($) -{ - my $LibVersion = $_[0]; - my $Arch = getArch($LibVersion); - if($OStarget eq "symbian") - { # options for GCCE compiler - my %Symbian_Opts = map {$_=>1} ( - "-D__GCCE__", - "-DUNICODE", - "-fexceptions", - "-D__SYMBIAN32__", - "-D__MARM_INTERWORK__", - "-D_UNICODE", - "-D__S60_50__", - "-D__S60_3X__", - "-D__SERIES60_3X__", - "-D__EPOC32__", - "-D__MARM__", - "-D__EABI__", - "-D__MARM_ARMV5__", - "-D__SUPPORT_CPP_EXCEPTIONS__", - "-march=armv5t", - "-mapcs", - "-mthumb-interwork", - "-DEKA2", - "-DSYMBIAN_ENABLE_SPLIT_HEADERS" - ); - return join(" ", keys(%Symbian_Opts)); - } - elsif($OSgroup eq "windows" - and get_dumpmachine($GCC_PATH)=~/mingw/i) - { # add options to MinGW compiler - # to simulate the MSVC compiler - my %MinGW_Opts = map {$_=>1} ( - "-D_WIN32", - "-D_STDCALL_SUPPORTED", - "-D__int64=\"long long\"", - "-D__int32=int", - "-D__int16=short", - "-D__int8=char", - "-D__possibly_notnullterminated=\" \"", - "-D__nullterminated=\" \"", - "-D__nullnullterminated=\" \"", - "-D__w64=\" \"", - "-D__ptr32=\" \"", - "-D__ptr64=\" \"", - "-D__forceinline=inline", - "-D__inline=inline", - "-D__uuidof(x)=IID()", - "-D__try=", - "-D__except(x)=", - "-D__declspec(x)=__attribute__((x))", - "-D__pragma(x)=", - "-D_inline=inline", - "-D__forceinline=__inline", - "-D__stdcall=__attribute__((__stdcall__))", - "-D__cdecl=__attribute__((__cdecl__))", - "-D__fastcall=__attribute__((__fastcall__))", - "-D__thiscall=__attribute__((__thiscall__))", - "-D_stdcall=__attribute__((__stdcall__))", - "-D_cdecl=__attribute__((__cdecl__))", - "-D_fastcall=__attribute__((__fastcall__))", - "-D_thiscall=__attribute__((__thiscall__))", - "-DSHSTDAPI_(x)=x", - "-D_MSC_EXTENSIONS", - "-DSECURITY_WIN32", - "-D_MSC_VER=1500", - "-D_USE_DECLSPECS_FOR_SAL", - "-D__noop=\" \"", - "-DDECLSPEC_DEPRECATED=\" \"", - "-D__builtin_alignof(x)=__alignof__(x)", - "-DSORTPP_PASS"); - if($Arch eq "x86") { - $MinGW_Opts{"-D_M_IX86=300"}=1; - } - elsif($Arch eq "x86_64") { - $MinGW_Opts{"-D_M_AMD64=300"}=1; - } - elsif($Arch eq "ia64") { - $MinGW_Opts{"-D_M_IA64=300"}=1; - } - return join(" ", keys(%MinGW_Opts)); - } - return ""; -} - -my %C_Structure = map {$_=>1} ( -# FIXME: Can't separate union and struct data types before dumping, -# so it sometimes cause compilation errors for unknown reason -# when trying to declare TYPE* tmp_add_class_N -# This is a list of such structures + list of other C structures - "sigval", - "sigevent", - "sigaction", - "sigvec", - "sigstack", - "timeval", - "timezone", - "rusage", - "rlimit", - "wait", - "flock", - "stat", - "_stat", - "stat32", - "_stat32", - "stat64", - "_stat64", - "_stati64", - "if_nameindex", - "usb_device", - "sigaltstack", - "sysinfo", - "timeLocale", - "tcp_debug", - "rpc_createerr", - # Other - "timespec", - "random_data", - "drand48_data", - "_IO_marker", - "_IO_FILE", - "lconv", - "sched_param", - "tm", - "itimerspec", - "_pthread_cleanup_buffer", - "fd_set", - "siginfo", - "mallinfo", - "timex", - "sigcontext", - "ucontext", - # Mac - "_timex", - "_class_t", - "_category_t", - "_class_ro_t", - "_protocol_t", - "_message_ref_t", - "_super_message_ref_t", - "_ivar_t", - "_ivar_list_t" -); - -sub getCompileCmd($$$) -{ - my ($Path, $Opt, $Inc) = @_; - my $GccCall = $GCC_PATH; - if($Opt) { - $GccCall .= " ".$Opt; - } - $GccCall .= " -x "; - if($OSgroup eq "macos") { - $GccCall .= "objective-"; - } - - if($EMERGENCY_MODE_48) - { # workaround for GCC 4.8 (C only) - $GccCall .= "c++"; - } - elsif(check_gcc($GCC_PATH, "4")) - { # compile as "C++" header - # to obtain complete dump using GCC 4.0 - $GccCall .= "c++-header"; - } - else - { # compile as "C++" source - # GCC 3.3 cannot compile headers - $GccCall .= "c++"; - } - if(my $Opts = platformSpecs($Version)) - { # platform-specific options - $GccCall .= " ".$Opts; - } - # allow extra qualifications - # and other nonconformant code - $GccCall .= " -fpermissive"; - $GccCall .= " -w"; - if($NoStdInc) - { - $GccCall .= " -nostdinc"; - $GccCall .= " -nostdinc++"; - } - if(my $Opts_GCC = getGCC_Opts($Version)) - { # user-defined options - $GccCall .= " ".$Opts_GCC; - } - $GccCall .= " \"$Path\""; - if($Inc) - { # include paths - $GccCall .= " ".$Inc; - } - return $GccCall; -} - -sub detectPreamble($$) -{ - my ($Content, $LibVersion) = @_; - my %HeaderElems = ( - # Types - "stdio.h" => ["FILE", "va_list"], - "stddef.h" => ["NULL", "ptrdiff_t"], - "stdint.h" => ["uint8_t", "uint16_t", "uint32_t", "uint64_t", - "int8_t", "int16_t", "int32_t", "int64_t"], - "time.h" => ["time_t"], - "sys/types.h" => ["ssize_t", "u_int32_t", "u_short", "u_char", - "u_int", "off_t", "u_quad_t", "u_long", "mode_t"], - "unistd.h" => ["gid_t", "uid_t", "socklen_t"], - "stdbool.h" => ["_Bool"], - "rpc/xdr.h" => ["bool_t"], - "in_systm.h" => ["n_long", "n_short"], - # Fields - "arpa/inet.h" => ["fw_src", "ip_src"], - # Functions - "stdlib.h" => ["free", "malloc", "size_t"], - "string.h" => ["memmove", "strcmp"] - ); - my %AutoPreamble = (); - foreach (keys(%HeaderElems)) - { - foreach my $Elem (@{$HeaderElems{$_}}) { - $AutoPreamble{$Elem} = $_; - } - } - my %Types = (); - while($Content=~s/error\:\s*(field\s*|)\W+(.+?)\W+//) - { # error: 'FILE' has not been declared - $Types{$2} = 1; - } - if(keys(%Types)) - { - my %AddHeaders = (); - foreach my $Type (keys(%Types)) - { - if(my $Header = $AutoPreamble{$Type}) - { - if(my $Path = identifyHeader($Header, $LibVersion)) - { - if(skipHeader($Path, $LibVersion)) { - next; - } - $Path = path_format($Path, $OSgroup); - $AddHeaders{$Path}{"Type"} = $Type; - $AddHeaders{$Path}{"Header"} = $Header; - } + else { + exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection"); } } - if(keys(%AddHeaders)) { - return \%AddHeaders; - } } - return undef; -} - -sub checkCTags($) -{ - my $Path = $_[0]; - if(not $Path) { - return; - } - my $CTags = undef; - if($OSgroup eq "bsd") - { # use ectags on BSD - $CTags = get_CmdPath("ectags"); - if(not $CTags) { - printMsg("WARNING", "can't find \'ectags\' program"); - } - } - if(not $CTags) { - $CTags = get_CmdPath("ctags"); - } - if(not $CTags) + foreach my $InfoId (sort {$b<=>$a} keys(%{$SymbolInfo{$LVer}})) { - printMsg("WARNING", "can't find \'ctags\' program"); - return; - } - - if($OSgroup ne "linux") - { # macos, freebsd, etc. - my $Info = `$CTags --version 2>\"$TMP_DIR/null\"`; - if($Info!~/exuberant/i) - { - printMsg("WARNING", "incompatible version of \'ctags\' program"); - return; - } - } - - my $Out = $TMP_DIR."/ctags.txt"; - system("$CTags --c-kinds=pxn -f \"$Out\" \"$Path\" 2>\"$TMP_DIR/null\""); - if($Debug) { - copy($Out, $DEBUG_PATH{$Version}."/ctags.txt"); - } - open(CTAGS, "<", $Out); - while(my $Line = ) - { - chomp($Line); - my ($Name, $Header, $Def, $Type, $Scpe) = split(/\t/, $Line); - if(defined $Intrinsic_Keywords{$Name}) - { # noise - next; - } - if($Type eq "n") - { - if(index($Scpe, "class:")==0) { - next; - } - if(index($Scpe, "struct:")==0) { - next; - } - if(index($Scpe, "namespace:")==0) - { - if($Scpe=~s/\Anamespace://) { - $Name = $Scpe."::".$Name; - } - } - $TUnit_NameSpaces{$Version}{$Name} = 1; - } - elsif($Type eq "p") - { - if(not $Scpe or index($Scpe, "namespace:")==0) { - $TUnit_Funcs{$Version}{$Name} = 1; - } - } - elsif($Type eq "x") - { - if(not $Scpe or index($Scpe, "namespace:")==0) { - $TUnit_Vars{$Version}{$Name} = 1; - } - } - } - close(CTAGS); -} - -sub preChange($$) -{ - my ($HeaderPath, $IncStr) = @_; - - my $PreprocessCmd = getCompileCmd($HeaderPath, "-E", $IncStr); - my $Content = undef; - - if($OStarget eq "windows" - and get_dumpmachine($GCC_PATH)=~/mingw/i - and $MinGWMode{$Version}!=-1) - { # modify headers to compile by MinGW - if(not $Content) - { # preprocessing - $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`; - } - if($Content=~s/__asm\s*(\{[^{}]*?\}|[^{};]*)//g) - { # __asm { ... } - $MinGWMode{$Version}=1; - } - if($Content=~s/\s+(\/ \/.*?)\n/\n/g) - { # comments after preprocessing - $MinGWMode{$Version}=1; - } - if($Content=~s/(\W)(0x[a-f]+|\d+)(i|ui)(8|16|32|64)(\W)/$1$2$5/g) - { # 0xffui8 - $MinGWMode{$Version}=1; - } - - if($MinGWMode{$Version}) { - printMsg("INFO", "Using MinGW compatibility mode"); - } - } - - if(($COMMON_LANGUAGE{$Version} eq "C" or $CheckHeadersOnly) - and $CppMode{$Version}!=-1 and not $CppCompat and not $CPP_HEADERS) - { # rename C++ keywords in C code - # disable this code by -cpp-compatible option - if(not $Content) - { # preprocessing - $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`; - } - my $RegExp_C = join("|", keys(%CppKeywords_C)); - my $RegExp_F = join("|", keys(%CppKeywords_F)); - my $RegExp_O = join("|", keys(%CppKeywords_O)); - - my $Detected = undef; - - while($Content=~s/(\A|\n[^\#\/\n][^\n]*?|\n)(\*\s*|\s+|\@|\,|\()($RegExp_C|$RegExp_F)(\s*([\,\)\;\.\[]|\-\>|\:\s*\d))/$1$2c99_$3$4/g) - { # MATCH: - # int foo(int new, int class, int (*new)(int)); - # int foo(char template[], char*); - # unsigned private: 8; - # DO NOT MATCH: - # #pragma GCC visibility push(default) - $CppMode{$Version} = 1; - $Detected = "$1$2$3$4" if(not defined $Detected); - } - if($Content=~s/([^\w\s]|\w\s+)(?int($Registered_Headers{$Version}{$b}{"Pos"})} @Headers; - - my $IncludeString = getIncString(getIncPaths(@{$Include_Preamble{$Version}}, @Headers), "GCC"); - - my $TmpHeaderPath = $TMP_DIR."/dump".$Version.".h"; - my $HeaderPath = $TmpHeaderPath; - - # write tmp-header - open(TMP_HEADER, ">", $TmpHeaderPath) || die ("can't open file \'$TmpHeaderPath\': $!\n"); - if(my $AddDefines = $Descriptor{$Version}{"Defines"}) - { - $AddDefines=~s/\n\s+/\n /g; - print TMP_HEADER "\n // add defines\n ".$AddDefines."\n"; - } - print TMP_HEADER "\n // add includes\n"; - foreach my $HPath (@{$Include_Preamble{$Version}}) { - print TMP_HEADER " #include \"".path_format($HPath, "unix")."\"\n"; - } - foreach my $HPath (@Headers) - { - if(not grep {$HPath eq $_} (@{$Include_Preamble{$Version}})) { - print TMP_HEADER " #include \"".path_format($HPath, "unix")."\"\n"; - } - } - close(TMP_HEADER); - - if($ExtraInfo) - { # extra information for other tools - if($IncludeString) { - writeFile($ExtraInfo."/include-string", $IncludeString); + # symbol and its symlink have same signatures + if(my $SVer = $In::ABI{$LVer}{"SymbolVersion"}{$MnglName}) { + $CompSign{$LVer}{$SVer} = $SymbolInfo{$LVer}{$InfoId}; } - writeFile($ExtraInfo."/recursive-includes", Dumper($RecursiveIncludes{$Version})); - writeFile($ExtraInfo."/direct-includes", Dumper($Header_Includes{$Version})); - if(my @Redirects = keys(%{$Header_ErrorRedirect{$Version}})) - { - my $REDIR = ""; - foreach my $P1 (sort @Redirects) { - $REDIR .= $P1.";".$Header_ErrorRedirect{$Version}{$P1}."\n"; - } - writeFile($ExtraInfo."/include-redirect", $REDIR); - } - } - - if(not keys(%{$TargetHeaders{$Version}})) - { # Target headers - addTargetHeaders($Version); - } - - # clean memory - %RecursiveIncludes = (); - %Header_Include_Prefix = (); - %Header_Includes = (); - - # clean cache - delete($Cache{"identifyHeader"}); - delete($Cache{"detect_header_includes"}); - delete($Cache{"selectSystemHeader"}); - - # preprocessing stage - my $Pre = callPreprocessor($TmpHeaderPath, $IncludeString, $Version); - checkPreprocessedUnit($Pre); - - if($ExtraInfo) - { # extra information for other tools - writeFile($ExtraInfo."/header-paths", join("\n", sort keys(%{$PreprocessedHeaders{$Version}}))); - } - - # clean memory - delete($Include_Neighbors{$Version}); - delete($PreprocessedHeaders{$Version}); - - if($COMMON_LANGUAGE{$Version} eq "C++") { - checkCTags($Pre); - } - - if(my $PrePath = preChange($TmpHeaderPath, $IncludeString)) - { # try to correct the preprocessor output - $HeaderPath = $PrePath; - } - - if($COMMON_LANGUAGE{$Version} eq "C++") - { # add classes and namespaces to the dump - my $CHdump = "-fdump-class-hierarchy -c"; - if($CppMode{$Version}==1 - or $MinGWMode{$Version}==1) { - $CHdump .= " -fpreprocessed"; - } - my $ClassHierarchyCmd = getCompileCmd($HeaderPath, $CHdump, $IncludeString); - chdir($TMP_DIR); - system($ClassHierarchyCmd." >null 2>&1"); - chdir($ORIG_DIR); - if(my $ClassDump = (cmd_find($TMP_DIR,"f","*.class",1))[0]) + if(my $Alias = $SymbolInfo{$LVer}{$InfoId}{"Alias"}) { - my $Content = readFile($ClassDump); - foreach my $ClassInfo (split(/\n\n/, $Content)) - { - if($ClassInfo=~/\AClass\s+(.+)\s*/i) - { - my $CName = $1; - next if($CName=~/\A(__|_objc_|_opaque_)/); - $TUnit_NameSpaces{$Version}{$CName} = -1; - if($CName=~/\A[\w:]+\Z/) - { # classes - $TUnit_Classes{$Version}{$CName} = 1; - } - if($CName=~/(\w[\w:]*)::/) - { # namespaces - my $NS = $1; - if(not defined $TUnit_NameSpaces{$Version}{$NS}) { - $TUnit_NameSpaces{$Version}{$NS} = 1; - } - } - } - elsif($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i) - { # read v-tables (advanced approach) - my ($CName, $VTable) = ($1, $2); - $ClassVTable_Content{$Version}{$CName} = $VTable; - } - } - foreach my $NS (keys(%{$AddNameSpaces{$Version}})) - { # add user-defined namespaces - $TUnit_NameSpaces{$Version}{$NS} = 1; - } - if($Debug) - { # debug mode - mkpath($DEBUG_PATH{$Version}); - copy($ClassDump, $DEBUG_PATH{$Version}."/class-hierarchy-dump.txt"); + $CompSign{$LVer}{$Alias} = $SymbolInfo{$LVer}{$InfoId}; + + if(my $SAVer = $In::ABI{$LVer}{"SymbolVersion"}{$Alias}) { + $CompSign{$LVer}{$SAVer} = $SymbolInfo{$LVer}{$InfoId}; } - unlink($ClassDump); } - # add namespaces and classes - if(my $NS_Add = get_namespace_additions($TUnit_NameSpaces{$Version})) - { # GCC on all supported platforms does not include namespaces to the dump by default - appendFile($HeaderPath, "\n // add namespaces\n".$NS_Add); - } - # some GCC versions don't include class methods to the TU dump by default - my ($AddClass, $ClassNum) = ("", 0); - my $GCC_44 = check_gcc($GCC_PATH, "4.4"); # support for old GCC versions - foreach my $CName (sort keys(%{$TUnit_Classes{$Version}})) - { - next if($C_Structure{$CName}); - next if(not $STDCXX_TESTING and $CName=~/\Astd::/); - next if($SkipTypes{$Version}{$CName}); - if(not $Force and $GCC_44 - and $OSgroup eq "linux") - { # optimization for linux with GCC >= 4.4 - # disable this code by -force option - if(index($CName, "::")!=-1) - { # should be added by name space - next; - } - } - else - { - if($CName=~/\A(.+)::[^:]+\Z/ - and $TUnit_Classes{$Version}{$1}) - { # classes inside other classes - next; - } - } - if(defined $TUnit_Funcs{$Version}{$CName}) - { # the same name for a function and type - next; - } - if(defined $TUnit_Vars{$Version}{$CName}) - { # the same name for a variable and type - next; - } - $AddClass .= " $CName* tmp_add_class_".($ClassNum++).";\n"; - } - if($AddClass) { - appendFile($HeaderPath, "\n // add classes\n".$AddClass); - } - } - writeLog($Version, "Temporary header file \'$TmpHeaderPath\' with the following content will be compiled to create GCC translation unit dump:\n".readFile($TmpHeaderPath)."\n"); - # create TU dump - my $TUdump = "-fdump-translation-unit -fkeep-inline-functions -c"; - if($UserLang eq "C") { - $TUdump .= " -U__cplusplus -D_Bool=\"bool\""; - } - if($CppMode{$Version}==1 - or $MinGWMode{$Version}==1) { - $TUdump .= " -fpreprocessed"; - } - my $SyntaxTreeCmd = getCompileCmd($HeaderPath, $TUdump, $IncludeString); - writeLog($Version, "The GCC parameters:\n $SyntaxTreeCmd\n\n"); - chdir($TMP_DIR); - system($SyntaxTreeCmd." >\"$TMP_DIR/tu_errors\" 2>&1"); - my $Errors = ""; - if($?) - { # failed to compile, but the TU dump still can be created - if($Errors = readFile($TMP_DIR."/tu_errors")) - { # try to recompile - # FIXME: handle other errors and try to recompile - if($CppMode{$Version}==1 - and index($Errors, "c99_")!=-1 - and not defined $CppIncompat) - { # disable c99 mode and try again - $CppMode{$Version}=-1; - - if($Debug) - { - # printMsg("INFO", $Errors); - } - - printMsg("INFO", "Disabling C++ compatibility mode"); - resetLogging($Version); - $TMP_DIR = tempdir(CLEANUP=>1); - return getDump(); - } - elsif($AutoPreambleMode{$Version}!=-1 - and my $AddHeaders = detectPreamble($Errors, $Version)) - { # add auto preamble headers and try again - $AutoPreambleMode{$Version}=-1; - my @Headers = sort {$b cmp $a} keys(%{$AddHeaders}); # sys/types.h should be the first - foreach my $Num (0 .. $#Headers) - { - my $Path = $Headers[$Num]; - if(not grep {$Path eq $_} (@{$Include_Preamble{$Version}})) - { - push_U($Include_Preamble{$Version}, $Path); - printMsg("INFO", "Add \'".$AddHeaders->{$Path}{"Header"}."\' preamble header for \'".$AddHeaders->{$Path}{"Type"}."\'"); - } - } - resetLogging($Version); - $TMP_DIR = tempdir(CLEANUP=>1); - return getDump(); - } - elsif($Cpp0xMode{$Version}!=-1 - and ($Errors=~/\Q-std=c++0x\E/ - or $Errors=~/is not a class or namespace/)) - { # c++0x: enum class - if(check_gcc($GCC_PATH, "4.6")) - { - $Cpp0xMode{$Version}=-1; - printMsg("INFO", "Enabling c++0x mode"); - resetLogging($Version); - $TMP_DIR = tempdir(CLEANUP=>1); - $CompilerOptions{$Version} .= " -std=c++0x"; - return getDump(); - } - else { - printMsg("WARNING", "Probably c++0x construction detected"); - } - - } - elsif($MinGWMode{$Version}==1) - { # disable MinGW mode and try again - $MinGWMode{$Version}=-1; - resetLogging($Version); - $TMP_DIR = tempdir(CLEANUP=>1); - return getDump(); - } - writeLog($Version, $Errors); - } - else { - writeLog($Version, "$!: $?\n"); - } - printMsg("ERROR", "some errors occurred when compiling headers"); - printErrorLog($Version); - $COMPILE_ERRORS = $ERROR_CODE{"Compile_Error"}; - writeLog($Version, "\n"); # new line - } - chdir($ORIG_DIR); - unlink($TmpHeaderPath); - unlink($HeaderPath); - - if(my @TUs = cmd_find($TMP_DIR,"f","*.tu",1)) { - return $TUs[0]; - } - else - { - my $Msg = "can't compile header(s)"; - if($Errors=~/error trying to exec \W+cc1plus\W+/) { - $Msg .= "\nDid you install G++?"; - } - exitStatus("Cannot_Compile", $Msg); - } -} - -sub cmd_file($) -{ - my $Path = $_[0]; - return "" if(not $Path or not -e $Path); - if(my $CmdPath = get_CmdPath("file")) { - return `$CmdPath -b \"$Path\"`; - } - return ""; -} - -sub getIncString($$) -{ - my ($ArrRef, $Style) = @_; - return "" if(not $ArrRef or $#{$ArrRef}<0); - my $String = ""; - foreach (@{$ArrRef}) { - $String .= " ".inc_opt($_, $Style); - } - return $String; -} - -sub getIncPaths(@) -{ - my @HeaderPaths = @_; - my @IncPaths = @{$Add_Include_Paths{$Version}}; - if($INC_PATH_AUTODETECT{$Version}) - { # auto-detecting dependencies - my %Includes = (); - foreach my $HPath (@HeaderPaths) - { - foreach my $Dir (get_HeaderDeps($HPath, $Version)) + if(defined $CompSign{$LVer}{$MnglName}) + { # NOTE: duplicated entries in the ABI Dump + if(defined $SymbolInfo{$LVer}{$InfoId}{"Param"}) { - if($Skip_Include_Paths{$Version}{$Dir}) { + if($SymbolInfo{$LVer}{$InfoId}{"Param"}{0}{"name"} eq "p1") { next; } - if($SystemRoot) - { - if($Skip_Include_Paths{$Version}{$SystemRoot.$Dir}) { - next; - } - } - $Includes{$Dir} = 1; - } - } - foreach my $Dir (@{sortIncPaths([keys(%Includes)], $Version)}) { - push_U(\@IncPaths, $Dir); - } - } - else - { # user-defined paths - @IncPaths = @{$Include_Paths{$Version}}; - } - return \@IncPaths; -} - -sub push_U($@) -{ # push unique - if(my $Array = shift @_) - { - if(@_) - { - my %Exist = map {$_=>1} @{$Array}; - foreach my $Elem (@_) - { - if(not defined $Exist{$Elem}) - { - push(@{$Array}, $Elem); - $Exist{$Elem} = 1; - } - } - } - } -} - -sub callPreprocessor($$$) -{ - my ($Path, $Inc, $LibVersion) = @_; - return "" if(not $Path or not -f $Path); - my $IncludeString=$Inc; - if(not $Inc) { - $IncludeString = getIncString(getIncPaths($Path), "GCC"); - } - my $Cmd = getCompileCmd($Path, "-dD -E", $IncludeString); - my $Out = $TMP_DIR."/preprocessed.h"; - system($Cmd." >\"$Out\" 2>\"$TMP_DIR/null\""); - return $Out; -} - -sub cmd_find($;$$$$) -{ # native "find" is much faster than File::Find (~6x) - # also the File::Find doesn't support --maxdepth N option - # so using the cross-platform wrapper for the native one - my ($Path, $Type, $Name, $MaxDepth, $UseRegex) = @_; - return () if(not $Path or not -e $Path); - if($OSgroup eq "windows") - { - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - my $Cmd = "dir \"$Path\" /B /O"; - if($MaxDepth!=1) { - $Cmd .= " /S"; - } - if($Type eq "d") { - $Cmd .= " /AD"; - } - elsif($Type eq "f") { - $Cmd .= " /A-D"; - } - my @Files = split(/\n/, `$Cmd 2>\"$TMP_DIR/null\"`); - if($Name) - { - if(not $UseRegex) - { # FIXME: how to search file names in MS shell? - # wildcard to regexp - $Name=~s/\*/.*/g; - $Name='\A'.$Name.'\Z'; - } - @Files = grep { /$Name/i } @Files; - } - my @AbsPaths = (); - foreach my $File (@Files) - { - if(not is_abs($File)) { - $File = join_P($Path, $File); - } - if($Type eq "f" and not -f $File) - { # skip dirs - next; - } - push(@AbsPaths, path_format($File, $OSgroup)); - } - if($Type eq "d") { - push(@AbsPaths, $Path); - } - return @AbsPaths; - } - else - { - my $FindCmd = get_CmdPath("find"); - if(not $FindCmd) { - exitStatus("Not_Found", "can't find a \"find\" command"); - } - $Path = get_abs_path($Path); - if(-d $Path and -l $Path - and $Path!~/\/\Z/) - { # for directories that are symlinks - $Path.="/"; - } - my $Cmd = $FindCmd." \"$Path\""; - if($MaxDepth) { - $Cmd .= " -maxdepth $MaxDepth"; - } - if($Type) { - $Cmd .= " -type $Type"; - } - if($Name and not $UseRegex) - { # wildcards - $Cmd .= " -name \"$Name\""; - } - my $Res = `$Cmd 2>\"$TMP_DIR/null\"`; - if($? and $!) { - printMsg("ERROR", "problem with \'find\' utility ($?): $!"); - } - my @Files = split(/\n/, $Res); - if($Name and $UseRegex) - { # regex - @Files = grep { /$Name/ } @Files; - } - return @Files; - } -} - -sub unpackDump($) -{ - my $Path = $_[0]; - return "" if(not $Path or not -e $Path); - $Path = get_abs_path($Path); - $Path = path_format($Path, $OSgroup); - my ($Dir, $FileName) = separate_path($Path); - my $UnpackDir = $TMP_DIR."/unpack"; - rmtree($UnpackDir); - mkpath($UnpackDir); - if($FileName=~s/\Q.zip\E\Z//g) - { # *.zip - my $UnzipCmd = get_CmdPath("unzip"); - if(not $UnzipCmd) { - exitStatus("Not_Found", "can't find \"unzip\" command"); - } - chdir($UnpackDir); - system("$UnzipCmd \"$Path\" >\"$TMP_DIR/null\""); - if($?) { - exitStatus("Error", "can't extract \'$Path\' ($?): $!"); - } - chdir($ORIG_DIR); - my @Contents = cmd_find($UnpackDir, "f"); - if(not @Contents) { - exitStatus("Error", "can't extract \'$Path\'"); - } - return $Contents[0]; - } - elsif($FileName=~s/\Q.tar.gz\E(\.\w+|)\Z//g) - { # *.tar.gz - # *.tar.gz.amd64 (dh & cdbs) - if($OSgroup eq "windows") - { # -xvzf option is not implemented in tar.exe (2003) - # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead - my $TarCmd = get_CmdPath("tar"); - if(not $TarCmd) { - exitStatus("Not_Found", "can't find \"tar\" command"); - } - my $GzipCmd = get_CmdPath("gzip"); - if(not $GzipCmd) { - exitStatus("Not_Found", "can't find \"gzip\" command"); - } - chdir($UnpackDir); - system("$GzipCmd -k -d -f \"$Path\""); # keep input files (-k) - if($?) { - exitStatus("Error", "can't extract \'$Path\'"); - } - system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >\"$TMP_DIR/null\""); - if($?) { - exitStatus("Error", "can't extract \'$Path\' ($?): $!"); - } - chdir($ORIG_DIR); - unlink($Dir."/".$FileName.".tar"); - my @Contents = cmd_find($UnpackDir, "f"); - if(not @Contents) { - exitStatus("Error", "can't extract \'$Path\'"); - } - return $Contents[0]; - } - else - { # Unix, Mac - my $TarCmd = get_CmdPath("tar"); - if(not $TarCmd) { - exitStatus("Not_Found", "can't find \"tar\" command"); - } - chdir($UnpackDir); - system("$TarCmd -xvzf \"$Path\" >\"$TMP_DIR/null\""); - if($?) { - exitStatus("Error", "can't extract \'$Path\' ($?): $!"); - } - chdir($ORIG_DIR); - my @Contents = cmd_find($UnpackDir, "f"); - if(not @Contents) { - exitStatus("Error", "can't extract \'$Path\'"); - } - return $Contents[0]; - } - } -} - -sub createArchive($$) -{ - my ($Path, $To) = @_; - if(not $To) { - $To = "."; - } - if(not $Path or not -e $Path - or not -d $To) { - return ""; - } - my ($From, $Name) = separate_path($Path); - if($OSgroup eq "windows") - { # *.zip - my $ZipCmd = get_CmdPath("zip"); - if(not $ZipCmd) { - exitStatus("Not_Found", "can't find \"zip\""); - } - my $Pkg = $To."/".$Name.".zip"; - unlink($Pkg); - chdir($To); - system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"$TMP_DIR/null\""); - if($?) - { # cannot allocate memory (or other problems with "zip") - unlink($Path); - exitStatus("Error", "can't pack the ABI dump: ".$!); - } - chdir($ORIG_DIR); - unlink($Path); - return $Pkg; - } - else - { # *.tar.gz - my $TarCmd = get_CmdPath("tar"); - if(not $TarCmd) { - exitStatus("Not_Found", "can't find \"tar\""); - } - my $GzipCmd = get_CmdPath("gzip"); - if(not $GzipCmd) { - exitStatus("Not_Found", "can't find \"gzip\""); - } - my $Pkg = abs_path($To)."/".$Name.".tar.gz"; - unlink($Pkg); - chdir($From); - system($TarCmd, "-czf", $Pkg, $Name); - if($?) - { # cannot allocate memory (or other problems with "tar") - unlink($Path); - exitStatus("Error", "can't pack the ABI dump: ".$!); - } - chdir($ORIG_DIR); - unlink($Path); - return $To."/".$Name.".tar.gz"; - } -} - -sub is_header_file($) -{ - if($_[0]=~/\.($HEADER_EXT)\Z/i) { - return $_[0]; - } - return 0; -} - -sub is_not_header($) -{ - if($_[0]=~/\.\w+\Z/ - and $_[0]!~/\.($HEADER_EXT)\Z/i) { - return 1; - } - return 0; -} - -sub is_header($$$) -{ - my ($Header, $UserDefined, $LibVersion) = @_; - return 0 if(-d $Header); - if(-f $Header) { - $Header = get_abs_path($Header); - } - else - { - if(is_abs($Header)) - { # incorrect absolute path - return 0; - } - if(my $HPath = identifyHeader($Header, $LibVersion)) { - $Header = $HPath; - } - else - { # can't find header - return 0; - } - } - if($Header=~/\.\w+\Z/) - { # have an extension - return is_header_file($Header); - } - else - { - if($UserDefined==2) - { # specified on the command line - if(cmd_file($Header)!~/HTML|XML/i) { - return $Header; - } - } - elsif($UserDefined) - { # specified in the XML-descriptor - # header file without an extension - return $Header; - } - else - { - if(index($Header, "/include/")!=-1 - or cmd_file($Header)=~/C[\+]*\s+program/i) - { # !~/HTML|XML|shared|dynamic/i - return $Header; } } - } - return 0; -} - -sub addTargetHeaders($) -{ - my $LibVersion = $_[0]; - foreach my $RegHeader (keys(%{$Registered_Headers{$LibVersion}})) - { - my $RegDir = get_dirname($RegHeader); - $TargetHeaders{$LibVersion}{get_filename($RegHeader)} = 1; - if(not $INC_PATH_AUTODETECT{$LibVersion}) { - detect_recursive_includes($RegHeader, $LibVersion); + if(not $CompSign{$LVer}{$MnglName}{"MnglName"}) + { # NOTE: global data may enter here twice + $CompSign{$LVer}{$MnglName} = $SymbolInfo{$LVer}{$InfoId}; } - foreach my $RecInc (keys(%{$RecursiveIncludes{$LibVersion}{$RegHeader}})) - { - my $Dir = get_dirname($RecInc); - - if(familiarDirs($RegDir, $Dir) - or $RecursiveIncludes{$LibVersion}{$RegHeader}{$RecInc}!=1) - { # in the same directory or included by #include "..." - $TargetHeaders{$LibVersion}{get_filename($RecInc)} = 1; - } - } - } -} - -sub familiarDirs($$) -{ - my ($D1, $D2) = @_; - if($D1 eq $D2) { - return 1; - } - - my $U1 = index($D1, "/usr/"); - my $U2 = index($D2, "/usr/"); - - if($U1==0 and $U2!=0) { - return 0; - } - - if($U2==0 and $U1!=0) { - return 0; - } - - if(index($D2, $D1."/")==0) { - return 1; - } - - # /usr/include/DIR - # /home/user/DIR - - my $DL = get_depth($D1); - - my @Dirs1 = ($D1); - while($DL - get_depth($D1)<=2 - and get_depth($D1)>=4 - and $D1=~s/[\/\\]+[^\/\\]*?\Z//) { - push(@Dirs1, $D1); - } - - my @Dirs2 = ($D2); - while(get_depth($D2)>=4 - and $D2=~s/[\/\\]+[^\/\\]*?\Z//) { - push(@Dirs2, $D2); - } - - foreach my $P1 (@Dirs1) - { - foreach my $P2 (@Dirs2) - { - - if($P1 eq $P2) { - return 1; - } - } - } - return 0; -} - -sub readHeaders($) -{ - $Version = $_[0]; - printMsg("INFO", "checking header(s) ".$Descriptor{$Version}{"Version"}." ..."); - my $DumpPath = getDump(); - if($Debug) - { # debug mode - mkpath($DEBUG_PATH{$Version}); - copy($DumpPath, $DEBUG_PATH{$Version}."/translation-unit-dump.txt"); - } - getInfo($DumpPath); -} - -sub prepareTypes($) -{ - my $LibVersion = $_[0]; - if(not checkDump($LibVersion, "2.0")) - { # support for old ABI dumps - # type names have been corrected in ACC 1.22 (dump 2.0 format) - foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}})) - { - my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"}; - if($TName=~/\A(\w+)::(\w+)/) { - my ($P1, $P2) = ($1, $2); - if($P1 eq $P2) { - $TName=~s/\A$P1:\:$P1(\W)/$P1$1/; - } - else { - $TName=~s/\A(\w+:\:)$P2:\:$P2(\W)/$1$P2$2/; - } - } - $TypeInfo{$LibVersion}{$TypeId}{"Name"} = $TName; - } - } - if(not checkDump($LibVersion, "2.5")) - { # support for old ABI dumps - # V < 2.5: array size == "number of elements" - # V >= 2.5: array size in bytes - foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { - my %Type = get_PureType($TypeId, $TypeInfo{$LibVersion}); - if($Type{"Type"} eq "Array") - { - if(my $Size = $Type{"Size"}) - { # array[N] - my %Base = get_OneStep_BaseType($Type{"Tid"}, $TypeInfo{$LibVersion}); - $Size *= $Base{"Size"}; - $TypeInfo{$LibVersion}{$TypeId}{"Size"} = "$Size"; - } - else - { # array[] is a pointer - $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $WORD_SIZE{$LibVersion}; - } - } - } - } - my $V2 = ($LibVersion==1)?2:1; - if(not checkDump($LibVersion, "2.7")) - { # support for old ABI dumps - # size of "method ptr" corrected in 2.7 - foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { - my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion}); - if($PureType{"Type"} eq "MethodPtr") - { - my %Type = get_Type($TypeId, $LibVersion); - my $TypeId_2 = getTypeIdByName($PureType{"Name"}, $V2); - my %Type2 = get_Type($TypeId_2, $V2); - if($Type{"Size"} ne $Type2{"Size"}) { - $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $Type2{"Size"}; - } - } - } - } -} - -sub prepareSymbols($) -{ - my $LibVersion = $_[0]; - - if(not keys(%{$SymbolInfo{$LibVersion}})) - { # check if input is valid - if(not $ExtendedCheck) + if(not defined $SymbolInfo{$LVer}{$InfoId}{"Unmangled"}) { - if($CheckHeadersOnly) { - exitStatus("Empty_Set", "the set of public symbols is empty (".$Descriptor{$LibVersion}{"Version"}.")"); + if($MnglName eq $ShortName) { + $SymbolInfo{$LVer}{$InfoId}{"Unmangled"} = $ShortName; } else { - exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection (".$Descriptor{$LibVersion}{"Version"}.")"); - } - } - } - - my $Remangle = 0; - if(not checkDump(1, "2.10") - or not checkDump(2, "2.10")) - { # different formats - $Remangle = 1; - } - if($CheckHeadersOnly) - { # different languages - if($UserLang) - { # --lang=LANG for both versions - if(($UsedDump{1}{"V"} and $UserLang ne $UsedDump{1}{"L"}) - or ($UsedDump{2}{"V"} and $UserLang ne $UsedDump{2}{"L"})) - { - if($UserLang eq "C++") - { # remangle symbols - $Remangle = 1; - } - elsif($UserLang eq "C") - { # remove mangling - $Remangle = -1; - } + $SymbolInfo{$LVer}{$InfoId}{"Unmangled"} = getSignature($MnglName, $LVer, "Class|Name|Qual"); } } } - foreach my $InfoId (sort {int($b)<=>int($a)} keys(%{$SymbolInfo{$LibVersion}})) - { # reverse order: D0, D1, D2, D0 (artificial, GCC < 4.5), C1, C2 - if(not checkDump($LibVersion, "2.13")) - { # support for old ABI dumps - if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}) - { - foreach my $P (keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})) - { - my $TypeId = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"type"}; - my $DVal = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"}; - my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"}; - if(defined $DVal and $DVal ne "") - { - if($TName eq "char") { - $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = chr($DVal); - } - elsif($TName eq "bool") { - $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = $DVal?"true":"false"; - } - } - } - } - } - if($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) - { - if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"} - and keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}}) - and $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this") - { # support for old GCC < 4.5: skip artificial ~dtor(int __in_chrg) - # + support for old ABI dumps - next; - } - } - my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"}; - my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"}; - my $ClassID = $SymbolInfo{$LibVersion}{$InfoId}{"Class"}; - my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"}; - - my $SRemangle = 0; - if(not checkDump(1, "2.12") - or not checkDump(2, "2.12")) - { # support for old ABI dumps - if($ShortName eq "operator>>") - { - if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}) - { # corrected mangling of operator>> - $SRemangle = 1; - } - } - if($SymbolInfo{$LibVersion}{$InfoId}{"Data"}) - { - if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"} - and isConstType($Return, $LibVersion) and $MnglName!~/L\d+$ShortName/) - { # corrected mangling of const global data - # some global data is not mangled in the TU dump: qt_sine_table (Qt 4.8) - # and incorrectly mangled by old ACC versions - $SRemangle = 1; - } - } - } - if(not $CheckHeadersOnly) - { # support for old ABI dumps - if(not checkDump(1, "2.17") - or not checkDump(2, "2.17")) - { - if($SymbolInfo{$LibVersion}{$InfoId}{"Data"}) - { - if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}) - { - if(link_symbol($ShortName, $LibVersion, "-Deps")) - { - $MnglName = $ShortName; - $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName; - } - } - } - } - } - if($Remangle==1 or $SRemangle==1) - { # support for old ABI dumps: some symbols are not mangled in old dumps - # mangle both sets of symbols (old and new) - # NOTE: remangling all symbols by the same mangler - if($MnglName=~/\A_ZN(V|)K/) - { # mangling may be incorrect on old ABI dumps - # because of absent "Const" attribute - $SymbolInfo{$LibVersion}{$InfoId}{"Const"} = 1; - } - if($MnglName=~/\A_ZN(K|)V/) - { # mangling may be incorrect on old ABI dumps - # because of absent "Volatile" attribute - $SymbolInfo{$LibVersion}{$InfoId}{"Volatile"} = 1; - } - if(($ClassID and $MnglName!~/\A(_Z|\?)/) - or (not $ClassID and $CheckHeadersOnly) - or (not $ClassID and not link_symbol($MnglName, $LibVersion, "-Deps"))) - { # support for old ABI dumps, GCC >= 4.0 - # remangling all manually mangled symbols - if($MnglName = mangle_symbol($InfoId, $LibVersion, "GCC")) - { - $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName; - $MangledNames{$LibVersion}{$MnglName} = 1; - } - } - } - elsif($Remangle==-1) - { # remove mangling - $MnglName = ""; - $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = ""; - } - if(not $MnglName) { - next; - } - if(not $CompleteSignature{$LibVersion}{$MnglName}{"MnglName"}) - { # NOTE: global data may enter here twice - %{$CompleteSignature{$LibVersion}{$MnglName}} = %{$SymbolInfo{$LibVersion}{$InfoId}}; - - } - if(not checkDump($LibVersion, "2.6")) - { # support for old dumps - # add "Volatile" attribute - if($MnglName=~/_Z(K|)V/) { - $CompleteSignature{$LibVersion}{$MnglName}{"Volatile"}=1; - } - } - # symbol and its symlink have same signatures - if($SymVer{$LibVersion}{$MnglName}) { - %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$MnglName}}} = %{$SymbolInfo{$LibVersion}{$InfoId}}; - } - - if(my $Alias = $CompleteSignature{$LibVersion}{$MnglName}{"Alias"}) + if($In::ABI{$LVer}{"Language"} eq "C++" + and getCmdPath("c++filt")) + { + my @VTables = (); + foreach my $S (keys(%{$In::ABI{$LVer}{"SymLib"}})) { - %{$CompleteSignature{$LibVersion}{$Alias}} = %{$SymbolInfo{$LibVersion}{$InfoId}}; - if($SymVer{$LibVersion}{$Alias}) { - %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$Alias}}} = %{$SymbolInfo{$LibVersion}{$InfoId}}; + if(index($S, "_ZTV")==0) { + push(@VTables, $S); } } - - # clean memory - delete($SymbolInfo{$LibVersion}{$InfoId}); - } - if($COMMON_LANGUAGE{$LibVersion} eq "C++" or $OSgroup eq "windows") { - translateSymbols(keys(%{$CompleteSignature{$LibVersion}}), $LibVersion); - } - if($ExtendedCheck) - { # --ext option - addExtension($LibVersion); + translateSymbols(@VTables, $LVer); } - # clean memory - delete($SymbolInfo{$LibVersion}); + if($In::Opt{"ExtendedCheck"}) { + addExtension($LVer); + } - foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}})) + foreach my $Symbol (keys(%{$CompSign{$LVer}})) { # detect allocable classes with public exported constructors # or classes with auto-generated or inline-only constructors # and other temp info - if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) + if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"}) { - my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}; - if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"} - and not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"}) + my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + if($CompSign{$LVer}{$Symbol}{"Constructor"} + and not $CompSign{$LVer}{$Symbol}{"InLine"}) { # Class() { ... } will not be exported - if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"}) + if(not $CompSign{$LVer}{$Symbol}{"Private"}) { - if($CheckHeadersOnly or link_symbol($Symbol, $LibVersion, "-Deps")) { - $AllocableClass{$LibVersion}{$ClassName} = 1; + if($In::Opt{"CheckHeadersOnly"} or linkSymbol($Symbol, $LVer, "-Deps")) { + $AllocableClass{$LVer}{$ClassName} = 1; } } } - if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"}) + if(not $CompSign{$LVer}{$Symbol}{"Private"}) { # all imported class methods - if(symbolFilter($Symbol, $LibVersion, "Affected", "Binary")) + if(symbolFilter($Symbol, $CompSign{$LVer}{$Symbol}, "Affected", "Binary", $LVer)) { - if($CheckHeadersOnly) + if($In::Opt{"CheckHeadersOnly"}) { - if(not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"} - or $CompleteSignature{$LibVersion}{$Symbol}{"Virt"}) + if(not $CompSign{$LVer}{$Symbol}{"InLine"} + or $CompSign{$LVer}{$Symbol}{"Virt"}) { # all symbols except non-virtual inline - $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1; + $ClassMethods{"Binary"}{$LVer}{$ClassName}{$Symbol} = 1; } } else { - $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1; + $ClassMethods{"Binary"}{$LVer}{$ClassName}{$Symbol} = 1; } } - if(symbolFilter($Symbol, $LibVersion, "Affected", "Source")) { - $ClassMethods{"Source"}{$LibVersion}{$ClassName}{$Symbol} = 1; + if(symbolFilter($Symbol, $CompSign{$LVer}{$Symbol}, "Affected", "Source", $LVer)) { + $ClassMethods{"Source"}{$LVer}{$ClassName}{$Symbol} = 1; } } - $ClassNames{$LibVersion}{$ClassName} = 1; + $ClassNames{$LVer}{$ClassName} = 1; } - if(my $RetId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"}) + if(my $RetId = $CompSign{$LVer}{$Symbol}{"Return"}) { - my %Base = get_BaseType($RetId, $LibVersion); + my %Base = getBaseType($RetId, $LVer); if(defined $Base{"Type"} and $Base{"Type"}=~/Struct|Class/) { - my $Name = $TypeInfo{$LibVersion}{$Base{"Tid"}}{"Name"}; + my $Name = $TypeInfo{$LVer}{$Base{"Tid"}}{"Name"}; if($Name=~/<([^<>\s]+)>/) { - if(my $Tid = getTypeIdByName($1, $LibVersion)) { - $ReturnedClass{$LibVersion}{$Tid} = 1; + if(my $Tid = getTypeIdByName($1, $LVer)) { + $ReturnedClass{$LVer}{$Tid} = 1; } } else { - $ReturnedClass{$LibVersion}{$Base{"Tid"}} = 1; + $ReturnedClass{$LVer}{$Base{"Tid"}} = 1; } } } - foreach my $Num (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}})) + foreach my $Num (keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) { - my $PId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Num}{"type"}; - if(get_PLevel($PId, $LibVersion)>=1) + my $PId = $CompSign{$LVer}{$Symbol}{"Param"}{$Num}{"type"}; + if(getPLevel($PId, $LVer)>=1) { - if(my %Base = get_BaseType($PId, $LibVersion)) + if(my %Base = getBaseType($PId, $LVer)) { if($Base{"Type"}=~/Struct|Class/) { - $ParamClass{$LibVersion}{$Base{"Tid"}}{$Symbol} = 1; - foreach my $SubId (get_sub_classes($Base{"Tid"}, $LibVersion, 1)) + $ParamClass{$LVer}{$Base{"Tid"}}{$Symbol} = 1; + foreach my $SubId (getSubClasses($Base{"Tid"}, $LVer, 1)) { # mark all derived classes - $ParamClass{$LibVersion}{$SubId}{$Symbol} = 1; + $ParamClass{$LVer}{$SubId}{$Symbol} = 1; } } } @@ -9387,645 +1579,63 @@ } # mapping {short name => symbols} - $Func_ShortName{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"ShortName"}}{$Symbol} = 1; - } - foreach my $MnglName (keys(%VTableClass)) - { # reconstruct attributes of v-tables - if(index($MnglName, "_ZTV")==0) - { - if(my $ClassName = $VTableClass{$MnglName}) - { - if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName}) - { - $CompleteSignature{$LibVersion}{$MnglName}{"Header"} = $TypeInfo{$LibVersion}{$ClassId}{"Header"}; - $CompleteSignature{$LibVersion}{$MnglName}{"Class"} = $ClassId; - } - } - } + $Func_ShortName{$LVer}{$CompSign{$LVer}{$Symbol}{"ShortName"}}{$Symbol} = 1; } - # types - foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}})) + foreach my $ClassName (keys(%{$In::ABI{$LVer}{"ClassVTable"}})) { - if(my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"}) + my $MnglName = $In::ABI{$LVer}{"ClassVTable"}{$ClassName}; + + if(my $ClassId = $TName_Tid{$LVer}{$ClassName}) { - if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"}) { - $ClassNames{$LibVersion}{$TName} = 1; + if(my $H = $TypeInfo{$LVer}{$ClassId}{"Header"}) { + $CompSign{$LVer}{$MnglName}{"Header"} = $H; } - if(defined $TypeInfo{$LibVersion}{$TypeId}{"Base"}) - { - $ClassNames{$LibVersion}{$TName} = 1; - foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"Base"}})) - { - if(my $BName = $TypeInfo{$LibVersion}{$Bid}{"Name"}) { - $ClassNames{$LibVersion}{$BName} = 1; - } - } + if(my $S = $TypeInfo{$LVer}{$ClassId}{"Source"}) { + $CompSign{$LVer}{$MnglName}{"Source"} = $S; } + $CompSign{$LVer}{$MnglName}{"Class"} = $ClassId; + $CompSign{$LVer}{$MnglName}{"Unmangled"} = getSignature($MnglName, $LVer, "Class|Name"); } - } -} - -sub getFirst($$) -{ - my ($Tid, $LibVersion) = @_; - if(not $Tid) { - return $Tid; - } - - if(my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"}) - { - if($TName_Tid{$LibVersion}{$Name}) { - return $TName_Tid{$LibVersion}{$Name}; - } - } - - return $Tid; -} - -sub register_SymbolUsage($$$) -{ - my ($InfoId, $UsedType, $LibVersion) = @_; - - my %FuncInfo = %{$SymbolInfo{$LibVersion}{$InfoId}}; - if(my $RTid = getFirst($FuncInfo{"Return"}, $LibVersion)) - { - register_TypeUsage($RTid, $UsedType, $LibVersion); - $SymbolInfo{$LibVersion}{$InfoId}{"Return"} = $RTid; - } - if(my $FCid = getFirst($FuncInfo{"Class"}, $LibVersion)) - { - register_TypeUsage($FCid, $UsedType, $LibVersion); - $SymbolInfo{$LibVersion}{$InfoId}{"Class"} = $FCid; - if(my $ThisId = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."*const", $LibVersion)) - { # register "this" pointer - register_TypeUsage($ThisId, $UsedType, $LibVersion); - } - if(my $ThisId_C = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."const*const", $LibVersion)) - { # register "this" pointer (const method) - register_TypeUsage($ThisId_C, $UsedType, $LibVersion); - } - } - foreach my $PPos (keys(%{$FuncInfo{"Param"}})) - { - if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"}, $LibVersion)) - { - register_TypeUsage($PTid, $UsedType, $LibVersion); - $FuncInfo{"Param"}{$PPos}{"type"} = $PTid; - } - } - foreach my $TPos (keys(%{$FuncInfo{"TParam"}})) - { - my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"}; - if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) { - register_TypeUsage($TTid, $UsedType, $LibVersion); - } - } -} - -sub register_TypeUsage($$$) -{ - my ($TypeId, $UsedType, $LibVersion) = @_; - if(not $TypeId) { - return; - } - if($UsedType->{$TypeId}) - { # already registered - return; + $VTableClass{$LVer}{$MnglName} = $ClassName; } - my %TInfo = get_Type($TypeId, $LibVersion); - if($TInfo{"Type"}) + # types + foreach my $TypeId (keys(%{$TypeInfo{$LVer}})) { - if(my $NS = $TInfo{"NameSpace"}) + if(my $TName = $TypeInfo{$LVer}{$TypeId}{"Name"}) { - if(my $NSTid = $TName_Tid{$LibVersion}{$NS}) { - register_TypeUsage($NSTid, $UsedType, $LibVersion); + if(defined $TypeInfo{$LVer}{$TypeId}{"VTable"}) { + $ClassNames{$LVer}{$TName} = 1; } - } - - if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/) - { - $UsedType->{$TypeId} = 1; - if($TInfo{"Type"}=~/\A(Struct|Class)\Z/) - { - foreach my $BaseId (keys(%{$TInfo{"Base"}})) { - register_TypeUsage($BaseId, $UsedType, $LibVersion); - } - foreach my $TPos (keys(%{$TInfo{"TParam"}})) - { - my $TPName = $TInfo{"TParam"}{$TPos}{"name"}; - if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) { - register_TypeUsage($TTid, $UsedType, $LibVersion); - } - } - } - foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}})) + if(defined $TypeInfo{$LVer}{$TypeId}{"Base"}) { - if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"}, $LibVersion)) - { - register_TypeUsage($MTid, $UsedType, $LibVersion); - $TInfo{"Memb"}{$Memb_Pos}{"type"} = $MTid; - } - } - if($TInfo{"Type"} eq "FuncPtr" - or $TInfo{"Type"} eq "MethodPtr" - or $TInfo{"Type"} eq "Func") - { - if(my $RTid = $TInfo{"Return"}) { - register_TypeUsage($RTid, $UsedType, $LibVersion); - } - foreach my $PPos (keys(%{$TInfo{"Param"}})) + $ClassNames{$LVer}{$TName} = 1; + foreach my $Bid (keys(%{$TypeInfo{$LVer}{$TypeId}{"Base"}})) { - if(my $PTid = $TInfo{"Param"}{$PPos}{"type"}) { - register_TypeUsage($PTid, $UsedType, $LibVersion); + if(my $BName = $TypeInfo{$LVer}{$Bid}{"Name"}) { + $ClassNames{$LVer}{$BName} = 1; } } } - if($TInfo{"Type"} eq "FieldPtr") - { - if(my $RTid = $TInfo{"Return"}) { - register_TypeUsage($RTid, $UsedType, $LibVersion); - } - if(my $CTid = $TInfo{"Class"}) { - register_TypeUsage($CTid, $UsedType, $LibVersion); - } - } - if($TInfo{"Type"} eq "MethodPtr") - { - if(my $CTid = $TInfo{"Class"}) { - register_TypeUsage($CTid, $UsedType, $LibVersion); - } - } - } - elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|Restrict|Array|Typedef)\Z/) - { - $UsedType->{$TypeId} = 1; - if(my $BTid = getFirst($TInfo{"BaseType"}, $LibVersion)) - { - register_TypeUsage($BTid, $UsedType, $LibVersion); - $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $BTid; - } - } - else - { # Intrinsic, TemplateParam, TypeName, SizeOf, etc. - $UsedType->{$TypeId} = 1; } } } -sub selectSymbol($$$$) -{ # select symbol to check or to dump - my ($Symbol, $SInfo, $Level, $LibVersion) = @_; - - if($Level eq "Dump") - { - if($SInfo->{"Virt"} or $SInfo->{"PureVirt"}) - { # TODO: check if this symbol is from - # base classes of other target symbols - return 1; - } - } - - if(not $STDCXX_TESTING and $Symbol=~/\A(_ZS|_ZNS|_ZNKS)/) - { # stdc++ interfaces - return 0; - } - - my $Target = 0; - if(my $Header = $SInfo->{"Header"}) { - $Target = (is_target_header($Header, 1) or is_target_header($Header, 2)); - } - if($ExtendedCheck) - { - if(index($Symbol, "external_func_")==0) { - $Target = 1; - } - } - if($CheckHeadersOnly or $Level eq "Source") - { - if($Target) - { - if($Level eq "Dump") - { # dumped - if($BinaryOnly) - { - if(not $SInfo->{"InLine"} or $SInfo->{"Data"}) { - return 1; - } - } - else { - return 1; - } - } - elsif($Level eq "Source") - { # checked - return 1; - } - elsif($Level eq "Binary") - { # checked - if(not $SInfo->{"InLine"} or $SInfo->{"Data"} - or $SInfo->{"Virt"} or $SInfo->{"PureVirt"}) { - return 1; - } - } - } - } - else - { # library is available - if(link_symbol($Symbol, $LibVersion, "-Deps")) - { # exported symbols - return 1; - } - if($Level eq "Dump") - { # dumped - if($BinaryOnly) - { - if($SInfo->{"Data"}) - { - if($Target) { - return 1; - } - } - } - else - { # SrcBin - if($Target) { - return 1; - } - } - } - elsif($Level eq "Source") - { # checked - if($SInfo->{"PureVirt"} or $SInfo->{"Data"} or $SInfo->{"InLine"} - or isInLineInst($SInfo, $LibVersion)) - { # skip LOCAL symbols - if($Target) { - return 1; - } - } - } - elsif($Level eq "Binary") - { # checked - if($SInfo->{"PureVirt"} or $SInfo->{"Data"}) - { - if($Target) { - return 1; - } - } - } - } - return 0; -} - -sub cleanDump($) -{ # clean data - my $LibVersion = $_[0]; - foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}})) - { - if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}})) - { - delete($SymbolInfo{$LibVersion}{$InfoId}); - next; - } - my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"}; - if(not $MnglName) - { - delete($SymbolInfo{$LibVersion}{$InfoId}); - next; - } - my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"}; - if(not $ShortName) - { - delete($SymbolInfo{$LibVersion}{$InfoId}); - next; - } - if($MnglName eq $ShortName) - { # remove duplicate data - delete($SymbolInfo{$LibVersion}{$InfoId}{"MnglName"}); - } - if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})) { - delete($SymbolInfo{$LibVersion}{$InfoId}{"Param"}); - } - if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}})) { - delete($SymbolInfo{$LibVersion}{$InfoId}{"TParam"}); - } - delete($SymbolInfo{$LibVersion}{$InfoId}{"Type"}); - } - foreach my $Tid (keys(%{$TypeInfo{$LibVersion}})) - { - if(not keys(%{$TypeInfo{$LibVersion}{$Tid}})) - { - delete($TypeInfo{$LibVersion}{$Tid}); - next; - } - delete($TypeInfo{$LibVersion}{$Tid}{"Tid"}); - foreach my $Attr ("Header", "Line", "Size", "NameSpace") - { - if(not $TypeInfo{$LibVersion}{$Tid}{$Attr}) { - delete($TypeInfo{$LibVersion}{$Tid}{$Attr}); - } - } - if(not keys(%{$TypeInfo{$LibVersion}{$Tid}{"TParam"}})) { - delete($TypeInfo{$LibVersion}{$Tid}{"TParam"}); - } - } -} - -sub pickType($$) -{ - my ($Tid, $LibVersion) = @_; - - if(my $Dupl = $TypeTypedef{$LibVersion}{$Tid}) - { - if(defined $TypeInfo{$LibVersion}{$Dupl}) - { - if($TypeInfo{$LibVersion}{$Dupl}{"Name"} eq $TypeInfo{$LibVersion}{$Tid}{"Name"}) - { # duplicate - return 0; - } - } - } - - my $THeader = $TypeInfo{$LibVersion}{$Tid}{"Header"}; - - if(isBuiltIn($THeader)) { - return 0; - } - - if($TypeInfo{$LibVersion}{$Tid}{"Type"}!~/Class|Struct|Union|Enum|Typedef/) { - return 0; - } - - if(isAnon($TypeInfo{$LibVersion}{$Tid}{"Name"})) { - return 0; - } - - if(selfTypedef($Tid, $LibVersion)) { - return 0; - } - - if(not isTargetType($Tid, $LibVersion)) { - return 0; - } - - return 0; -} - -sub isTargetType($$) -{ - my ($Tid, $LibVersion) = @_; - - if($TypeInfo{$LibVersion}{$Tid}{"Type"}!~/Class|Struct|Union|Enum|Typedef/) - { # derived - return 1; - } - - if(my $THeader = $TypeInfo{$LibVersion}{$Tid}{"Header"}) - { - if(not is_target_header($THeader, $LibVersion)) - { # from target headers - return 0; - } - } - else { - return 0; - } - - if($SkipInternalTypes) - { - if($TypeInfo{$LibVersion}{$Tid}{"Name"}=~/($SkipInternalTypes)/) - { - return 0; - } - } - - return 1; -} - -sub remove_Unused($$) -{ # remove unused data types from the ABI dump - my ($LibVersion, $Kind) = @_; - - my %UsedType = (); - - foreach my $InfoId (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}})) - { - register_SymbolUsage($InfoId, \%UsedType, $LibVersion); - } - foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { - if($UsedType{$Tid}) - { # All & Extended - next; - } - - if($Kind eq "Extended") - { - if(pickType($Tid, $LibVersion)) - { - my %Tree = (); - register_TypeUsage($Tid, \%Tree, $LibVersion); - - my $Tmpl = 0; - foreach (sort {int($a)<=>int($b)} keys(%Tree)) - { - if(defined $TypeInfo{$LibVersion}{$_}{"Template"} - or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam") - { - $Tmpl = 1; - last; - } - } - if(not $Tmpl) - { - foreach (keys(%Tree)) { - $UsedType{$_} = 1; - } - } - } - } - } - - my %Delete = (); - - foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { # remove unused types - if($UsedType{$Tid}) - { # All & Extended - next; - } - - if($Kind eq "Extra") - { - my %Tree = (); - register_TypeUsage($Tid, \%Tree, $LibVersion); - - foreach (sort {int($a)<=>int($b)} keys(%Tree)) - { - if(defined $TypeInfo{$LibVersion}{$_}{"Template"} - or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam") - { - $Delete{$Tid} = 1; - last; - } - } - } - else - { - # remove type - delete($TypeInfo{$LibVersion}{$Tid}); - } - } - - if($Kind eq "Extra") - { # remove duplicates - foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { - if($UsedType{$Tid}) - { # All & Extended - next; - } - - my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"}; - - if($TName_Tid{$LibVersion}{$Name} ne $Tid) { - delete($TypeInfo{$LibVersion}{$Tid}); - } - } - } - - foreach my $Tid (keys(%Delete)) - { - delete($TypeInfo{$LibVersion}{$Tid}); - } -} - -sub check_Completeness($$) -{ - my ($Info, $LibVersion) = @_; - - # data types - if(defined $Info->{"Memb"}) - { - foreach my $Pos (keys(%{$Info->{"Memb"}})) - { - if(defined $Info->{"Memb"}{$Pos}{"type"}) { - check_TypeInfo($Info->{"Memb"}{$Pos}{"type"}, $LibVersion); - } - } - } - if(defined $Info->{"Base"}) - { - foreach my $Bid (keys(%{$Info->{"Base"}})) { - check_TypeInfo($Bid, $LibVersion); - } - } - if(defined $Info->{"BaseType"}) { - check_TypeInfo($Info->{"BaseType"}, $LibVersion); - } - if(defined $Info->{"TParam"}) - { - foreach my $Pos (keys(%{$Info->{"TParam"}})) - { - my $TName = $Info->{"TParam"}{$Pos}{"name"}; - if($TName=~/\A\(.+\)(true|false|\d.*)\Z/) { - next; - } - if($TName eq "_BoolType") { - next; - } - if($TName=~/\Asizeof\(/) { - next; - } - if(my $Tid = $TName_Tid{$LibVersion}{$TName}) { - check_TypeInfo($Tid, $LibVersion); - } - else - { - if(defined $Debug) { - printMsg("WARNING", "missed type $TName"); - } - } - } - } - - # symbols - if(defined $Info->{"Param"}) - { - foreach my $Pos (keys(%{$Info->{"Param"}})) - { - if(defined $Info->{"Param"}{$Pos}{"type"}) { - check_TypeInfo($Info->{"Param"}{$Pos}{"type"}, $LibVersion); - } - } - } - if(defined $Info->{"Return"}) { - check_TypeInfo($Info->{"Return"}, $LibVersion); - } - if(defined $Info->{"Class"}) { - check_TypeInfo($Info->{"Class"}, $LibVersion); - } -} - -sub check_TypeInfo($$) -{ - my ($Tid, $LibVersion) = @_; - - if(defined $CheckedTypeInfo{$LibVersion}{$Tid}) { - return; - } - $CheckedTypeInfo{$LibVersion}{$Tid} = 1; - - if(defined $TypeInfo{$LibVersion}{$Tid}) - { - if(not $TypeInfo{$LibVersion}{$Tid}{"Name"}) { - printMsg("ERROR", "missed type name ($Tid)"); - } - check_Completeness($TypeInfo{$LibVersion}{$Tid}, $LibVersion); - } - else { - printMsg("ERROR", "missed type id $Tid"); - } -} - -sub selfTypedef($$) -{ - my ($TypeId, $LibVersion) = @_; - my %Type = get_Type($TypeId, $LibVersion); - if($Type{"Type"} eq "Typedef") - { - my %Base = get_OneStep_BaseType($TypeId, $TypeInfo{$LibVersion}); - if($Base{"Type"}=~/Class|Struct/) - { - if($Type{"Name"} eq $Base{"Name"}) { - return 1; - } - elsif($Type{"Name"}=~/::(\w+)\Z/) - { - if($Type{"Name"} eq $Base{"Name"}."::".$1) - { # QPointer::QPointer - return 1; - } - } - } - } - return 0; -} - sub addExtension($) { - my $LibVersion = $_[0]; - foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) + my $LVer = $_[0]; + foreach my $Tid (sort {$a<=>$b} keys(%{$TypeInfo{$LVer}})) { - if(pickType($Tid, $LibVersion)) + if(pickType($Tid, $LVer)) { - my $TName = $TypeInfo{$LibVersion}{$Tid}{"Name"}; + my $TName = $TypeInfo{$LVer}{$Tid}{"Name"}; $TName=~s/\A(struct|union|class|enum) //; my $Symbol = "external_func_".$TName; + $Symbol=~s/\W+/_/g; - %{$CompleteSignature{$LibVersion}{$Symbol}} = ( + %{$CompSign{$LVer}{$Symbol}} = ( "Header" => "extended.h", "ShortName" => $Symbol, "MnglName" => $Symbol, @@ -10037,6 +1647,7 @@ $CheckedSymbols{"Source"}{$Symbol} = 1; } } + $ExtendedSymbols{"external_func_0"} = 1; $CheckedSymbols{"Binary"}{"external_func_0"} = 1; $CheckedSymbols{"Source"}{"external_func_0"} = 1; @@ -10044,33 +1655,37 @@ sub findMethod($$$) { - my ($VirtFunc, $ClassId, $LibVersion) = @_; - foreach my $BaseClass_Id (keys(%{$TypeInfo{$LibVersion}{$ClassId}{"Base"}})) + my ($VirtFunc, $ClassId, $LVer) = @_; + foreach my $BaseClass_Id (keys(%{$TypeInfo{$LVer}{$ClassId}{"Base"}})) { - if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LibVersion)) { + if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LVer)) { return $VirtMethodInClass; } - elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LibVersion)) { + elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LVer)) { return $VirtMethodInBaseClasses; } } - return ""; + return undef; } sub findMethod_Class($$$) { - my ($VirtFunc, $ClassId, $LibVersion) = @_; - my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}; - return "" if(not defined $VirtualTable{$LibVersion}{$ClassName}); - my $TargetSuffix = get_symbol_suffix($VirtFunc, 1); - my $TargetShortName = $CompleteSignature{$LibVersion}{$VirtFunc}{"ShortName"}; - foreach my $Candidate (keys(%{$VirtualTable{$LibVersion}{$ClassName}})) + my ($VirtFunc, $ClassId, $LVer) = @_; + + my $ClassName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + if(not defined $VirtualTable{$LVer}{$ClassName}) { + return undef; + } + my $TargetSuffix = getSignature($VirtFunc, $LVer, "Qual"); + my $TargetShortName = $CompSign{$LVer}{$VirtFunc}{"ShortName"}; + + foreach my $Candidate (keys(%{$VirtualTable{$LVer}{$ClassName}})) { # search for interface with the same parameters suffix (overridden) - if($TargetSuffix eq get_symbol_suffix($Candidate, 1)) + if($TargetSuffix eq getSignature($Candidate, $LVer, "Qual")) { - if($CompleteSignature{$LibVersion}{$VirtFunc}{"Destructor"}) + if($CompSign{$LVer}{$VirtFunc}{"Destructor"}) { - if($CompleteSignature{$LibVersion}{$Candidate}{"Destructor"}) + if($CompSign{$LVer}{$Candidate}{"Destructor"}) { if(($VirtFunc=~/D0E/ and $Candidate=~/D0E/) or ($VirtFunc=~/D1E/ and $Candidate=~/D1E/) @@ -10081,171 +1696,146 @@ } else { - if($TargetShortName eq $CompleteSignature{$LibVersion}{$Candidate}{"ShortName"}) { + if($TargetShortName eq $CompSign{$LVer}{$Candidate}{"ShortName"}) { return $Candidate; } } } } - return ""; + return undef; } sub registerVTable($) { - my $LibVersion = $_[0]; - foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}})) + my $LVer = $_[0]; + foreach my $Symbol (keys(%{$CompSign{$LVer}})) { - if($CompleteSignature{$LibVersion}{$Symbol}{"Virt"} - or $CompleteSignature{$LibVersion}{$Symbol}{"PureVirt"}) + if($CompSign{$LVer}{$Symbol}{"Virt"} + or $CompSign{$LVer}{$Symbol}{"PureVirt"}) { - my $ClassName = $TypeInfo{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"Class"}}{"Name"}; - next if(not $STDCXX_TESTING and $ClassName=~/\A(std::|__cxxabi)/); - if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"} + my $ClassName = $TypeInfo{$LVer}{$CompSign{$LVer}{$Symbol}{"Class"}}{"Name"}; + if(not $In::Opt{"StdcxxTesting"} and $ClassName=~/\A(std::|__cxxabi)/) { + next; + } + if($CompSign{$LVer}{$Symbol}{"Destructor"} and $Symbol=~/D2E/) { # pure virtual D2-destructors are marked as "virt" in the dump # virtual D2-destructors are NOT marked as "virt" in the dump # both destructors are not presented in the v-table next; } - my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol); - $VirtualTable{$LibVersion}{$ClassName}{$MnglName} = 1; + my ($MnglName, $VersionSpec, $SymbolVersion) = symbolParts($Symbol); + $VirtualTable{$LVer}{$ClassName}{$MnglName} = 1; } } } sub registerOverriding($) { - my $LibVersion = $_[0]; - my @Classes = keys(%{$VirtualTable{$LibVersion}}); - @Classes = sort {int($TName_Tid{$LibVersion}{$a})<=>int($TName_Tid{$LibVersion}{$b})} @Classes; + my $LVer = $_[0]; + my @Classes = keys(%{$VirtualTable{$LVer}}); + @Classes = sort {$TName_Tid{$LVer}{$a}<=>$TName_Tid{$LVer}{$b}} @Classes; foreach my $ClassName (@Classes) { - foreach my $VirtFunc (keys(%{$VirtualTable{$LibVersion}{$ClassName}})) + foreach my $VirtFunc (keys(%{$VirtualTable{$LVer}{$ClassName}})) { - if($CompleteSignature{$LibVersion}{$VirtFunc}{"PureVirt"}) + if($CompSign{$LVer}{$VirtFunc}{"PureVirt"}) { # pure virtuals next; } - my $ClassId = $TName_Tid{$LibVersion}{$ClassName}; - if(my $Overridden = findMethod($VirtFunc, $ClassId, $LibVersion)) + my $ClassId = $TName_Tid{$LVer}{$ClassName}; + if(my $Overridden = findMethod($VirtFunc, $ClassId, $LVer)) { - if($CompleteSignature{$LibVersion}{$Overridden}{"Virt"} - or $CompleteSignature{$LibVersion}{$Overridden}{"PureVirt"}) + if($CompSign{$LVer}{$Overridden}{"Virt"} + or $CompSign{$LVer}{$Overridden}{"PureVirt"}) { # both overridden virtual methods # and implemented pure virtual methods - $CompleteSignature{$LibVersion}{$VirtFunc}{"Override"} = $Overridden; - $OverriddenMethods{$LibVersion}{$Overridden}{$VirtFunc} = 1; - delete($VirtualTable{$LibVersion}{$ClassName}{$VirtFunc}); # remove from v-table model + $CompSign{$LVer}{$VirtFunc}{"Override"} = $Overridden; + $OverriddenMethods{$LVer}{$Overridden}{$VirtFunc} = 1; + + # remove from v-table model + delete($VirtualTable{$LVer}{$ClassName}{$VirtFunc}); } } } - if(not keys(%{$VirtualTable{$LibVersion}{$ClassName}})) { - delete($VirtualTable{$LibVersion}{$ClassName}); + if(not keys(%{$VirtualTable{$LVer}{$ClassName}})) { + delete($VirtualTable{$LVer}{$ClassName}); } } } sub setVirtFuncPositions($) { - my $LibVersion = $_[0]; - foreach my $ClassName (keys(%{$VirtualTable{$LibVersion}})) + my $LVer = $_[0]; + foreach my $ClassName (keys(%{$VirtualTable{$LVer}})) { my ($Num, $Rel) = (1, 0); - if(my @Funcs = sort keys(%{$VirtualTable{$LibVersion}{$ClassName}})) + if(my @Funcs = sort keys(%{$VirtualTable{$LVer}{$ClassName}})) { - if($UsedDump{$LibVersion}{"DWARF"}) { - @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @Funcs; + if($UsedDump{$LVer}{"DWARF"}) { + @Funcs = sort {$CompSign{$LVer}{$a}{"VirtPos"}<=>$CompSign{$LVer}{$b}{"VirtPos"}} @Funcs; } else { - @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @Funcs; + @Funcs = sort {$CompSign{$LVer}{$a}{"Line"}<=>$CompSign{$LVer}{$b}{"Line"}} @Funcs; } foreach my $VirtFunc (@Funcs) { - if($UsedDump{$LibVersion}{"DWARF"}) { - $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $CompleteSignature{$LibVersion}{$VirtFunc}{"VirtPos"}; + if($UsedDump{$LVer}{"DWARF"}) + { + if(defined $CompSign{$LVer}{$VirtFunc}{"VirtPos"}) { + $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = $CompSign{$LVer}{$VirtFunc}{"VirtPos"}; + } + else { + $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = 1; + } } else { - $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $Num++; + $VirtualTable{$LVer}{$ClassName}{$VirtFunc} = $Num++; } # set relative positions if(defined $VirtualTable{1}{$ClassName} and defined $VirtualTable{1}{$ClassName}{$VirtFunc} and defined $VirtualTable{2}{$ClassName} and defined $VirtualTable{2}{$ClassName}{$VirtFunc}) { # relative position excluding added and removed virtual functions - if(not $CompleteSignature{1}{$VirtFunc}{"Override"} - and not $CompleteSignature{2}{$VirtFunc}{"Override"}) { - $CompleteSignature{$LibVersion}{$VirtFunc}{"RelPos"} = $Rel++; + if(not $CompSign{1}{$VirtFunc}{"Override"} + and not $CompSign{2}{$VirtFunc}{"Override"}) { + $CompSign{$LVer}{$VirtFunc}{"RelPos"} = $Rel++; } } } } } - foreach my $ClassName (keys(%{$ClassNames{$LibVersion}})) + foreach my $ClassName (keys(%{$ClassNames{$LVer}})) { my $AbsNum = 1; - foreach my $VirtFunc (getVTable_Model($TName_Tid{$LibVersion}{$ClassName}, $LibVersion)) { - $VirtualTable_Model{$LibVersion}{$ClassName}{$VirtFunc} = $AbsNum++; - } - } -} - -sub get_sub_classes($$$) -{ - my ($ClassId, $LibVersion, $Recursive) = @_; - return () if(not defined $Class_SubClasses{$LibVersion}{$ClassId}); - my @Subs = (); - foreach my $SubId (keys(%{$Class_SubClasses{$LibVersion}{$ClassId}})) - { - if($Recursive) - { - foreach my $SubSubId (get_sub_classes($SubId, $LibVersion, $Recursive)) { - push(@Subs, $SubSubId); - } - } - push(@Subs, $SubId); - } - return @Subs; -} - -sub get_base_classes($$$) -{ - my ($ClassId, $LibVersion, $Recursive) = @_; - my %ClassType = get_Type($ClassId, $LibVersion); - return () if(not defined $ClassType{"Base"}); - my @Bases = (); - foreach my $BaseId (sort {int($ClassType{"Base"}{$a}{"pos"})<=>int($ClassType{"Base"}{$b}{"pos"})} - keys(%{$ClassType{"Base"}})) - { - if($Recursive) - { - foreach my $SubBaseId (get_base_classes($BaseId, $LibVersion, $Recursive)) { - push(@Bases, $SubBaseId); - } + foreach my $VirtFunc (getVTable_Model($ClassName, $LVer)) { + $VirtualTable_Model{$LVer}{$ClassName}{$VirtFunc} = $AbsNum++; } - push(@Bases, $BaseId); } - return @Bases; } sub getVTable_Model($$) { # return an ordered list of v-table elements - my ($ClassId, $LibVersion) = @_; - my @Bases = get_base_classes($ClassId, $LibVersion, 1); + my ($ClassName, $LVer) = @_; + + my $ClassId = $TName_Tid{$LVer}{$ClassName}; + my @Bases = getBaseClasses($ClassId, $LVer, 1); my @Elements = (); foreach my $BaseId (@Bases, $ClassId) { - if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"}) + if(my $BName = $TypeInfo{$LVer}{$BaseId}{"Name"}) { - if(defined $VirtualTable{$LibVersion}{$BName}) + if(defined $VirtualTable{$LVer}{$BName}) { - my @VFuncs = keys(%{$VirtualTable{$LibVersion}{$BName}}); - if($UsedDump{$LibVersion}{"DWARF"}) { - @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @VFuncs; + my @VFuncs = sort keys(%{$VirtualTable{$LVer}{$BName}}); + if($UsedDump{$LVer}{"DWARF"}) { + @VFuncs = sort {$CompSign{$LVer}{$a}{"VirtPos"} <=> $CompSign{$LVer}{$b}{"VirtPos"}} @VFuncs; } else { - @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @VFuncs; + @VFuncs = sort {$CompSign{$LVer}{$a}{"Line"} <=> $CompSign{$LVer}{$b}{"Line"}} @VFuncs; } + foreach my $VFunc (@VFuncs) { push(@Elements, $VFunc); } @@ -10257,15 +1847,15 @@ sub getVShift($$) { - my ($ClassId, $LibVersion) = @_; - my @Bases = get_base_classes($ClassId, $LibVersion, 1); + my ($ClassId, $LVer) = @_; + my @Bases = getBaseClasses($ClassId, $LVer, 1); my $VShift = 0; foreach my $BaseId (@Bases) { - if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"}) + if(my $BName = $TypeInfo{$LVer}{$BaseId}{"Name"}) { - if(defined $VirtualTable{$LibVersion}{$BName}) { - $VShift+=keys(%{$VirtualTable{$LibVersion}{$BName}}); + if(defined $VirtualTable{$LVer}{$BName}) { + $VShift+=keys(%{$VirtualTable{$LVer}{$BName}}); } } } @@ -10274,12 +1864,12 @@ sub getShift($$) { - my ($ClassId, $LibVersion) = @_; - my @Bases = get_base_classes($ClassId, $LibVersion, 0); + my ($ClassId, $LVer) = @_; + my @Bases = getBaseClasses($ClassId, $LVer, 0); my $Shift = 0; foreach my $BaseId (@Bases) { - if(my $Size = $TypeInfo{$LibVersion}{$BaseId}{"Size"}) + if(my $Size = $TypeInfo{$LVer}{$BaseId}{"Size"}) { if($Size!=1) { # not empty base class @@ -10292,40 +1882,42 @@ sub getVTable_Size($$) { # number of v-table elements - my ($ClassName, $LibVersion) = @_; + my ($ClassName, $LVer) = @_; my $Size = 0; # three approaches if(not $Size) { # real size - if(my %VTable = getVTable_Real($ClassName, $LibVersion)) { + if(my %VTable = getVTable_Real($ClassName, $LVer)) { $Size = keys(%VTable); } } if(not $Size) { # shared library symbol size - if($Size = getSymbolSize($ClassVTable{$ClassName}, $LibVersion)) { - $Size /= $WORD_SIZE{$LibVersion}; + if(my $VTSym = $In::ABI{$LVer}{"ClassVTable"}{$ClassName}) + { + if($Size = getSymbolSize($VTSym, $LVer)) { + $Size /= $In::ABI{$LVer}{"WordSize"}; + } } } if(not $Size) { # model size - if(defined $VirtualTable_Model{$LibVersion}{$ClassName}) { - $Size = keys(%{$VirtualTable_Model{$LibVersion}{$ClassName}}) + 2; + if(defined $VirtualTable_Model{$LVer}{$ClassName}) { + $Size = keys(%{$VirtualTable_Model{$LVer}{$ClassName}}) + 2; } } return $Size; } -sub isCopyingClass($$) -{ - my ($TypeId, $LibVersion) = @_; - return $TypeInfo{$LibVersion}{$TypeId}{"Copied"}; -} - sub isLeafClass($$) { - my ($ClassId, $LibVersion) = @_; - return (not keys(%{$Class_SubClasses{$LibVersion}{$ClassId}})); + my ($ClassId, $LVer) = @_; + if(not defined $In::ABI{$LVer}{"Class_SubClasses"}{$ClassId} + or not keys(%{$In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}})) { + return 1; + } + + return 0; } sub havePubFields($) @@ -10336,11 +1928,13 @@ sub isAccessible($$$$) { # check interval in structured type for public fields my ($TypePtr, $Skip, $Start, $End) = @_; - return 0 if(not $TypePtr); + if(not $TypePtr) { + return 0; + } if($End==-1) { $End = keys(%{$TypePtr->{"Memb"}})-1; } - foreach my $MemPos (sort {int($a)<=>int($b)} keys(%{$TypePtr->{"Memb"}})) + foreach my $MemPos (sort {$a<=>$b} keys(%{$TypePtr->{"Memb"}})) { if($Skip and $Skip->{$MemPos}) { # skip removed/added fields @@ -10356,58 +1950,45 @@ return 0; } -sub isReserved($) -{ # reserved fields == private - my $MName = $_[0]; - if($MName=~/reserved|padding|f_spare/i) { - return 1; - } - if($MName=~/\A[_]*(spare|pad|unused|dummy)[_\d]*\Z/i) { - return 1; - } - if($MName=~/(pad\d+)/i) { - return 1; - } - return 0; -} - sub isPublic($$) { my ($TypePtr, $FieldPos) = @_; + return 0 if(not $TypePtr); return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}); return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}{"name"}); - if(not $TypePtr->{"Memb"}{$FieldPos}{"access"}) - { # by name in C language - # FIXME: add other methods to detect private members - my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"}; - if($MName=~/priv|abidata|parent_object/i) - { # C-styled private data - return 0; - } - if(lc($MName) eq "abi") - { # ABI information/reserved field - return 0; - } - if(isReserved($MName)) - { # reserved fields - return 0; - } - return 1; - } - elsif($TypePtr->{"Memb"}{$FieldPos}{"access"} ne "private") + + my $Access = $TypePtr->{"Memb"}{$FieldPos}{"access"}; + if($Access eq "private") { # by access in C++ language - return 1; + return 0; } - return 0; + + # by name in C language + # TODO: add other methods to detect private members + my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"}; + if($MName=~/priv|abidata|parent_object|impl/i) + { # C-styled private data + return 0; + } + if(lc($MName) eq "abi") + { # ABI information/reserved field + return 0; + } + if(isReserved($MName)) + { # reserved fields + return 0; + } + + return 1; } sub getVTable_Real($$) { - my ($ClassName, $LibVersion) = @_; - if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName}) + my ($ClassName, $LVer) = @_; + if(my $ClassId = $TName_Tid{$LVer}{$ClassName}) { - my %Type = get_Type($ClassId, $LibVersion); + my %Type = getType($ClassId, $LVer); if(defined $Type{"VTable"}) { return %{$Type{"VTable"}}; } @@ -10450,7 +2031,7 @@ return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = -1); } my %Indexes = map {$_=>1} (keys(%VTable_Old), keys(%VTable_New)); - foreach my $Offset (sort {int($a)<=>int($b)} keys(%Indexes)) + foreach my $Offset (sort {$a<=>$b} keys(%Indexes)) { if(not defined $VTable_Old{$Offset}) { # v-table v.1 < v-table v.2 @@ -10466,7 +2047,7 @@ $Entry1 = simpleVEntry($Entry1); $Entry2 = simpleVEntry($Entry2); - if($Entry1=~/ 0x/ and $Entry2=~/ 0x/) + if($Entry1=~/ 0x/ or $Entry2=~/ 0x/) { # NOTE: problem with vtable-dumper next; } @@ -10485,7 +2066,7 @@ } } } - if(differentDumps("G")) + if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"}) { if($Entry1=~/\A\-(0x|\d+)/ and $Entry2=~/\A\-(0x|\d+)/) { @@ -10505,6 +2086,11 @@ my $Level = $_[0]; foreach my $ClassName (keys(%{$VirtualTable{1}})) { + my $ClassId = $TName_Tid{1}{$ClassName}; + if(isPrivateABI($ClassId, 1)) { + next; + } + if($VTableChanged_M{$ClassName}) { # already registered next; @@ -10525,10 +2111,15 @@ sub mergeBases($) { my $Level = $_[0]; - foreach my $ClassName (keys(%{$ClassNames{1}})) + foreach my $ClassName (sort keys(%{$ClassNames{1}})) { # detect added and removed virtual functions my $ClassId = $TName_Tid{1}{$ClassName}; next if(not $ClassId); + + if(isPrivateABI($ClassId, 1)) { + next; + } + if(defined $VirtualTable{2}{$ClassName}) { foreach my $Symbol (keys(%{$VirtualTable{2}{$ClassName}})) @@ -10536,11 +2127,20 @@ if($TName_Tid{1}{$ClassName} and not defined $VirtualTable{1}{$ClassName}{$Symbol}) { # added to v-table - if(defined $CompleteSignature{1}{$Symbol} - and $CompleteSignature{1}{$Symbol}{"Virt"}) - { # override some method in v.1 - next; + if(defined $CompSign{1}{$Symbol}) + { + if($CompSign{1}{$Symbol}{"Virt"}) + { # override some method in v.1 + next; + } } + else + { + if(linkSymbol($Symbol, 1, "+Deps")) { + next; + } + } + $AddedInt_Virt{$Level}{$ClassName}{$Symbol} = 1; } } @@ -10552,18 +2152,28 @@ if($TName_Tid{2}{$ClassName} and not defined $VirtualTable{2}{$ClassName}{$Symbol}) { # removed from v-table - if(defined $CompleteSignature{2}{$Symbol} - and $CompleteSignature{2}{$Symbol}{"Virt"}) - { # override some method in v.2 - next; + if(defined $CompSign{2}{$Symbol}) + { + if($CompSign{2}{$Symbol}{"Virt"}) + { # override some method in v.2 + next; + } + } + else + { + if(linkSymbol($Symbol, 2, "+Deps")) { + next; + } } + $RemovedInt_Virt{$Level}{$ClassName}{$Symbol} = 1; } } } + if($Level eq "Binary") { # Binary-level - my %Class_Type = get_Type($ClassId, 1); + my %Class_Type = getType($ClassId, 1); foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$ClassName}})) { # check replacements, including pure virtual methods my $AddedPos = $VirtualTable{2}{$ClassName}{$AddedVFunc}; @@ -10583,9 +2193,11 @@ { # skip: DomUi => DomUI parameter (Qt 4.2.3 to 4.3.0) next; } + my $ProblemType = "Virtual_Replacement"; - my @Affected = ($RemovedVFunc); - if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) + my @Affected = (); + + if($CompSign{1}{$RemovedVFunc}{"PureVirt"}) { # pure methods if(not isUsedClass($ClassId, 1, $Level)) { # not a parameter of some exported method @@ -10595,52 +2207,55 @@ # affected all methods (both virtual and non-virtual ones) @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}})); - push(@Affected, keys(%{$OverriddenMethods{1}{$RemovedVFunc}})); } - $VTableChanged_M{$ClassName}=1; + else { + @Affected = ($RemovedVFunc); + } + + push(@Affected, keys(%{$OverriddenMethods{1}{$RemovedVFunc}})); + + $VTableChanged_M{$ClassName} = 1; + foreach my $AffectedInt (@Affected) { - if($CompleteSignature{1}{$AffectedInt}{"PureVirt"}) + if($CompSign{1}{$AffectedInt}{"PureVirt"}) { # affected exported methods only next; } - if(not symbolFilter($AffectedInt, 1, "Affected", $Level)) { + if(not symbolFilter($AffectedInt, $CompSign{1}{$AffectedInt}, "Affected", $Level, 1)) { next; } - %{$CompatProblems{$Level}{$AffectedInt}{$ProblemType}{$tr_name{$AddedVFunc}}}=( + %{$CompatProblems{$Level}{$AffectedInt}{$ProblemType}{$AddedVFunc}}=( "Type_Name"=>$Class_Type{"Name"}, - "Target"=>get_Signature($AddedVFunc, 2), - "Old_Value"=>get_Signature($RemovedVFunc, 1)); + "Target"=>$AddedVFunc, + "Old_Value"=>$RemovedVFunc); } } } } } - if(not checkDump(1, "2.0") - or not checkDump(2, "2.0")) - { # support for old ABI dumps - # "Base" attribute introduced in ACC 1.22 (ABI dump 2.0 format) - return; - } + foreach my $ClassName (sort keys(%{$ClassNames{1}})) { my $ClassId_Old = $TName_Tid{1}{$ClassName}; next if(not $ClassId_Old); + + if(isPrivateABI($ClassId_Old, 1)) { + next; + } + if(not isCreatable($ClassId_Old, 1)) { # skip classes without public constructors (including auto-generated) # example: class has only a private exported or private inline constructor next; } - if($ClassName=~/>/) - { # skip affected template instances - next; - } - my %Class_Old = get_Type($ClassId_Old, 1); + + my %Class_Old = getType($ClassId_Old, 1); my $ClassId_New = $TName_Tid{2}{$ClassName}; if(not $ClassId_New) { next; } - my %Class_New = get_Type($ClassId_New, 2); + my %Class_New = getType($ClassId_New, 2); if($Class_New{"Type"}!~/Class|Struct/) { # became typedef if($Level eq "Binary") { @@ -10648,7 +2263,7 @@ } if($Level eq "Source") { - %Class_New = get_PureType($ClassId_New, $TypeInfo{2}); + %Class_New = getPureType($ClassId_New, 2); if($Class_New{"Type"}!~/Class|Struct/) { next; } @@ -10661,22 +2276,55 @@ next; } + if($Level eq "Binary" and cmpVTables_Real($ClassName, 1)!=0) + { + foreach my $Symbol (sort keys(%{$RemovedInt_Virt{$Level}{$ClassName}})) + { + if($VirtualReplacement{$Symbol}) { + next; + } + + if(symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) + { + my $ProblemType = "Removed_Virtual_Method"; + if($CompSign{1}{$Symbol}{"PureVirt"}) { + $ProblemType = "Removed_Pure_Virtual_Method"; + } + + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($Symbol, 1, "Class|Name|Qual")}}=( + "Type_Name"=>$ClassName, + "Target"=>$Symbol); + } + + $VTableChanged_M{$ClassName} = 1; + foreach my $SubId (getSubClasses($ClassId_Old, 1, 1)) + { + if(my $SubName = $TypeInfo{1}{$SubId}{"Name"}) { + $VTableChanged_M{$SubName} = 1; + } + } + } + } + + if(index($ClassName, ">")!=-1) + { # skip affected template instances + next; + } my @Bases_Old = sort {$Class_Old{"Base"}{$a}{"pos"}<=>$Class_Old{"Base"}{$b}{"pos"}} keys(%{$Class_Old{"Base"}}); my @Bases_New = sort {$Class_New{"Base"}{$a}{"pos"}<=>$Class_New{"Base"}{$b}{"pos"}} keys(%{$Class_New{"Base"}}); - my %Tr_Old = map {$TypeInfo{1}{$_}{"Name"} => uncover_typedefs($TypeInfo{1}{$_}{"Name"}, 1)} @Bases_Old; - my %Tr_New = map {$TypeInfo{2}{$_}{"Name"} => uncover_typedefs($TypeInfo{2}{$_}{"Name"}, 2)} @Bases_New; + my %Tr_Old = map {$TypeInfo{1}{$_}{"Name"} => uncoverTypedefs($TypeInfo{1}{$_}{"Name"}, 1)} @Bases_Old; + my %Tr_New = map {$TypeInfo{2}{$_}{"Name"} => uncoverTypedefs($TypeInfo{2}{$_}{"Name"}, 2)} @Bases_New; my ($BNum1, $BNum2) = (1, 1); my %BasePos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @Bases_Old; my %BasePos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @Bases_New; - my %ShortBase_Old = map {get_ShortClass($_, 1) => 1} @Bases_Old; - my %ShortBase_New = map {get_ShortClass($_, 2) => 1} @Bases_New; + my %ShortBase_Old = map {getShortClass($_, 1) => 1} @Bases_Old; + my %ShortBase_New = map {getShortClass($_, 2) => 1} @Bases_New; my $Shift_Old = getShift($ClassId_Old, 1); my $Shift_New = getShift($ClassId_New, 2); my %BaseId_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @Bases_New; - my ($Added, $Removed) = (0, 0); my @StableBases_Old = (); foreach my $BaseId (@Bases_Old) { @@ -10685,7 +2333,7 @@ push(@StableBases_Old, $BaseId); } elsif(not $ShortBase_New{$Tr_Old{$BaseName}} - and not $ShortBase_New{get_ShortClass($BaseId, 1)}) + and not $ShortBase_New{getShortClass($BaseId, 1)}) { # removed base # excluding namespace::SomeClass to SomeClass renaming my $ProblemKind = "Removed_Base_Class"; @@ -10704,33 +2352,32 @@ and cmpVTables($ClassName)==1) { # affected v-table $ProblemKind .= "_And_VTable"; - $VTableChanged_M{$ClassName}=1; + $VTableChanged_M{$ClassName} = 1; } } my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}}); - foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1)) + foreach my $SubId (getSubClasses($ClassId_Old, 1, 1)) { if(my $SubName = $TypeInfo{1}{$SubId}{"Name"}) { push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}})); if($ProblemKind=~/VTable/) { - $VTableChanged_M{$SubName}=1; + $VTableChanged_M{$SubName} = 1; } } } foreach my $Interface (@Affected) { - if(not symbolFilter($Interface, 1, "Affected", $Level)) { - next; + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName, + "Old_Size"=>$Class_Old{"Size"}*$BYTE, + "New_Size"=>$Class_New{"Size"}*$BYTE, + "Shift"=>abs($Shift_New-$Shift_Old)); } - %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=( - "Type_Name"=>$ClassName, - "Target"=>$BaseName, - "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE, - "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE, - "Shift"=>abs($Shift_New-$Shift_Old) ); } - $Removed+=1; } } my @StableBases_New = (); @@ -10741,7 +2388,7 @@ push(@StableBases_New, $BaseId); } elsif(not $ShortBase_Old{$Tr_New{$BaseName}} - and not $ShortBase_Old{get_ShortClass($BaseId, 2)}) + and not $ShortBase_Old{getShortClass($BaseId, 2)}) { # added base # excluding namespace::SomeClass to SomeClass renaming my $ProblemKind = "Added_Base_Class"; @@ -10760,33 +2407,32 @@ and cmpVTables($ClassName)==1) { # affected v-table $ProblemKind .= "_And_VTable"; - $VTableChanged_M{$ClassName}=1; + $VTableChanged_M{$ClassName} = 1; } } my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}}); - foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1)) + foreach my $SubId (getSubClasses($ClassId_Old, 1, 1)) { if(my $SubName = $TypeInfo{1}{$SubId}{"Name"}) { push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}})); if($ProblemKind=~/VTable/) { - $VTableChanged_M{$SubName}=1; + $VTableChanged_M{$SubName} = 1; } } } foreach my $Interface (@Affected) { - if(not symbolFilter($Interface, 1, "Affected", $Level)) { - next; + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName, + "Old_Size"=>$Class_Old{"Size"}*$BYTE, + "New_Size"=>$Class_New{"Size"}*$BYTE, + "Shift"=>abs($Shift_New-$Shift_Old)); } - %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=( - "Type_Name"=>$ClassName, - "Target"=>$BaseName, - "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE, - "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE, - "Shift"=>abs($Shift_New-$Shift_Old) ); } - $Added+=1; } } if($Level eq "Binary") @@ -10805,14 +2451,14 @@ { # changed position of the base class foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) { - if(not symbolFilter($Interface, 1, "Affected", $Level)) { - next; + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{"Base_Class_Position"}{"this"}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName, + "Old_Value"=>$OldPos-1, + "New_Value"=>$NewPos-1); } - %{$CompatProblems{$Level}{$Interface}{"Base_Class_Position"}{"this"}}=( - "Type_Name"=>$ClassName, - "Target"=>$BaseName, - "Old_Value"=>$OldPos-1, - "New_Value"=>$NewPos-1 ); } } if($Class_Old{"Base"}{$BaseId}{"virtual"} @@ -10820,12 +2466,12 @@ { # became non-virtual base foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) { - if(not symbolFilter($Interface, 1, "Affected", $Level)) { - next; + if(symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=( + "Type_Name"=>$ClassName, + "Target"=>$BaseName ); } - %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=( - "Type_Name"=>$ClassName, - "Target"=>$BaseName ); } } elsif(not $Class_Old{"Base"}{$BaseId}{"virtual"} @@ -10833,7 +2479,7 @@ { # became virtual base foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) { - if(not symbolFilter($Interface, 1, "Affected", $Level)) { + if(not symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) { next; } %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Virtually_Inherited"}{"this->".$BaseName}}=( @@ -10848,7 +2494,7 @@ { # size of allocable class foreach my $BaseId (@StableBases_Old) { # search for changed base - my %BaseType = get_Type($BaseId, 1); + my %BaseType = getType($BaseId, 1); my $Size_Old = $TypeInfo{1}{$BaseId}{"Size"}; my $Size_New = $TypeInfo{2}{$BaseId_New{$Tr_Old{$BaseType{"Name"}}}}{"Size"}; if($Size_Old ne $Size_New @@ -10876,18 +2522,19 @@ next if(not $ProblemType); foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}})) { # base class size changes affecting current class - if(not symbolFilter($Interface, 1, "Affected", $Level)) { + if(not symbolFilter($Interface, $CompSign{1}{$Interface}, "Affected", $Level, 1)) { next; } %{$CompatProblems{$Level}{$Interface}{$ProblemType}{"this->".$BaseType{"Name"}}}=( "Type_Name"=>$BaseType{"Name"}, "Target"=>$BaseType{"Name"}, - "Old_Size"=>$Size_Old*$BYTE_SIZE, - "New_Size"=>$Size_New*$BYTE_SIZE ); + "Old_Size"=>$Size_Old*$BYTE, + "New_Size"=>$Size_New*$BYTE ); } } } } + if(defined $VirtualTable_Model{1}{$ClassName} and cmpVTables_Real($ClassName, 1)==1 and my @VFunctions = keys(%{$VirtualTable_Model{1}{$ClassName}})) @@ -10896,13 +2543,13 @@ my $VShift_New = getVShift($ClassId_New, 2); if($VShift_Old ne $VShift_New) { # changes in the base class or changes in the list of base classes - my @AllBases_Old = get_base_classes($ClassId_Old, 1, 1); - my @AllBases_New = get_base_classes($ClassId_New, 2, 1); + my @AllBases_Old = getBaseClasses($ClassId_Old, 1, 1); + my @AllBases_New = getBaseClasses($ClassId_New, 2, 1); ($BNum1, $BNum2) = (1, 1); my %StableBase = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @AllBases_New; foreach my $BaseId (@AllBases_Old) { - my %BaseType = get_Type($BaseId, 1); + my %BaseType = getType($BaseId, 1); if(not $StableBase{$Tr_Old{$BaseType{"Name"}}}) { # lost base next; @@ -10917,36 +2564,41 @@ { # Removed_Virtual_Method, will be registered in mergeVirtualTables() next; } + if($VirtualTable_Model{2}{$ClassName}{$Symbol}-$VirtualTable_Model{1}{$ClassName}{$Symbol}==0) { # skip interfaces that have not changed the absolute virtual position next; } - if(not symbolFilter($Symbol, 1, "Affected", $Level)) { + + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) { next; } + $VTableChanged_M{$BaseType{"Name"}} = 1; $VTableChanged_M{$ClassName} = 1; + foreach my $VirtFunc (keys(%{$AddedInt_Virt{$Level}{$BaseType{"Name"}}})) { # the reason of the layout change: added virtual functions next if($VirtualReplacement{$VirtFunc}); my $ProblemType = "Added_Virtual_Method"; - if($CompleteSignature{2}{$VirtFunc}{"PureVirt"}) { + if($CompSign{2}{$VirtFunc}{"PureVirt"}) { $ProblemType = "Added_Pure_Virtual_Method"; } - %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 2)}}=( + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($VirtFunc, 2, "Class|Name|Qual")}}=( "Type_Name"=>$BaseType{"Name"}, - "Target"=>get_Signature($VirtFunc, 2) ); + "Target"=>$VirtFunc ); } + foreach my $VirtFunc (keys(%{$RemovedInt_Virt{$Level}{$BaseType{"Name"}}})) { # the reason of the layout change: removed virtual functions next if($VirtualReplacement{$VirtFunc}); my $ProblemType = "Removed_Virtual_Method"; - if($CompleteSignature{1}{$VirtFunc}{"PureVirt"}) { + if($CompSign{1}{$VirtFunc}{"PureVirt"}) { $ProblemType = "Removed_Pure_Virtual_Method"; } - %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 1)}}=( + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{getSignature($VirtFunc, 1, "Class|Name|Qual")}}=( "Type_Name"=>$BaseType{"Name"}, - "Target"=>get_Signature($VirtFunc, 1) ); + "Target"=>$VirtFunc ); } } } @@ -10959,17 +2611,17 @@ sub isCreatable($$) { - my ($ClassId, $LibVersion) = @_; - if($AllocableClass{$LibVersion}{$TypeInfo{$LibVersion}{$ClassId}{"Name"}} - or isCopyingClass($ClassId, $LibVersion)) { + my ($ClassId, $LVer) = @_; + if($AllocableClass{$LVer}{$TypeInfo{$LVer}{$ClassId}{"Name"}} + or isCopyingClass($ClassId, $LVer)) { return 1; } - if(keys(%{$Class_SubClasses{$LibVersion}{$ClassId}})) + if(keys(%{$In::ABI{$LVer}{"Class_SubClasses"}{$ClassId}})) { # Fix for incomplete data: if this class has # a base class then it should also has a constructor return 1; } - if($ReturnedClass{$LibVersion}{$ClassId}) + if($ReturnedClass{$LVer}{$ClassId}) { # returned by some method of this class # or any other class return 1; @@ -10979,13 +2631,13 @@ sub isUsedClass($$$) { - my ($ClassId, $LibVersion, $Level) = @_; - if(keys(%{$ParamClass{$LibVersion}{$ClassId}})) + my ($ClassId, $LVer, $Level) = @_; + if(keys(%{$ParamClass{$LVer}{$ClassId}})) { # parameter of some exported method return 1; } - my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}; - if(keys(%{$ClassMethods{$Level}{$LibVersion}{$CName}})) + my $CName = $TypeInfo{$LVer}{$ClassId}{"Name"}; + if(keys(%{$ClassMethods{$Level}{$LVer}{$CName}})) { # method from target class return 1; } @@ -10999,11 +2651,11 @@ # - virtual # - pure-virtual # - non-virtual - if($CompleteSignature{1}{$Interface}{"Data"}) + if($CompSign{1}{$Interface}{"Data"}) { # global data is not affected return; } - my $Class_Id = $CompleteSignature{1}{$Interface}{"Class"}; + my $Class_Id = $CompSign{1}{$Interface}{"Class"}; if(not $Class_Id) { return; } @@ -11015,7 +2667,7 @@ $CheckedTypes{$Level}{$CName} = 1; if($Level eq "Binary") { # Binary-level - if($CompleteSignature{1}{$Interface}{"PureVirt"} + if($CompSign{1}{$Interface}{"PureVirt"} and not isUsedClass($Class_Id, 1, $Level)) { # pure virtuals should not be affected # if there are no exported methods using this class @@ -11025,22 +2677,22 @@ foreach my $Func (keys(%{$VirtualTable{1}{$CName}})) { if(defined $VirtualTable{2}{$CName}{$Func} - and defined $CompleteSignature{2}{$Func}) + and defined $CompSign{2}{$Func}) { - if(not $CompleteSignature{1}{$Func}{"PureVirt"} - and $CompleteSignature{2}{$Func}{"PureVirt"}) + if(not $CompSign{1}{$Func}{"PureVirt"} + and $CompSign{2}{$Func}{"PureVirt"}) { # became pure virtual - %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Pure"}{$tr_name{$Func}}}=( + %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Pure"}{$Func}}=( "Type_Name"=>$CName, - "Target"=>get_Signature_M($Func, 1) ); + "Target"=>$Func ); $VTableChanged_M{$CName} = 1; } - elsif($CompleteSignature{1}{$Func}{"PureVirt"} - and not $CompleteSignature{2}{$Func}{"PureVirt"}) + elsif($CompSign{1}{$Func}{"PureVirt"} + and not $CompSign{2}{$Func}{"PureVirt"}) { # became non-pure virtual - %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Non_Pure"}{$tr_name{$Func}}}=( + %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Non_Pure"}{$Func}}=( "Type_Name"=>$CName, - "Target"=>get_Signature_M($Func, 1) ); + "Target"=>$Func ); $VTableChanged_M{$CName} = 1; } } @@ -11053,11 +2705,11 @@ next if($Interface eq $AddedVFunc); next if($VirtualReplacement{$AddedVFunc}); my $VPos_Added = $VirtualTable{2}{$CName}{$AddedVFunc}; - if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"}) + if($CompSign{2}{$AddedVFunc}{"PureVirt"}) { # pure virtual methods affect all others (virtual and non-virtual) - %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=( + %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$AddedVFunc}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($AddedVFunc, 2) ); + "Target"=>$AddedVFunc ); $VTableChanged_M{$CName} = 1; } elsif(not defined $VirtualTable{1}{$CName} @@ -11065,9 +2717,9 @@ { # added virtual function at the end of v-table if(not keys(%{$VirtualTable_Model{1}{$CName}})) { # became polymorphous class, added v-table pointer - %{$CompatProblems{$Level}{$Interface}{"Added_First_Virtual_Method"}{$tr_name{$AddedVFunc}}}=( + %{$CompatProblems{$Level}{$Interface}{"Added_First_Virtual_Method"}{$AddedVFunc}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($AddedVFunc, 2) ); + "Target"=>$AddedVFunc ); $VTableChanged_M{$CName} = 1; } else @@ -11081,9 +2733,9 @@ if(isLeafClass($Class_Id, 1)) { $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Copying_Class"; } - %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=( + %{$CompatProblems{$Level}{$Interface}{$ProblemType}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($AddedVFunc, 2) ); + "Target"=>$AddedVFunc ); $VTableChanged_M{$CName} = 1; } else @@ -11092,15 +2744,15 @@ if(isLeafClass($Class_Id, 1)) { $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class"; } - %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=( + %{$CompatProblems{$Level}{$Interface}{$ProblemType}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($AddedVFunc, 2) ); + "Target"=>$AddedVFunc ); $VTableChanged_M{$CName} = 1; } } } - elsif($CompleteSignature{1}{$Interface}{"Virt"} - or $CompleteSignature{1}{$Interface}{"PureVirt"}) + elsif($CompSign{1}{$Interface}{"Virt"} + or $CompSign{1}{$Interface}{"PureVirt"}) { if(defined $VirtualTable{1}{$CName} and defined $VirtualTable{2}{$CName}) @@ -11113,17 +2765,16 @@ my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}})); foreach my $ASymbol (@Affected) { - if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"}) + if(not $CompSign{1}{$ASymbol}{"PureVirt"}) { - if(not symbolFilter($ASymbol, 1, "Affected", $Level)) { + if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) { next; } } - $CheckedSymbols{$Level}{$ASymbol} = 1; - %{$CompatProblems{$Level}{$ASymbol}{"Added_Virtual_Method"}{$tr_name{$AddedVFunc}}}=( + %{$CompatProblems{$Level}{$ASymbol}{"Added_Virtual_Method"}{getSignature($AddedVFunc, 2, "Class|Name|Qual")}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($AddedVFunc, 2) ); - $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1; + "Target"=>$AddedVFunc ); + $VTableChanged_M{$TypeInfo{1}{$CompSign{1}{$ASymbol}{"Class"}}{"Name"}} = 1; } } } @@ -11132,26 +2783,29 @@ # safe } } - foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}})) + + foreach my $RemovedVFunc (sort keys(%{$RemovedInt_Virt{$Level}{$CName}})) { next if($VirtualReplacement{$RemovedVFunc}); if($RemovedVFunc eq $Interface - and $CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) + and $CompSign{1}{$RemovedVFunc}{"PureVirt"}) { # This case is for removed virtual methods # implemented in both versions of a library next; } + if(not keys(%{$VirtualTable_Model{2}{$CName}})) { # became non-polymorphous class, removed v-table pointer - %{$CompatProblems{$Level}{$Interface}{"Removed_Last_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=( + %{$CompatProblems{$Level}{$Interface}{"Removed_Last_Virtual_Method"}{getSignature($RemovedVFunc, 1, "Class|Name|Qual")}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($RemovedVFunc, 1) ); + "Target"=>$RemovedVFunc); $VTableChanged_M{$CName} = 1; } - elsif($CompleteSignature{1}{$Interface}{"Virt"} - or $CompleteSignature{1}{$Interface}{"PureVirt"}) + elsif($CompSign{1}{$Interface}{"Virt"} + or $CompSign{1}{$Interface}{"PureVirt"}) { - if(defined $VirtualTable{1}{$CName} and defined $VirtualTable{2}{$CName}) + if(defined $VirtualTable{1}{$CName} + and defined $VirtualTable{2}{$CName}) { if(not defined $VirtualTable{1}{$CName}{$Interface}) { next; @@ -11174,21 +2828,21 @@ my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}})); foreach my $ASymbol (@Affected) { - if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"}) + if(not $CompSign{1}{$ASymbol}{"PureVirt"}) { - if(not symbolFilter($ASymbol, 1, "Affected", $Level)) { + if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) { next; } } my $ProblemType = "Removed_Virtual_Method"; - if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) { + if($CompSign{1}{$RemovedVFunc}{"PureVirt"}) { $ProblemType = "Removed_Pure_Virtual_Method"; } - $CheckedSymbols{$Level}{$ASymbol} = 1; - %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$RemovedVFunc}}}=( + + %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{getSignature($RemovedVFunc, 1, "Class|Name|Qual")}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($RemovedVFunc, 1) ); - $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1; + "Target"=>$RemovedVFunc); + $VTableChanged_M{$TypeInfo{1}{$CompSign{1}{$ASymbol}{"Class"}}{"Name"}} = 1; } } } @@ -11200,47 +2854,51 @@ foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}})) { next if($Interface eq $AddedVFunc); - if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"}) + if($CompSign{2}{$AddedVFunc}{"PureVirt"}) { - %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=( + %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$AddedVFunc}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($AddedVFunc, 2) ); + "Target"=>$AddedVFunc ); } } foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}})) { - if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) + if($CompSign{1}{$RemovedVFunc}{"PureVirt"}) { - %{$CompatProblems{$Level}{$Interface}{"Removed_Pure_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=( + %{$CompatProblems{$Level}{$Interface}{"Removed_Pure_Virtual_Method"}{$RemovedVFunc}}=( "Type_Name"=>$CName, - "Target"=>get_Signature($RemovedVFunc, 1) ); + "Target"=>$RemovedVFunc ); } } } } -sub find_MemberPair_Pos_byName($$) +sub findMemPairByName($$) { - my ($Member_Name, $Pair_Type) = @_; - $Member_Name=~s/\A[_]+|[_]+\Z//g; - foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}})) + my ($Mem, $PairType) = @_; + $Mem=~s/\A[_]+|[_]+\Z//g; + foreach my $Pair (sort {$a<=>$b} keys(%{$PairType->{"Memb"}})) { - if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos}) + if(defined $PairType->{"Memb"}{$Pair}) { - my $Name = $Pair_Type->{"Memb"}{$MemberPair_Pos}{"name"}; - $Name=~s/\A[_]+|[_]+\Z//g; - if($Name eq $Member_Name) { - return $MemberPair_Pos; + my $Name = $PairType->{"Memb"}{$Pair}{"name"}; + + if(index($Name, "_")!=-1) { + $Name=~s/\A[_]+|[_]+\Z//g; + } + + if($Name eq $Mem) { + return $Pair; } } } return "lost"; } -sub find_MemberPair_Pos_byVal($$) +sub findMemPairByVal($$) { my ($Member_Value, $Pair_Type) = @_; - foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}})) + foreach my $MemberPair_Pos (sort {$a<=>$b} keys(%{$Pair_Type->{"Memb"}})) { if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos} and $Pair_Type->{"Memb"}{$MemberPair_Pos}{"value"} eq $Member_Value) { @@ -11250,49 +2908,27 @@ return "lost"; } -sub isRecurType($$$) -{ - foreach (@{$_[2]}) - { - if( $_->{"T1"} eq $_[0] - and $_->{"T2"} eq $_[1] ) - { - return 1; - } - } - return 0; -} - -sub pushType($$$) -{ - my %IDs = ( - "T1" => $_[0], - "T2" => $_[1] - ); - push(@{$_[2]}, \%IDs); -} - sub isRenamed($$$$$) { - my ($MemPos, $Type1, $LVersion1, $Type2, $LVersion2) = @_; + my ($MemPos, $Type1, $V1, $Type2, $V2) = @_; my $Member_Name = $Type1->{"Memb"}{$MemPos}{"name"}; my $MemberType_Id = $Type1->{"Memb"}{$MemPos}{"type"}; - my %MemberType_Pure = get_PureType($MemberType_Id, $TypeInfo{$LVersion1}); + my %MemberType_Pure = getPureType($MemberType_Id, $V1); if(not defined $Type2->{"Memb"}{$MemPos}) { return ""; } my $PairType_Id = $Type2->{"Memb"}{$MemPos}{"type"}; - my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{$LVersion2}); + my %PairType_Pure = getPureType($PairType_Id, $V2); my $Pair_Name = $Type2->{"Memb"}{$MemPos}{"name"}; - my $MemberPair_Pos_Rev = ($Member_Name eq $Pair_Name)?$MemPos:find_MemberPair_Pos_byName($Pair_Name, $Type1); + my $MemberPair_Pos_Rev = ($Member_Name eq $Pair_Name)?$MemPos:findMemPairByName($Pair_Name, $Type1); if($MemberPair_Pos_Rev eq "lost") { if($MemberType_Pure{"Name"} eq $PairType_Pure{"Name"}) { # base type match return $Pair_Name; } - if($TypeInfo{$LVersion1}{$MemberType_Id}{"Name"} eq $TypeInfo{$LVersion2}{$PairType_Id}{"Name"}) + if($TypeInfo{$V1}{$MemberType_Id}{"Name"} eq $TypeInfo{$V2}{$PairType_Id}{"Name"}) { # exact type match return $Pair_Name; } @@ -11312,7 +2948,7 @@ { my ($Pos, $TypeRef) = @_; my $Name = $TypeRef->{"Memb"}{$Pos}{"name"}; - if($Name=~/last|count|max|total/i) + if($Name=~/last|count|max|total|num/i) { # GST_LEVEL_COUNT, GST_RTSP_ELAST return 1; } @@ -11338,15 +2974,17 @@ $N1=~s/\A(struct|union|enum) //; $N2=~s/\A(struct|union|enum) //; - if($N1 ne $N2 + if($N1 ne $N2 and $N2!~/\A\Q$N1\E_v\d+\Z/ and not isAnon($N1) and not isAnon($N2)) { # different names + # NOTE: compare versioned types (type vs type_v30) if($T1->{"Type"} ne "Pointer" or $T2->{"Type"} ne "Pointer") { # compare base types return 1; } + if($N1!~/\Avoid\s*\*/ and $N2=~/\Avoid\s*\*/) { @@ -11375,51 +3013,45 @@ return 0; } -sub isOpaque($) -{ - my $T = $_[0]; - if(not defined $T->{"Memb"}) - { - return 1; - } - return 0; -} - -sub removeVPtr($) -{ # support for old ABI dumps - my $TPtr = $_[0]; - my @Pos = sort {int($a)<=>int($b)} keys(%{$TPtr->{"Memb"}}); - if($#Pos>=1) - { - foreach my $Pos (0 .. $#Pos-1) - { - %{$TPtr->{"Memb"}{$Pos}} = %{$TPtr->{"Memb"}{$Pos+1}}; - } - delete($TPtr->{"Memb"}{$#Pos}); - } -} - sub mergeTypes($$$) { my ($Type1_Id, $Type2_Id, $Level) = @_; return {} if(not $Type1_Id or not $Type2_Id); - if($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id}) + if(defined $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id}) { # already merged return $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id}; } - my %Type1 = get_Type($Type1_Id, 1); - my %Type2 = get_Type($Type2_Id, 2); + my %Type1 = getType($Type1_Id, 1); + my %Type2 = getType($Type2_Id, 2); if(not $Type1{"Name"} or not $Type2{"Name"}) { return {}; } - $CheckedTypes{$Level}{$Type1{"Name"}} = 1; - my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1}); - my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2}); + my %Type1_Pure = getPureType($Type1_Id, 1); + my %Type2_Pure = getPureType($Type2_Id, 2); + + if(defined $UsedDump{1}{"DWARF"}) + { + if($Type1_Pure{"Name"} eq "__unknown__" + or $Type2_Pure{"Name"} eq "__unknown__") + { # Error ABI dump + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {}); + } + } + + if(isPrivateABI($Type1_Id, 1)) { + return {}; + } + + if($Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum|Ptr|Typedef/) { + $CheckedTypes{$Level}{$Type1{"Name"}} = 1; + } - $CheckedTypes{$Level}{$Type1_Pure{"Name"}} = 1; + if($Type1_Pure{"Type"}=~/Intrinsic|Class|Struct|Union|Enum|Ptr|Typedef/) { + $CheckedTypes{$Level}{$Type1_Pure{"Name"}} = 1; + } my %SubProblems = (); @@ -11443,58 +3075,32 @@ } } - if(not $Type1_Pure{"Size"} - or not $Type2_Pure{"Size"}) - { # including a case when "class Class { ... };" changed to "class Class;" - if(not defined $Type1_Pure{"Memb"} or not defined $Type2_Pure{"Memb"} - or index($Type1_Pure{"Name"}, "<")==-1 or index($Type2_Pure{"Name"}, "<")==-1) - { # NOTE: template instances have no size - return {}; + if($Type1_Pure{"Size"} eq "" + or $Type2_Pure{"Size"} eq "") + { # NOTE: size of struct may be 0 bytes + if($Type1_Pure{"Type"}=~/Class|Struct|Union/) + { # including a case when "class Class { ... };" changed to "class Class;" + if(not defined $Type1_Pure{"Memb"} or not defined $Type2_Pure{"Memb"} + or index($Type1_Pure{"Name"}, "<")==-1 or index($Type2_Pure{"Name"}, "<")==-1) + { # NOTE: template instances have no size + return {}; + } } } + if(isRecurType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes)) { # skip recursive declarations return {}; } return {} if(not $Type1_Pure{"Name"} or not $Type2_Pure{"Name"}); - return {} if($SkipTypes{1}{$Type1_Pure{"Name"}}); - return {} if($SkipTypes{1}{$Type1{"Name"}}); + return {} if($In::Desc{1}{"SkipTypes"}{$Type1_Pure{"Name"}}); + return {} if($In::Desc{1}{"SkipTypes"}{$Type1{"Name"}}); - if(not isTargetType($Type1_Pure{"Tid"}, 1)) { + if($Type1_Pure{"Type"}=~/Class|Struct|Union|Enum|Typedef/ + and not isTargetType($Type1_Pure{"Tid"}, 1)) { return {}; } - if($Type1_Pure{"Type"}=~/Class|Struct/ and $Type2_Pure{"Type"}=~/Class|Struct/) - { # support for old ABI dumps - # _vptr field added in 3.0 - if(not checkDump(1, "3.0") and checkDump(2, "3.0")) - { - if(defined $Type2_Pure{"Memb"} - and $Type2_Pure{"Memb"}{0}{"name"} eq "_vptr") - { - if(keys(%{$Type2_Pure{"Memb"}})==1) { - delete($Type2_Pure{"Memb"}{0}); - } - else { - removeVPtr(\%Type2_Pure); - } - } - } - if(checkDump(1, "3.0") and not checkDump(2, "3.0")) - { - if(defined $Type1_Pure{"Memb"} - and $Type1_Pure{"Memb"}{0}{"name"} eq "_vptr") - { - if(keys(%{$Type1_Pure{"Memb"}})==1) { - delete($Type1_Pure{"Memb"}{0}); - } - else { - removeVPtr(\%Type1_Pure); - } - } - } - } - my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef"); my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef"); @@ -11502,15 +3108,15 @@ and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef" and $Typedef_1{"Name"} eq $Typedef_2{"Name"}) { - my %Base_1 = get_OneStep_BaseType($Typedef_1{"Tid"}, $TypeInfo{1}); - my %Base_2 = get_OneStep_BaseType($Typedef_2{"Tid"}, $TypeInfo{2}); + my %Base_1 = getOneStepBaseType($Typedef_1{"Tid"}, 1); + my %Base_2 = getOneStepBaseType($Typedef_2{"Tid"}, 2); if($Base_1{"Name"} ne $Base_2{"Name"}) { - if(differentDumps("G") - or differentDumps("V")) + if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"} + or $In::Opt{"SkipTypedefUncover"}) { # different GCC versions or different dumps - $Base_1{"Name"} = uncover_typedefs($Base_1{"Name"}, 1); - $Base_2{"Name"} = uncover_typedefs($Base_2{"Name"}, 2); + $Base_1{"Name"} = uncoverTypedefs($Base_1{"Name"}, 1); + $Base_2{"Name"} = uncoverTypedefs($Base_2{"Name"}, 2); # std::__va_list and __va_list $Base_1{"Name"}=~s/\A(\w+::)+//; $Base_2{"Name"}=~s/\A(\w+::)+//; @@ -11528,11 +3134,21 @@ %{$SubProblems{"DataType_Size"}{$Typedef_1{"Name"}}}=( "Target"=>$Typedef_1{"Name"}, "Type_Name"=>$Typedef_1{"Name"}, - "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2{"Size"}*$BYTE_SIZE ); + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE ); + } + my %Base1_Pure = getPureType($Base_1{"Tid"}, 1); + my %Base2_Pure = getPureType($Base_2{"Tid"}, 2); + + if(defined $UsedDump{1}{"DWARF"}) + { + if($Base1_Pure{"Name"}=~/\b__unknown__\b/ + or $Base2_Pure{"Name"}=~/\b__unknown__\b/) + { # Error ABI dump + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {}); + } } - my %Base1_Pure = get_PureType($Base_1{"Tid"}, $TypeInfo{1}); - my %Base2_Pure = get_PureType($Base_2{"Tid"}, $TypeInfo{2}); + if(tNameLock($Base_1{"Tid"}, $Base_2{"Tid"})) { if(diffTypes($Base1_Pure{"Tid"}, $Base2_Pure{"Tid"}, $Level)) @@ -11579,7 +3195,9 @@ } return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems); } + pushType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes); + if(($Type1_Pure{"Name"} eq $Type2_Pure{"Name"} or (isAnon($Type1_Pure{"Name"}) and isAnon($Type2_Pure{"Name"}))) and $Type1_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/) @@ -11610,8 +3228,8 @@ %{$SubProblems{$ProblemKind}{$Type1_Pure{"Name"}}}=( "Target"=>$Type1_Pure{"Name"}, "Type_Name"=>$Type1_Pure{"Name"}, - "Old_Size"=>$Type1_Pure{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2_Pure{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1_Pure{"Size"}*$BYTE, + "New_Size"=>$Type2_Pure{"Size"}*$BYTE); } } if(defined $Type1_Pure{"BaseType"} @@ -11628,22 +3246,15 @@ my (%AddedField, %RemovedField, %RenamedField, %RenamedField_Rev, %RelatedField, %RelatedField_Rev) = (); my %NameToPosA = map {$Type1_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type1_Pure{"Memb"}}); my %NameToPosB = map {$Type2_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type2_Pure{"Memb"}}); - foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}})) + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}})) { # detect removed and renamed fields my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"}; next if(not $Member_Name); - my $MemberPair_Pos = (defined $Type2_Pure{"Memb"}{$Member_Pos} and $Type2_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:find_MemberPair_Pos_byName($Member_Name, \%Type2_Pure); + my $MemberPair_Pos = (defined $Type2_Pure{"Memb"}{$Member_Pos} and $Type2_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:findMemPairByName($Member_Name, \%Type2_Pure); if($MemberPair_Pos eq "lost") { if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/) { - if(isUnnamed($Member_Name)) - { # support for old-version dumps - # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format) - if(not checkDump(2, "2.1")) { - next; - } - } if(my $RenamedTo = isRenamed($Member_Pos, \%Type1_Pure, 1, \%Type2_Pure, 2)) { # renamed $RenamedField{$Member_Pos} = $RenamedTo; @@ -11658,11 +3269,11 @@ { my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"}; next if($Member_Value1 eq ""); - $MemberPair_Pos = find_MemberPair_Pos_byVal($Member_Value1, \%Type2_Pure); + $MemberPair_Pos = findMemPairByVal($Member_Value1, \%Type2_Pure); if($MemberPair_Pos ne "lost") { # renamed my $RenamedTo = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"name"}; - my $MemberPair_Pos_Rev = find_MemberPair_Pos_byName($RenamedTo, \%Type1_Pure); + my $MemberPair_Pos_Rev = findMemPairByName($RenamedTo, \%Type1_Pure); if($MemberPair_Pos_Rev eq "lost") { $RenamedField{$Member_Pos} = $RenamedTo; @@ -11684,20 +3295,13 @@ $RelatedField_Rev{$MemberPair_Pos} = $Member_Pos; } } - foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}})) + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}})) { # detect added fields my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"}; next if(not $Member_Name); - my $MemberPair_Pos = (defined $Type1_Pure{"Memb"}{$Member_Pos} and $Type1_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:find_MemberPair_Pos_byName($Member_Name, \%Type1_Pure); + my $MemberPair_Pos = (defined $Type1_Pure{"Memb"}{$Member_Pos} and $Type1_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:findMemPairByName($Member_Name, \%Type1_Pure); if($MemberPair_Pos eq "lost") { - if(isUnnamed($Member_Name)) - { # support for old-version dumps - # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format) - if(not checkDump(1, "2.1")) { - next; - } - } if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union|Enum)\Z/) { if(not $RenamedField_Rev{$Member_Pos}) @@ -11711,7 +3315,7 @@ { # detect moved fields my (%RelPos, %RelPosName, %AbsPos) = (); my $Pos = 0; - foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}})) + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}})) { # relative positions in 1st version my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"}; next if(not $Member_Name); @@ -11723,7 +3327,7 @@ } } $Pos = 0; - foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}})) + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}})) { # relative positions in 2nd version my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"}; next if(not $Member_Name); @@ -11779,7 +3383,7 @@ } } } - foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}})) + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type1_Pure{"Memb"}})) { # check older fields, public and private my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"}; next if(not $Member_Name); @@ -11836,11 +3440,11 @@ $ProblemType = "Removed_Private_Field"; } if($Level eq "Binary" - and not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})) + and not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, 1)) { if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1)) { # affected fields - if(getOffset($MNum-1, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2})) + if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2)) { # changed offset $ProblemType .= "_And_Layout"; } @@ -11896,7 +3500,7 @@ if(isLastElem($Member_Pos, \%Type1_Pure)) { $ProblemType = "Enum_Last_Member_Value"; } - if($SkipConstants{1}{$Member_Name}) { + if($In::Desc{1}{"SkipConstants"}{$Member_Name}) { $ProblemType = "Enum_Private_Member_Value"; } %{$SubProblems{$ProblemType}{$Member_Name}}=( @@ -11929,18 +3533,20 @@ my $MemberType1_Id = $Type1_Pure{"Memb"}{$Member_Pos}{"type"}; my $MemberType2_Id = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"type"}; - my $SizeV1 = $TypeInfo{1}{$MemberType1_Id}{"Size"}*$BYTE_SIZE; + my $SizeV1 = $TypeInfo{1}{$MemberType1_Id}{"Size"}*$BYTE; if(my $BSize1 = $Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}) { $SizeV1 = $BSize1; } - my $SizeV2 = $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE; + my $SizeV2 = $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE; if(my $BSize2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) { $SizeV2 = $BSize2; } + my $MemberType1_Name = $TypeInfo{1}{$MemberType1_Id}{"Name"}; my $MemberType2_Name = $TypeInfo{2}{$MemberType2_Id}{"Name"}; + if($Level eq "Binary" - and $SizeV1 and $SizeV2 + and $SizeV1 ne "" and $SizeV2 ne "" and $SizeV1 ne $SizeV2) { if($MemberType1_Name eq $MemberType2_Name or (isAnon($MemberType1_Name) and isAnon($MemberType2_Name)) @@ -11956,12 +3562,12 @@ # example: "abidata" members in GStreamer types $ProblemType = "Private_".$ProblemType; } - if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})) + if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE, \%Type1_Pure, \%RemovedField, 1)) { # check an effect if($Type2_Pure{"Type"} ne "Union" and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1)) { # public fields after the current - if(getOffset($MNum-1, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2})) + if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2)) { # changed offset $ProblemType .= "_And_Layout"; } @@ -11995,23 +3601,22 @@ { # do NOT check bitfield type changes next; } - if(checkDump(1, "2.13") and checkDump(2, "2.13")) + + if(not $Type1_Pure{"Memb"}{$Member_Pos}{"mutable"} + and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"}) { - if(not $Type1_Pure{"Memb"}{$Member_Pos}{"mutable"} - and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"}) - { - %{$SubProblems{"Field_Became_Mutable"}{$Member_Name}}=( - "Target"=>$Member_Name, - "Type_Name"=>$Type1_Pure{"Name"}); - } - elsif($Type1_Pure{"Memb"}{$Member_Pos}{"mutable"} - and not $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"}) - { - %{$SubProblems{"Field_Became_Non_Mutable"}{$Member_Name}}=( - "Target"=>$Member_Name, - "Type_Name"=>$Type1_Pure{"Name"}); - } + %{$SubProblems{"Field_Became_Mutable"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); } + elsif($Type1_Pure{"Memb"}{$Member_Pos}{"mutable"} + and not $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"}) + { + %{$SubProblems{"Field_Became_Non_Mutable"}{$Member_Name}}=( + "Target"=>$Member_Name, + "Type_Name"=>$Type1_Pure{"Name"}); + } + my %Sub_SubChanges = detectTypeChange($MemberType1_Id, $MemberType2_Id, "Field", $Level); foreach my $ProblemType (keys(%Sub_SubChanges)) { @@ -12023,15 +3628,13 @@ or $ProblemType eq "Field_Type_And_Size" or $ProblemType eq "Field_Type_Format") { - if(checkDump(1, "2.6") and checkDump(2, "2.6")) - { - if(addedQual($Old_Value, $New_Value, "volatile")) { - %{$Sub_SubChanges{"Field_Became_Volatile"}} = %{$Sub_SubChanges{$ProblemType}}; - } - elsif(removedQual($Old_Value, $New_Value, "volatile")) { - %{$Sub_SubChanges{"Field_Became_Non_Volatile"}} = %{$Sub_SubChanges{$ProblemType}}; - } + if(addedQual($Old_Value, $New_Value, "volatile")) { + %{$Sub_SubChanges{"Field_Became_Volatile"}} = %{$Sub_SubChanges{$ProblemType}}; + } + elsif(removedQual($Old_Value, $New_Value, "volatile")) { + %{$Sub_SubChanges{"Field_Became_Non_Volatile"}} = %{$Sub_SubChanges{$ProblemType}}; } + if(my $RA = addedQual($Old_Value, $New_Value, "const")) { if($RA==2) { @@ -12078,12 +3681,12 @@ or isUnnamed($Member_Name)) { $ProblemType = "Private_".$ProblemType; } - if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})) + if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE, \%Type1_Pure, \%RemovedField, 1)) { # check an effect if($Type2_Pure{"Type"} ne "Union" and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1)) { # public fields after the current - if(getOffset($MNum-1, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2})) + if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2)) { # changed offset $ProblemType .= "_And_Layout"; } @@ -12124,11 +3727,11 @@ my %DupProblems = (); - foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems})) + foreach my $Sub_SubProblemType (sort keys(%{$Sub_SubProblems})) { - foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) + foreach my $Sub_SubLocation (sort {length($a)<=>length($b)} sort keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) { - if(not defined $AllAffected) + if(not defined $In::Opt{"AllAffected"}) { if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) { next; @@ -12138,7 +3741,7 @@ my $NewLocation = ($Sub_SubLocation)?$Member_Name."->".$Sub_SubLocation:$Member_Name; $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}; - if(not defined $AllAffected) + if(not defined $In::Opt{"AllAffected"}) { $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1; } @@ -12150,7 +3753,7 @@ } } } - foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}})) + foreach my $Member_Pos (sort {$a<=>$b} keys(%{$Type2_Pure{"Memb"}})) { # checking added members, public and private my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"}; next if(not $Member_Name); @@ -12169,11 +3772,11 @@ $ProblemType = "Added_Private_Field"; } if($Level eq "Binary" - and not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, $TypeInfo{2}, getArch(2), $WORD_SIZE{2})) + and not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, 2)) { if(my $MNum = isAccessible(\%Type2_Pure, \%AddedField, $Member_Pos, -1)) { # public fields after the current - if(getOffset($MNum-1, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2})!=getOffset($RelatedField_Rev{$MNum-1}, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})) + if(getOffset($MNum-1, \%Type2_Pure, 2)!=getOffset($RelatedField_Rev{$MNum-1}, \%Type1_Pure, 1)) { # changed offset $ProblemType .= "_And_Layout"; } @@ -12218,391 +3821,67 @@ } } - pop(@RecurTypes); - return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems); -} - -sub isUnnamed($) { - return $_[0]=~/\Aunnamed\d+\Z/; -} - -sub get_ShortClass($$) -{ - my ($TypeId, $LibVersion) = @_; - my $TypeName = $TypeInfo{$LibVersion}{$TypeId}{"Name"}; - if($TypeInfo{$LibVersion}{$TypeId}{"Type"}!~/Intrinsic|Class|Struct|Union|Enum/) { - $TypeName = uncover_typedefs($TypeName, $LibVersion); - } - if(my $NameSpace = $TypeInfo{$LibVersion}{$TypeId}{"NameSpace"}) { - $TypeName=~s/\A(struct |)\Q$NameSpace\E\:\://g; - } - return $TypeName; -} - -sub goToFirst($$$) -{ - my ($TypeId, $LibVersion, $Type_Type) = @_; - return () if(not $TypeId); - if(defined $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}) { - return %{$Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}}; - } - return () if(not $TypeInfo{$LibVersion}{$TypeId}); - my %Type = %{$TypeInfo{$LibVersion}{$TypeId}}; - return () if(not $Type{"Type"}); - if($Type{"Type"} ne $Type_Type) - { - return () if(not $Type{"BaseType"}); - %Type = goToFirst($Type{"BaseType"}, $LibVersion, $Type_Type); - } - $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type} = \%Type; - return %Type; -} - -my %TypeSpecAttributes = ( - "Const" => 1, - "Volatile" => 1, - "ConstVolatile" => 1, - "Restrict" => 1, - "Typedef" => 1 -); - -sub get_PureType($$) -{ - my ($TypeId, $Info) = @_; - if(not $TypeId or not $Info - or not $Info->{$TypeId}) { - return (); - } - if(defined $Cache{"get_PureType"}{$TypeId}{$Info}) { - return %{$Cache{"get_PureType"}{$TypeId}{$Info}}; - } - my %Type = %{$Info->{$TypeId}}; - return %Type if(not $Type{"BaseType"}); - if($TypeSpecAttributes{$Type{"Type"}}) { - %Type = get_PureType($Type{"BaseType"}, $Info); - } - $Cache{"get_PureType"}{$TypeId}{$Info} = \%Type; - return %Type; -} - -sub get_PLevel($$) -{ - my ($TypeId, $LibVersion) = @_; - return 0 if(not $TypeId); - if(defined $Cache{"get_PLevel"}{$TypeId}{$LibVersion}) { - return $Cache{"get_PLevel"}{$TypeId}{$LibVersion}; - } - return 0 if(not $TypeInfo{$LibVersion}{$TypeId}); - my %Type = %{$TypeInfo{$LibVersion}{$TypeId}}; - return 1 if($Type{"Type"}=~/FuncPtr|FieldPtr/); - my $PLevel = 0; - if($Type{"Type"} =~/Pointer|Ref|FuncPtr|FieldPtr/) { - $PLevel += 1; - } - return $PLevel if(not $Type{"BaseType"}); - $PLevel += get_PLevel($Type{"BaseType"}, $LibVersion); - $Cache{"get_PLevel"}{$TypeId}{$LibVersion} = $PLevel; - return $PLevel; -} - -sub get_BaseType($$) -{ - my ($TypeId, $LibVersion) = @_; - return () if(not $TypeId); - if(defined $Cache{"get_BaseType"}{$TypeId}{$LibVersion}) { - return %{$Cache{"get_BaseType"}{$TypeId}{$LibVersion}}; - } - return () if(not $TypeInfo{$LibVersion}{$TypeId}); - my %Type = %{$TypeInfo{$LibVersion}{$TypeId}}; - return %Type if(not $Type{"BaseType"}); - %Type = get_BaseType($Type{"BaseType"}, $LibVersion); - $Cache{"get_BaseType"}{$TypeId}{$LibVersion} = \%Type; - return %Type; -} - -sub get_BaseTypeQual($$) -{ - my ($TypeId, $LibVersion) = @_; - return "" if(not $TypeId); - return "" if(not $TypeInfo{$LibVersion}{$TypeId}); - my %Type = %{$TypeInfo{$LibVersion}{$TypeId}}; - return "" if(not $Type{"BaseType"}); - my $Qual = ""; - if($Type{"Type"} eq "Pointer") { - $Qual .= "*"; - } - elsif($Type{"Type"} eq "Ref") { - $Qual .= "&"; - } - elsif($Type{"Type"} eq "ConstVolatile") { - $Qual .= "const volatile"; - } - elsif($Type{"Type"} eq "Const" - or $Type{"Type"} eq "Volatile" - or $Type{"Type"} eq "Restrict") { - $Qual .= lc($Type{"Type"}); - } - my $BQual = get_BaseTypeQual($Type{"BaseType"}, $LibVersion); - return $BQual.$Qual; -} - -sub get_OneStep_BaseType($$) -{ - my ($TypeId, $Info) = @_; - if(not $TypeId or not $Info - or not $Info->{$TypeId}) { - return (); - } - my %Type = %{$Info->{$TypeId}}; - return %Type if(not $Type{"BaseType"}); - if(my $BTid = $Type{"BaseType"}) - { - if($Info->{$BTid}) { - return %{$Info->{$BTid}}; - } - else { # something is going wrong - return (); - } - } - else { - return %Type; - } -} - -sub get_Type($$) -{ - my ($TypeId, $LibVersion) = @_; - return () if(not $TypeId); - return () if(not $TypeInfo{$LibVersion}{$TypeId}); - return %{$TypeInfo{$LibVersion}{$TypeId}}; -} - -sub isPrivateData($) -{ # non-public global data - my $Symbol = $_[0]; - return ($Symbol=~/\A(_ZGV|_ZTI|_ZTS|_ZTT|_ZTV|_ZTC|_ZThn|_ZTv0_n)/); -} - -sub isInLineInst($$) { - return (isTemplateInstance(@_) and not isTemplateSpec(@_)); -} - -sub isTemplateInstance($$) -{ - my ($SInfo, $LibVersion) = @_; - - if(my $ClassId = $SInfo->{"Class"}) + if($Type1_Pure{"Type"} eq "FuncPtr") { - if(my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}) + foreach my $PPos (sort {$a<=>$b} keys(%{$Type1_Pure{"Param"}})) { - if(index($ClassName,"<")!=-1) { - return 1; - } - } - } - if(my $ShortName = $SInfo->{"ShortName"}) - { - if(index($ShortName,"<")!=-1 - and index($ShortName,">")!=-1) { - return 1; - } - } - - return 0; -} - -sub isTemplateSpec($$) -{ - my ($SInfo, $LibVersion) = @_; - if(my $ClassId = $SInfo->{"Class"}) - { - if($TypeInfo{$LibVersion}{$ClassId}{"Spec"}) - { # class specialization - return 1; - } - elsif($SInfo->{"Spec"}) - { # method specialization - return 1; - } - } - return 0; -} - -sub symbolFilter($$$$) -{ # some special cases when the symbol cannot be imported - my ($Symbol, $LibVersion, $Type, $Level) = @_; - - if(isPrivateData($Symbol)) - { # non-public global data - return 0; - } - - if(defined $SkipInternalSymbols) - { - return 0 if($Symbol=~/($SkipInternalSymbols)/); - } - - if($Symbol=~/\A_Z/) - { - if($Symbol=~/[CD][3-4]E/) { - return 0; - } - } - - if($CheckHeadersOnly and not checkDump($LibVersion, "2.7")) - { # support for old ABI dumps in --headers-only mode - foreach my $Pos (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}})) - { - if(my $Pid = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"}) - { - my $PType = $TypeInfo{$LibVersion}{$Pid}{"Type"}; - if(not $PType or $PType eq "Unknown") { - return 0; - } - } - } - } - if($Type=~/Affected/) - { - my $Header = $CompleteSignature{$LibVersion}{$Symbol}{"Header"}; - - if($SkipSymbols{$LibVersion}{$Symbol}) - { # user defined symbols to ignore - return 0; - } - - if($SymbolsListPath and not $SymbolsList{$Symbol}) - { # user defined symbols - if(not $TargetHeadersPath or not $Header - or not is_target_header($Header, 1)) - { # -symbols-list | -headers-list - return 0; - } - } - - if($AppPath and not $SymbolsList_App{$Symbol}) - { # user defined symbols (in application) - return 0; - } - - my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}; - - if($ClassId) - { - if(not isTargetType($ClassId, $LibVersion)) { - return 0; - } - } - - my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"}; - if(not $NameSpace and $ClassId) - { # class methods have no "NameSpace" attribute - $NameSpace = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"}; - } - if($NameSpace) - { # user defined namespaces to ignore - if($SkipNameSpaces{$LibVersion}{$NameSpace}) { - return 0; - } - foreach my $NS (keys(%{$SkipNameSpaces{$LibVersion}})) - { # nested namespaces - if($NameSpace=~/\A\Q$NS\E(\:\:|\Z)/) { - return 0; - } - } - } - if($Header) - { - if(my $Skip = skipHeader($Header, $LibVersion)) - { # --skip-headers or (not ) - if($Skip==1) { - return 0; - } + if(not defined $Type2_Pure{"Param"}{$PPos}) { + next; } - } - if($TypesListPath and $ClassId) - { # user defined types - my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}; - if(not $TypesList{$CName}) + my $PT1 = $Type1_Pure{"Param"}{$PPos}{"type"}; + my $PT2 = $Type2_Pure{"Param"}{$PPos}{"type"}; + + my $PName = "p".$PPos; + + my $FP_SubProblems = mergeTypes($PT1, $PT2, $Level); + my %DupProblems = (); + + foreach my $FP_SubProblemType (keys(%{$FP_SubProblems})) { - if(my $NS = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"}) + foreach my $FP_SubLocation (keys(%{$FP_SubProblems->{$FP_SubProblemType}})) { - $CName=~s/\A\Q$NS\E\:\://g; - } - - if(not $TypesList{$CName}) - { - my $Found = 0; - - while($CName=~s/\:\:.+?\Z//) + if(not defined $In::Opt{"AllAffected"}) { - if($TypesList{$CName}) - { - $Found = 1; - last; + if(defined $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}}) { + next; } } - if(not $Found) { - return 0; - } - } - } - } - - if(not selectSymbol($Symbol, $CompleteSignature{$LibVersion}{$Symbol}, $Level, $LibVersion)) - { # non-target symbols - return 0; - } - if($Level eq "Binary") - { - if($CompleteSignature{$LibVersion}{$Symbol}{"InLine"} - or isInLineInst($CompleteSignature{$LibVersion}{$Symbol}, $LibVersion)) - { - if($ClassId and $CompleteSignature{$LibVersion}{$Symbol}{"Virt"}) - { # inline virtual methods - if($Type=~/InlineVirt/) { - return 1; - } - my $Allocable = (not isCopyingClass($ClassId, $LibVersion)); - if(not $Allocable) - { # check bases - foreach my $DCId (get_sub_classes($ClassId, $LibVersion, 1)) - { - if(not isCopyingClass($DCId, $LibVersion)) - { # exists a derived class without default c-tor - $Allocable=1; - last; - } - } - } - if(not $Allocable) { - return 0; + my $NewLocation = ($FP_SubLocation)?$PName."->".$FP_SubLocation:$PName; + $SubProblems{$FP_SubProblemType}{$NewLocation} = $FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}; + + if(not defined $In::Opt{"AllAffected"}) + { + $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}} = 1; } } - else - { # inline non-virtual methods - return 0; - } } + + %DupProblems = (); } } - return 1; + + pop(@RecurTypes); + return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems); +} + +sub isUnnamed($) { + return $_[0]=~/\Aunnamed\d+\Z/; } sub detectAdded($) { my $Level = $_[0]; - foreach my $Symbol (keys(%{$Symbol_Library{2}})) + foreach my $Symbol (keys(%{$In::ABI{2}{"SymLib"}})) { - if(link_symbol($Symbol, 1, "+Deps")) + if(linkSymbol($Symbol, 1, "+Deps")) { # linker can find a new symbol # in the old-version library # So, it's not a new symbol next; } - if(my $VSym = $SymVer{2}{$Symbol} + if(my $VSym = $In::ABI{2}{"SymbolVersion"}{$Symbol} and index($Symbol,"\@")==-1) { next; } @@ -12613,14 +3892,14 @@ sub detectRemoved($) { my $Level = $_[0]; - foreach my $Symbol (keys(%{$Symbol_Library{1}})) + foreach my $Symbol (keys(%{$In::ABI{1}{"SymLib"}})) { - if(link_symbol($Symbol, 2, "+Deps")) + if(linkSymbol($Symbol, 2, "+Deps")) { # linker can find an old symbol # in the new-version library next; } - if(my $VSym = $SymVer{1}{$Symbol} + if(my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol} and index($Symbol,"\@")==-1) { next; } @@ -12628,87 +3907,128 @@ } } +sub checkVtable($$$) +{ + my ($Level, $Symbol, $V) = @_; + + # skip v-tables for templates, that should not be imported by applications + if(my $CName = $VTableClass{$V}{$Symbol}) + { + if(index($CName, "<")!=-1) { + return 0; + } + + if(not keys(%{$ClassMethods{$Level}{$V}{$CName}})) + { # do not show vtables for "private" classes + # use case: vtable for QDragManager (Qt 4.5.3 to 4.6.0) became HIDDEN symbol + return 0; + } + } + + if($In::Desc{$V}{"SkipSymbols"}{$Symbol}) + { # user defined symbols to ignore + return 0; + } + + return 1; +} + sub mergeLibs($) { my $Level = $_[0]; foreach my $Symbol (sort keys(%{$AddedInt{$Level}})) { # checking added symbols - next if($CompleteSignature{2}{$Symbol}{"Private"}); - next if(not $CompleteSignature{2}{$Symbol}{"Header"}); - next if(not symbolFilter($Symbol, 2, "Affected + InlineVirt", $Level)); - %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=(); + next if(not $CompSign{2}{$Symbol}{"Header"} and not $CompSign{2}{$Symbol}{"Source"}); + + if(index($Symbol, "_ZTV")==0) + { + if(not checkVtable($Level, $Symbol, 2)) { + next; + } + } + else { + next if(not symbolFilter($Symbol, $CompSign{2}{$Symbol}, "Affected + InlineVirt", $Level, 2)); + } + + if($CompSign{2}{$Symbol}{"PureVirt"}) + { # symbols for pure virtual methods cannot be called by clients + next; + } + + %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}} = (); } foreach my $Symbol (sort keys(%{$RemovedInt{$Level}})) { # checking removed symbols - next if($CompleteSignature{1}{$Symbol}{"Private"}); - next if(not $CompleteSignature{1}{$Symbol}{"Header"}); + next if(not $CompSign{1}{$Symbol}{"Header"} and not $CompSign{1}{$Symbol}{"Source"}); + if(index($Symbol, "_ZTV")==0) - { # skip v-tables for templates, that should not be imported by applications - next if($tr_name{$Symbol}=~/=0)); } sub detectAdded_H($) { my $Level = $_[0]; - foreach my $Symbol (sort keys(%{$CompleteSignature{2}})) + foreach my $Symbol (sort keys(%{$CompSign{2}})) { if($Level eq "Source") { # remove symbol version - my ($SN, $SS, $SV) = separate_symbol($Symbol); + my ($SN, $SS, $SV) = symbolParts($Symbol); $Symbol=$SN; - if($CompleteSignature{2}{$Symbol}{"Artificial"}) + if($CompSign{2}{$Symbol}{"Artificial"}) { # skip artificial constructors next; } } - if(not $CompleteSignature{2}{$Symbol}{"Header"} - or not $CompleteSignature{2}{$Symbol}{"MnglName"}) { + + if(not $CompSign{2}{$Symbol}{"Header"} + and not $CompSign{2}{$Symbol}{"Source"}) { + next; + } + + if(not $CompSign{2}{$Symbol}{"MnglName"}) { next; } + if($ExtendedSymbols{$Symbol}) { next; } - if(not defined $CompleteSignature{1}{$Symbol} - or not $CompleteSignature{1}{$Symbol}{"MnglName"}) + + if(not $CompSign{2}{$Symbol}{"Virt"} + and not $CompSign{2}{$Symbol}{"PureVirt"} + and $CompSign{2}{$Symbol}{"InLine"}) + { # sunch symbols are not properly detected + next; + } + + if(not defined $CompSign{1}{$Symbol} + or not $CompSign{1}{$Symbol}{"MnglName"}) { if($UsedDump{2}{"SrcBin"}) { - if($UsedDump{1}{"BinOnly"} or not checkDump(1, "2.11")) - { # support for old and different (!) ABI dumps - if(not $CompleteSignature{2}{$Symbol}{"Virt"} - and not $CompleteSignature{2}{$Symbol}{"PureVirt"}) + if($UsedDump{1}{"BinOnly"}) + { # support for different ABI dumps + if(not $CompSign{2}{$Symbol}{"Virt"} + and not $CompSign{2}{$Symbol}{"PureVirt"}) { - if($CheckHeadersOnly) + if($In::Opt{"CheckHeadersOnly"}) { - if(my $Lang = $CompleteSignature{2}{$Symbol}{"Lang"}) + if(my $Lang = $CompSign{2}{$Symbol}{"Lang"}) { if($Lang eq "C") { # support for old ABI dumps: missed extern "C" functions @@ -12718,7 +4038,7 @@ } else { - if(not link_symbol($Symbol, 2, "-Deps")) + if(not linkSymbol($Symbol, 2, "-Deps")) { # skip added inline symbols and const global data next; } @@ -12734,33 +4054,86 @@ sub detectRemoved_H($) { my $Level = $_[0]; - foreach my $Symbol (sort keys(%{$CompleteSignature{1}})) + foreach my $Symbol (sort keys(%{$CompSign{1}})) { + my $ISymbol = $Symbol; + if($Level eq "Source") { # remove symbol version - my ($SN, $SS, $SV) = separate_symbol($Symbol); - $Symbol=$SN; + my ($SN, $SS, $SV) = symbolParts($Symbol); + $Symbol = $SN; } - if(not $CompleteSignature{1}{$Symbol}{"Header"} - or not $CompleteSignature{1}{$Symbol}{"MnglName"}) { + + if(not $CompSign{1}{$Symbol}{"Header"} + and not $CompSign{1}{$Symbol}{"Source"}) { next; } + + if(not $CompSign{1}{$Symbol}{"MnglName"}) { + next; + } + if($ExtendedSymbols{$Symbol}) { next; } - if(not defined $CompleteSignature{2}{$Symbol} - or not $CompleteSignature{2}{$Symbol}{"MnglName"}) - { + + if(not $CompSign{1}{$Symbol}{"Virt"} + and not $CompSign{1}{$Symbol}{"PureVirt"} + and $CompSign{1}{$Symbol}{"InLine"}) + { # sunch symbols are not properly detected + next; + } + + if(not defined $CompSign{2}{$Symbol} + or not $CompSign{2}{$Symbol}{"MnglName"}) + { + if(defined $UsedDump{1}{"DWARF"} + and defined $UsedDump{2}{"DWARF"} + and $Level eq "Source") + { # not present in debug-info, + # but present in dynsym + if(linkSymbol($Symbol, 2, "-Deps")) { + next; + } + + if($ISymbol ne $Symbol) + { + if(linkSymbol($ISymbol, 2, "-Deps")) { + next; + } + } + + if(my $SVer = $In::ABI{2}{"SymbolVersion"}{$Symbol}) + { + if(linkSymbol($SVer, 2, "-Deps")) { + next; + } + } + + if(my $Alias = $CompSign{1}{$ISymbol}{"Alias"}) + { + if(linkSymbol($Alias, 2, "-Deps")) { + next; + } + + if(my $SAVer = $In::ABI{2}{"SymbolVersion"}{$Alias}) + { + if(linkSymbol($SAVer, 2, "-Deps")) { + next; + } + } + } + } if($UsedDump{1}{"SrcBin"}) { - if($UsedDump{2}{"BinOnly"} or not checkDump(2, "2.11")) - { # support for old and different (!) ABI dumps - if(not $CompleteSignature{1}{$Symbol}{"Virt"} - and not $CompleteSignature{1}{$Symbol}{"PureVirt"}) + if($UsedDump{2}{"BinOnly"}) + { # support for different ABI dumps + if(not $CompSign{1}{$Symbol}{"Virt"} + and not $CompSign{1}{$Symbol}{"PureVirt"}) { - if($CheckHeadersOnly) + if($In::Opt{"CheckHeadersOnly"}) { # skip all removed symbols - if(my $Lang = $CompleteSignature{1}{$Symbol}{"Lang"}) + if(my $Lang = $CompSign{1}{$Symbol}{"Lang"}) { if($Lang eq "C") { # support for old ABI dumps: missed extern "C" functions @@ -12770,7 +4143,7 @@ } else { - if(not link_symbol($Symbol, 1, "-Deps")) + if(not linkSymbol($Symbol, 1, "-Deps")) { # skip removed inline symbols next; } @@ -12778,16 +4151,10 @@ } } } - if(not checkDump(1, "2.15")) - { - if($Symbol=~/_IT_E\Z/) - { # _ZN28QExplicitlySharedDataPointerI22QSslCertificatePrivateEC1IT_EERKS_IT_E - next; - } - } - if(not $CompleteSignature{1}{$Symbol}{"Class"}) + + if(not $CompSign{1}{$Symbol}{"Class"}) { - if(my $Short = $CompleteSignature{1}{$Symbol}{"ShortName"}) + if(my $Short = $CompSign{1}{$Symbol}{"ShortName"}) { if(defined $Constants{2}{$Short}) { @@ -12814,14 +4181,13 @@ my $Level = $_[0]; foreach my $Symbol (sort keys(%{$AddedInt{$Level}})) { # checking added symbols - next if($CompleteSignature{2}{$Symbol}{"PureVirt"}); - next if($CompleteSignature{2}{$Symbol}{"Private"}); - next if(not symbolFilter($Symbol, 2, "Affected", $Level)); + next if($CompSign{2}{$Symbol}{"PureVirt"}); + next if(not symbolFilter($Symbol, $CompSign{2}{$Symbol}, "Affected", $Level, 2)); if($Level eq "Binary") { - if($CompleteSignature{2}{$Symbol}{"InLine"}) + if($CompSign{2}{$Symbol}{"InLine"}) { - if(not $CompleteSignature{2}{$Symbol}{"Virt"}) + if(not $CompSign{2}{$Symbol}{"Virt"}) { # skip inline non-virtual functions next; } @@ -12837,14 +4203,13 @@ } foreach my $Symbol (sort keys(%{$RemovedInt{$Level}})) { # checking removed symbols - next if($CompleteSignature{1}{$Symbol}{"PureVirt"}); - next if($CompleteSignature{1}{$Symbol}{"Private"}); - next if(not symbolFilter($Symbol, 1, "Affected", $Level)); + next if($CompSign{1}{$Symbol}{"PureVirt"}); + next if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)); if($Level eq "Binary") { - if($CompleteSignature{1}{$Symbol}{"InLine"}) + if($CompSign{1}{$Symbol}{"InLine"}) { - if(not $CompleteSignature{1}{$Symbol}{"Virt"}) + if(not $CompSign{1}{$Symbol}{"Virt"}) { # skip inline non-virtual functions next; } @@ -12854,13 +4219,13 @@ { # Source if(my $Alt = $SourceAlternative{$Symbol}) { - if(defined $CompleteSignature{1}{$Alt} - and $CompleteSignature{1}{$Symbol}{"Const"}) + if(defined $CompSign{1}{$Alt} + and $CompSign{1}{$Symbol}{"Const"}) { - my $Cid = $CompleteSignature{1}{$Symbol}{"Class"}; + my $Cid = $CompSign{1}{$Symbol}{"Class"}; %{$CompatProblems{$Level}{$Symbol}{"Removed_Const_Overload"}{"this"}}=( "Type_Name"=>$TypeInfo{1}{$Cid}{"Name"}, - "Target"=>get_Signature($Alt, 1)); + "Target"=>$Alt); } else { # do NOT show removed symbol @@ -12875,25 +4240,32 @@ sub addParamNames($) { my $LibraryVersion = $_[0]; - return if(not keys(%AddIntParams)); + + if(not keys(%AddSymbolParams)) { + return; + } + my $SecondVersion = $LibraryVersion==1?2:1; - foreach my $Interface (sort keys(%{$CompleteSignature{$LibraryVersion}})) + foreach my $Interface (sort keys(%{$CompSign{$LibraryVersion}})) { - next if(not keys(%{$AddIntParams{$Interface}})); - foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibraryVersion}{$Interface}{"Param"}})) + if(not keys(%{$AddSymbolParams{$Interface}})) { + next; + } + + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LibraryVersion}{$Interface}{"Param"}})) { # add absent parameter names - my $ParamName = $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"}; - if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddIntParams{$Interface}{$ParamPos}) + my $ParamName = $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"}; + if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddSymbolParams{$Interface}{$ParamPos}) { # names from the external file - if(defined $CompleteSignature{$SecondVersion}{$Interface} - and defined $CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos}) + if(defined $CompSign{$SecondVersion}{$Interface} + and defined $CompSign{$SecondVersion}{$Interface}{"Param"}{$ParamPos}) { - if($CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) { - $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName; + if($CompSign{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) { + $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName; } } else { - $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName; + $CompSign{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName; } } } @@ -12903,43 +4275,38 @@ sub detectChangedTypedefs() { # detect changed typedefs to show # correct function signatures - foreach my $Typedef (keys(%{$Typedef_BaseName{1}})) + foreach my $Typedef (keys(%{$In::ABI{1}{"TypedefBase"}})) { - next if(not $Typedef); - my $BName1 = $Typedef_BaseName{1}{$Typedef}; + if(not $Typedef) { + next; + } + + my $BName1 = $In::ABI{1}{"TypedefBase"}{$Typedef}; if(not $BName1 or isAnon($BName1)) { next; } - my $BName2 = $Typedef_BaseName{2}{$Typedef}; + + my $BName2 = $In::ABI{2}{"TypedefBase"}{$Typedef}; if(not $BName2 or isAnon($BName2)) { next; } + + $BName1 = uncoverTypedefs($BName1, 1); + $BName2 = uncoverTypedefs($BName2, 2); + if($BName1 ne $BName2) { $ChangedTypedef{$Typedef} = 1; } } } -sub get_symbol_suffix($$) -{ - my ($Symbol, $Full) = @_; - my ($SN, $SO, $SV) = separate_symbol($Symbol); - $Symbol=$SN; # remove version - my $Signature = $tr_name{$Symbol}; - my $Suffix = substr($Signature, find_center($Signature, "(")); - if(not $Full) { - $Suffix=~s/(\))\s*(const volatile|volatile const|const|volatile)\Z/$1/g; - } - return $Suffix; -} - -sub get_symbol_prefix($$) +sub symbolPrefix($$) { - my ($Symbol, $LibVersion) = @_; - my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"}; - if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) + my ($Symbol, $LVer) = @_; + my $ShortName = $CompSign{$LVer}{$Symbol}{"ShortName"}; + if(my $ClassId = $CompSign{$LVer}{$Symbol}{"Class"}) { # methods - $ShortName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}."::".$ShortName; + $ShortName = $TypeInfo{$LVer}{$ClassId}{"Name"}."::".$ShortName; } return $ShortName; } @@ -12948,24 +4315,24 @@ { my $Symbol = $_[0]; my $PSymbol = $Symbol; - if(not defined $CompleteSignature{2}{$PSymbol} - or (not $CompleteSignature{2}{$PSymbol}{"MnglName"} - and not $CompleteSignature{2}{$PSymbol}{"ShortName"})) + if(not defined $CompSign{2}{$PSymbol} + or (not $CompSign{2}{$PSymbol}{"MnglName"} + and not $CompSign{2}{$PSymbol}{"ShortName"})) { # search for a pair - if(my $ShortName = $CompleteSignature{1}{$PSymbol}{"ShortName"}) + if(my $ShortName = $CompSign{1}{$PSymbol}{"ShortName"}) { - if($CompleteSignature{1}{$PSymbol}{"Data"}) + if($CompSign{1}{$PSymbol}{"Data"}) { if($PSymbol=~s/L(\d+$ShortName(E)\Z)/$1/ or $PSymbol=~s/(\d+$ShortName(E)\Z)/L$1/) { - if(defined $CompleteSignature{2}{$PSymbol} - and $CompleteSignature{2}{$PSymbol}{"MnglName"}) + if(defined $CompSign{2}{$PSymbol} + and $CompSign{2}{$PSymbol}{"MnglName"}) { $SourceAlternative{$Symbol} = $PSymbol; $SourceAlternative_B{$PSymbol} = $Symbol; - if(not defined $CompleteSignature{1}{$PSymbol} - or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) { + if(not defined $CompSign{1}{$PSymbol} + or not $CompSign{1}{$PSymbol}{"MnglName"}) { $SourceReplacement{$Symbol} = $PSymbol; } } @@ -12978,13 +4345,13 @@ if($PSymbol=~s/\A_ZN$Sp/_ZN/ or $PSymbol=~s/\A_ZN/_ZN$Sp/) { - if(defined $CompleteSignature{2}{$PSymbol} - and $CompleteSignature{2}{$PSymbol}{"MnglName"}) + if(defined $CompSign{2}{$PSymbol} + and $CompSign{2}{$PSymbol}{"MnglName"}) { $SourceAlternative{$Symbol} = $PSymbol; $SourceAlternative_B{$PSymbol} = $Symbol; - if(not defined $CompleteSignature{1}{$PSymbol} - or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) { + if(not defined $CompSign{1}{$PSymbol} + or not $CompSign{1}{$PSymbol}{"MnglName"}) { $SourceReplacement{$Symbol} = $PSymbol; } } @@ -12999,18 +4366,46 @@ sub getSymKind($$) { - my ($Symbol, $LibVersion) = @_; - if($CompleteSignature{$LibVersion}{$Symbol}{"Data"}) + my ($Symbol, $LVer) = @_; + if($CompSign{$LVer}{$Symbol}{"Data"}) { return "Global_Data"; } - elsif($CompleteSignature{$LibVersion}{$Symbol}{"Class"}) + elsif($CompSign{$LVer}{$Symbol}{"Class"}) { return "Method"; } return "Function"; } +sub isConstData($$) +{ + my ($Symbol, $LVer) = @_; + + my $Return = $CompSign{$LVer}{$Symbol}{"Return"}; + my $RTName = uncoverTypedefs($TypeInfo{$LVer}{$Return}{"Name"}, $LVer); + + return ($RTName=~/\bconst\Z/); +} + +sub getConstDataSym($$) +{ + my ($Symbol, $LVer) = @_; + + my $Short = $CompSign{$LVer}{$Symbol}{"ShortName"}; + $Symbol=~s/(\d+$Short)/L$1/; + return $Symbol; +} + +sub getNonConstDataSym($$) +{ + my ($Symbol, $LVer) = @_; + + my $Short = $CompSign{$LVer}{$Symbol}{"ShortName"}; + $Symbol=~s/L(\d+$Short)/$1/; + return $Symbol; +} + sub mergeSymbols($) { my $Level = $_[0]; @@ -13021,37 +4416,38 @@ my %AddedOverloads = (); foreach my $Symbol (sort keys(%{$AddedInt{$Level}})) { # check all added exported symbols - if(not $CompleteSignature{2}{$Symbol}{"Header"}) { + if(not $CompSign{2}{$Symbol}{"Header"} + and not $CompSign{2}{$Symbol}{"Source"}) { next; } - if(defined $CompleteSignature{1}{$Symbol} - and $CompleteSignature{1}{$Symbol}{"Header"}) + if(defined $CompSign{1}{$Symbol} + and ($CompSign{1}{$Symbol}{"Header"} or $CompSign{1}{$Symbol}{"Source"})) { # double-check added symbol next; } - if(not symbolFilter($Symbol, 2, "Affected", $Level)) { - next; - } if($Symbol=~/\A(_Z|\?)/) { # C++ - $AddedOverloads{get_symbol_prefix($Symbol, 2)}{get_symbol_suffix($Symbol, 1)} = $Symbol; + $AddedOverloads{symbolPrefix($Symbol, 2)}{getSignature($Symbol, 2, "Qual")} = $Symbol; } - if(my $OverriddenMethod = $CompleteSignature{2}{$Symbol}{"Override"}) + if(my $OverriddenMethod = $CompSign{2}{$Symbol}{"Override"}) { # register virtual overridings - my $Cid = $CompleteSignature{2}{$Symbol}{"Class"}; + my $Cid = $CompSign{2}{$Symbol}{"Class"}; my $AffectedClass_Name = $TypeInfo{2}{$Cid}{"Name"}; - if(defined $CompleteSignature{1}{$OverriddenMethod} and $CompleteSignature{1}{$OverriddenMethod}{"Virt"} - and not $CompleteSignature{1}{$OverriddenMethod}{"Private"}) + if(defined $CompSign{1}{$OverriddenMethod} and $CompSign{1}{$OverriddenMethod}{"Virt"} + and not $CompSign{1}{$OverriddenMethod}{"Private"}) { if($TName_Tid{1}{$AffectedClass_Name}) { # class should exist in previous version if(not isCopyingClass($TName_Tid{1}{$AffectedClass_Name}, 1)) { # old v-table is NOT copied by old applications - %{$CompatProblems{$Level}{$OverriddenMethod}{"Overridden_Virtual_Method"}{$tr_name{$Symbol}}}=( - "Type_Name"=>$AffectedClass_Name, - "Target"=>get_Signature($Symbol, 2), - "Old_Value"=>get_Signature($OverriddenMethod, 2), - "New_Value"=>get_Signature($Symbol, 2)); + if(symbolFilter($OverriddenMethod, $CompSign{1}{$OverriddenMethod}, "Affected", $Level, 1)) + { + %{$CompatProblems{$Level}{$OverriddenMethod}{"Overridden_Virtual_Method"}{$Symbol}}=( + "Type_Name"=>$AffectedClass_Name, + "Target"=>$Symbol, + "Old_Value"=>$OverriddenMethod, + "New_Value"=>$Symbol); + } } } } @@ -13059,95 +4455,96 @@ } foreach my $Symbol (sort keys(%{$RemovedInt{$Level}})) { # check all removed exported symbols - if(not $CompleteSignature{1}{$Symbol}{"Header"}) { + if(not $CompSign{1}{$Symbol}{"Header"} + and not $CompSign{1}{$Symbol}{"Source"}) { next; } - if(defined $CompleteSignature{2}{$Symbol} - and $CompleteSignature{2}{$Symbol}{"Header"}) + if(defined $CompSign{2}{$Symbol} + and ($CompSign{2}{$Symbol}{"Header"} or $CompSign{2}{$Symbol}{"Source"})) { # double-check removed symbol next; } - if($CompleteSignature{1}{$Symbol}{"Private"}) - { # skip private methods - next; - } - if(not symbolFilter($Symbol, 1, "Affected", $Level)) { + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected", $Level, 1)) { next; } + $CheckedSymbols{$Level}{$Symbol} = 1; - if(my $OverriddenMethod = $CompleteSignature{1}{$Symbol}{"Override"}) + + if(my $OverriddenMethod = $CompSign{1}{$Symbol}{"Override"}) { # register virtual overridings - my $Cid = $CompleteSignature{1}{$Symbol}{"Class"}; + my $Cid = $CompSign{1}{$Symbol}{"Class"}; my $AffectedClass_Name = $TypeInfo{1}{$Cid}{"Name"}; - if(defined $CompleteSignature{2}{$OverriddenMethod} - and $CompleteSignature{2}{$OverriddenMethod}{"Virt"}) + if(defined $CompSign{2}{$OverriddenMethod} + and $CompSign{2}{$OverriddenMethod}{"Virt"}) { if($TName_Tid{2}{$AffectedClass_Name}) { # class should exist in newer version - if(not isCopyingClass($CompleteSignature{1}{$Symbol}{"Class"}, 1)) + if(not isCopyingClass($CompSign{1}{$Symbol}{"Class"}, 1)) { # old v-table is NOT copied by old applications - %{$CompatProblems{$Level}{$Symbol}{"Overridden_Virtual_Method_B"}{$tr_name{$OverriddenMethod}}}=( + %{$CompatProblems{$Level}{$Symbol}{"Overridden_Virtual_Method_B"}{$OverriddenMethod}}=( "Type_Name"=>$AffectedClass_Name, - "Target"=>get_Signature($OverriddenMethod, 1), - "Old_Value"=>get_Signature($Symbol, 1), - "New_Value"=>get_Signature($OverriddenMethod, 1)); + "Target"=>$OverriddenMethod, + "Old_Value"=>$Symbol, + "New_Value"=>$OverriddenMethod); } } } } + if($Level eq "Binary" - and $OStarget eq "windows") + and $In::Opt{"Target"} eq "windows") { # register the reason of symbol name change - if(my $NewSym = $mangled_name{2}{$tr_name{$Symbol}}) + if(defined $CompSign{1}{$Symbol}{"Unmangled"} + and my $NewSym = getMangled_MSVC($CompSign{1}{$Symbol}{"Unmangled"}, 2)) { if($AddedInt{$Level}{$NewSym}) { - if($CompleteSignature{1}{$Symbol}{"Static"} ne $CompleteSignature{2}{$NewSym}{"Static"}) + if($CompSign{1}{$Symbol}{"Static"} ne $CompSign{2}{$NewSym}{"Static"}) { - if($CompleteSignature{2}{$NewSym}{"Static"}) + if($CompSign{2}{$NewSym}{"Static"}) { - %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Static"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Static"}{$Symbol}}=( + "Target"=>$Symbol, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } else { - %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Static"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Static"}{$Symbol}}=( + "Target"=>$Symbol, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } } - if($CompleteSignature{1}{$Symbol}{"Virt"} ne $CompleteSignature{2}{$NewSym}{"Virt"}) + if($CompSign{1}{$Symbol}{"Virt"} ne $CompSign{2}{$NewSym}{"Virt"}) { - if($CompleteSignature{2}{$NewSym}{"Virt"}) + if($CompSign{2}{$NewSym}{"Virt"}) { - %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Virtual"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Virtual"}{$Symbol}}=( + "Target"=>$Symbol, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } else { - %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Virtual"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Virtual"}{$Symbol}}=( + "Target"=>$Symbol, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } } - my $RTId1 = $CompleteSignature{1}{$Symbol}{"Return"}; - my $RTId2 = $CompleteSignature{2}{$NewSym}{"Return"}; + my $RTId1 = $CompSign{1}{$Symbol}{"Return"}; + my $RTId2 = $CompSign{2}{$NewSym}{"Return"}; my $RTName1 = $TypeInfo{1}{$RTId1}{"Name"}; my $RTName2 = $TypeInfo{2}{$RTId2}{"Name"}; if($RTName1 ne $RTName2) { my $ProblemType = "Symbol_Changed_Return"; - if($CompleteSignature{1}{$Symbol}{"Data"}) { + if($CompSign{1}{$Symbol}{"Data"}) { $ProblemType = "Global_Data_Symbol_Changed_Type"; } - %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, + %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{$Symbol}}=( + "Target"=>$Symbol, "Old_Type"=>$RTName1, "New_Type"=>$RTName2, "Old_Value"=>$Symbol, @@ -13156,14 +4553,15 @@ } } } + if($Symbol=~/\A(_Z|\?)/) { # C++ - my $Prefix = get_symbol_prefix($Symbol, 1); + my $Prefix = symbolPrefix($Symbol, 1); if(my @Overloads = sort keys(%{$AddedOverloads{$Prefix}}) - and not $AddedOverloads{$Prefix}{get_symbol_suffix($Symbol, 1)}) + and not $AddedOverloads{$Prefix}{getSignature($Symbol, 1, "Qual")}) { # changed signature: params, "const"-qualifier my $NewSym = $AddedOverloads{$Prefix}{$Overloads[0]}; - if($CompleteSignature{1}{$Symbol}{"Constructor"}) + if($CompSign{1}{$Symbol}{"Constructor"}) { if($Symbol=~/(C[1-2][EI])/) { @@ -13171,7 +4569,7 @@ $NewSym=~s/(C[1-2][EI])/$CtorType/g; } } - elsif($CompleteSignature{1}{$Symbol}{"Destructor"}) + elsif($CompSign{1}{$Symbol}{"Destructor"}) { if($Symbol=~/(D[0-2][EI])/) { @@ -13179,53 +4577,53 @@ $NewSym=~s/(D[0-2][EI])/$DtorType/g; } } - my $NS1 = $CompleteSignature{1}{$Symbol}{"NameSpace"}; - my $NS2 = $CompleteSignature{2}{$NewSym}{"NameSpace"}; + my $NS1 = $CompSign{1}{$Symbol}{"NameSpace"}; + my $NS2 = $CompSign{2}{$NewSym}{"NameSpace"}; if((not $NS1 and not $NS2) or ($NS1 and $NS2 and $NS1 eq $NS2)) { # from the same class and namespace - if($CompleteSignature{1}{$Symbol}{"Const"} - and not $CompleteSignature{2}{$NewSym}{"Const"}) + if($CompSign{1}{$Symbol}{"Const"} + and not $CompSign{2}{$NewSym}{"Const"}) { # "const" to non-"const" - %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Const"}{$tr_name{$Symbol}}}=( - "Type_Name"=>$TypeInfo{1}{$CompleteSignature{1}{$Symbol}{"Class"}}{"Name"}, - "Target"=>$tr_name{$Symbol}, - "New_Signature"=>get_Signature($NewSym, 2), + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Const"}{$Symbol}}=( + "Type_Name"=>$TypeInfo{1}{$CompSign{1}{$Symbol}{"Class"}}{"Name"}, + "Target"=>$Symbol, + "New_Signature"=>$NewSym, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } - elsif(not $CompleteSignature{1}{$Symbol}{"Const"} - and $CompleteSignature{2}{$NewSym}{"Const"}) + elsif(not $CompSign{1}{$Symbol}{"Const"} + and $CompSign{2}{$NewSym}{"Const"}) { # non-"const" to "const" - %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Const"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, - "New_Signature"=>get_Signature($NewSym, 2), + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Const"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } - if($CompleteSignature{1}{$Symbol}{"Volatile"} - and not $CompleteSignature{2}{$NewSym}{"Volatile"}) + if($CompSign{1}{$Symbol}{"Volatile"} + and not $CompSign{2}{$NewSym}{"Volatile"}) { # "volatile" to non-"volatile" - %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Volatile"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, - "New_Signature"=>get_Signature($NewSym, 2), + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Volatile"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } - elsif(not $CompleteSignature{1}{$Symbol}{"Volatile"} - and $CompleteSignature{2}{$NewSym}{"Volatile"}) + elsif(not $CompSign{1}{$Symbol}{"Volatile"} + and $CompSign{2}{$NewSym}{"Volatile"}) { # non-"volatile" to "volatile" - %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Volatile"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, - "New_Signature"=>get_Signature($NewSym, 2), + %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Volatile"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } - if(get_symbol_suffix($Symbol, 0) ne get_symbol_suffix($NewSym, 0)) + if(getSignature($Symbol, 1, "Param") ne getSignature($NewSym, 2, "Param")) { # params list - %{$CompatProblems{$Level}{$Symbol}{"Symbol_Changed_Parameters"}{$tr_name{$Symbol}}}=( - "Target"=>$tr_name{$Symbol}, - "New_Signature"=>get_Signature($NewSym, 2), + %{$CompatProblems{$Level}{$Symbol}{"Symbol_Changed_Parameters"}{$Symbol}}=( + "Target"=>$Symbol, + "New_Signature"=>$NewSym, "Old_Value"=>$Symbol, "New_Value"=>$NewSym ); } @@ -13233,26 +4631,25 @@ } } } - foreach my $Symbol (sort keys(%{$CompleteSignature{1}})) + + foreach my $Symbol (sort keys(%{$CompSign{1}})) { # checking symbols - $CurrentSymbol = $Symbol; - - my ($SN, $SS, $SV) = separate_symbol($Symbol); + my ($SN, $SS, $SV) = symbolParts($Symbol); if($Level eq "Source") { # remove symbol version - $Symbol=$SN; + $Symbol = $SN; } else { # Binary if(not $SV) { # symbol without version - if(my $VSym = $SymVer{1}{$Symbol}) + if(my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol}) { # the symbol is linked with versioned symbol - if($CompleteSignature{2}{$VSym}{"MnglName"}) + if($CompSign{2}{$VSym}{"MnglName"}) { # show report for symbol@ver only next; } - elsif(not link_symbol($VSym, 2, "-Deps")) + elsif(not linkSymbol($VSym, 2, "-Deps")) { # changed version: sym@v1 to sym@v2 # do NOT show report for symbol next; @@ -13266,138 +4663,155 @@ { # take a source-compatible replacement function $PSymbol = $S; } - if($CompleteSignature{1}{$Symbol}{"Private"}) + if($CompSign{1}{$Symbol}{"Data"} + and not defined $CompSign{2}{$Symbol}) + { + if(isConstData($Symbol, 1)) + { + if(my $NonConstSymbol = getNonConstDataSym($Symbol, 1)) + { + if($CompSign{2}{$NonConstSymbol}) { + $PSymbol = $NonConstSymbol; + } + } + } + else + { + if(my $ConstSymbol = getConstDataSym($Symbol, 1)) + { + if($CompSign{2}{$ConstSymbol}) { + $PSymbol = $ConstSymbol; + } + } + } + } + + if($CompSign{1}{$Symbol}{"Private"}) { # private symbols next; } - if(not defined $CompleteSignature{1}{$Symbol} - or not defined $CompleteSignature{2}{$PSymbol}) + if(not defined $CompSign{1}{$Symbol} + or not defined $CompSign{2}{$PSymbol}) { # no info next; } - if(not $CompleteSignature{1}{$Symbol}{"MnglName"} - or not $CompleteSignature{2}{$PSymbol}{"MnglName"}) + if(not $CompSign{1}{$Symbol}{"MnglName"} + or not $CompSign{2}{$PSymbol}{"MnglName"}) { # no mangled name next; } - if(not $CompleteSignature{1}{$Symbol}{"Header"} - or not $CompleteSignature{2}{$PSymbol}{"Header"}) - { # without a header + if((not $CompSign{1}{$Symbol}{"Header"} and not $CompSign{1}{$Symbol}{"Source"}) + or (not $CompSign{2}{$PSymbol}{"Header"} and not $CompSign{2}{$PSymbol}{"Source"})) + { # without a header or source next; } - if(not $CompleteSignature{1}{$Symbol}{"PureVirt"} - and $CompleteSignature{2}{$PSymbol}{"PureVirt"}) + if(not $CompSign{1}{$Symbol}{"PureVirt"} + and $CompSign{2}{$PSymbol}{"PureVirt"}) { # became pure next; } - if($CompleteSignature{1}{$Symbol}{"PureVirt"} - and not $CompleteSignature{2}{$PSymbol}{"PureVirt"}) + if($CompSign{1}{$Symbol}{"PureVirt"} + and not $CompSign{2}{$PSymbol}{"PureVirt"}) { # became non-pure next; } - if(not symbolFilter($Symbol, 1, "Affected + InlineVirt", $Level)) - { # exported, target, inline virtual and pure virtual - next; - } - if(not symbolFilter($PSymbol, 2, "Affected + InlineVirt", $Level)) + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", $Level, 1)) { # exported, target, inline virtual and pure virtual next; } - if(checkDump(1, "2.13") and checkDump(2, "2.13")) + if($CompSign{1}{$Symbol}{"Data"} + and $CompSign{2}{$PSymbol}{"Data"}) { - if($CompleteSignature{1}{$Symbol}{"Data"} - and $CompleteSignature{2}{$PSymbol}{"Data"}) + my $Value1 = $CompSign{1}{$Symbol}{"Value"}; + my $Value2 = $CompSign{2}{$PSymbol}{"Value"}; + if(defined $Value1) { - my $Value1 = $CompleteSignature{1}{$Symbol}{"Value"}; - my $Value2 = $CompleteSignature{2}{$PSymbol}{"Value"}; - if(defined $Value1) + $Value1 = showVal($Value1, $CompSign{1}{$Symbol}{"Return"}, 1); + if(defined $Value2) { - $Value1 = showVal($Value1, $CompleteSignature{1}{$Symbol}{"Return"}, 1); - if(defined $Value2) + $Value2 = showVal($Value2, $CompSign{2}{$PSymbol}{"Return"}, 2); + if($Value1 ne $Value2) { - $Value2 = showVal($Value2, $CompleteSignature{2}{$PSymbol}{"Return"}, 2); - if($Value1 ne $Value2) - { - %{$CompatProblems{$Level}{$Symbol}{"Global_Data_Value_Changed"}{""}}=( - "Old_Value"=>$Value1, - "New_Value"=>$Value2, - "Target"=>get_Signature($Symbol, 1) ); - } + %{$CompatProblems{$Level}{$Symbol}{"Global_Data_Value_Changed"}{""}}=( + "Old_Value"=>$Value1, + "New_Value"=>$Value2, + "Target"=>$Symbol ); } } } } - if($CompleteSignature{2}{$PSymbol}{"Private"}) + if($CompSign{2}{$PSymbol}{"Private"}) { %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Private"}{""}}=( - "Target"=>get_Signature_M($PSymbol, 2) ); + "Target"=>$PSymbol ); } - elsif(not $CompleteSignature{1}{$Symbol}{"Protected"} - and $CompleteSignature{2}{$PSymbol}{"Protected"}) + elsif(not $CompSign{1}{$Symbol}{"Protected"} + and $CompSign{2}{$PSymbol}{"Protected"}) { %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Protected"}{""}}=( - "Target"=>get_Signature_M($PSymbol, 2) ); + "Target"=>$PSymbol ); } - elsif($CompleteSignature{1}{$Symbol}{"Protected"} - and not $CompleteSignature{2}{$PSymbol}{"Protected"}) + elsif($CompSign{1}{$Symbol}{"Protected"} + and not $CompSign{2}{$PSymbol}{"Protected"}) { %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Public"}{""}}=( - "Target"=>get_Signature_M($PSymbol, 2) ); + "Target"=>$PSymbol ); } # checking virtual table mergeVirtualTables($Symbol, $Level); - if($COMPILE_ERRORS) + if($In::Opt{"CompileError"}) { # if some errors occurred at the compiling stage # then some false positives can be skipped here - if(not $CompleteSignature{1}{$Symbol}{"Data"} and $CompleteSignature{2}{$PSymbol}{"Data"} + if(not $CompSign{1}{$Symbol}{"Data"} and $CompSign{2}{$PSymbol}{"Data"} and not $GlobalDataObject{2}{$Symbol}) { # missed information about parameters in newer version next; } - if($CompleteSignature{1}{$Symbol}{"Data"} and not $GlobalDataObject{1}{$Symbol} - and not $CompleteSignature{2}{$PSymbol}{"Data"}) + if($CompSign{1}{$Symbol}{"Data"} and not $GlobalDataObject{1}{$Symbol} + and not $CompSign{2}{$PSymbol}{"Data"}) { # missed information about parameters in older version next; } } - my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol); - # checking attributes - if($CompleteSignature{2}{$PSymbol}{"Static"} - and not $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/) + my ($MnglName, $VersionSpec, $SymbolVersion) = symbolParts($Symbol); + + # check attributes + if($CompSign{2}{$PSymbol}{"Static"} + and not $CompSign{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/) { %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Static"}{""}}=( - "Target"=>get_Signature($Symbol, 1) + "Target"=>$Symbol ); } - elsif(not $CompleteSignature{2}{$PSymbol}{"Static"} - and $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/) + elsif(not $CompSign{2}{$PSymbol}{"Static"} + and $CompSign{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/) { %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Static"}{""}}=( - "Target"=>get_Signature($Symbol, 1) + "Target"=>$Symbol ); } - if(($CompleteSignature{1}{$Symbol}{"Virt"} and $CompleteSignature{2}{$PSymbol}{"Virt"}) - or ($CompleteSignature{1}{$Symbol}{"PureVirt"} and $CompleteSignature{2}{$PSymbol}{"PureVirt"})) + if(($CompSign{1}{$Symbol}{"Virt"} and $CompSign{2}{$PSymbol}{"Virt"}) + or ($CompSign{1}{$Symbol}{"PureVirt"} and $CompSign{2}{$PSymbol}{"PureVirt"})) { # relative position of virtual and pure virtual methods if($Level eq "Binary") { - if(defined $CompleteSignature{1}{$Symbol}{"RelPos"} and defined $CompleteSignature{2}{$PSymbol}{"RelPos"} - and $CompleteSignature{1}{$Symbol}{"RelPos"}!=$CompleteSignature{2}{$PSymbol}{"RelPos"}) + if(defined $CompSign{1}{$Symbol}{"RelPos"} and defined $CompSign{2}{$PSymbol}{"RelPos"} + and $CompSign{1}{$Symbol}{"RelPos"}!=$CompSign{2}{$PSymbol}{"RelPos"}) { # top-level virtual methods only - my $Class_Id = $CompleteSignature{1}{$Symbol}{"Class"}; + my $Class_Id = $CompSign{1}{$Symbol}{"Class"}; my $Class_Name = $TypeInfo{1}{$Class_Id}{"Name"}; if(defined $VirtualTable{1}{$Class_Name} and defined $VirtualTable{2}{$Class_Name} and $VirtualTable{1}{$Class_Name}{$Symbol}!=$VirtualTable{2}{$Class_Name}{$Symbol}) - { # check the absolute position of virtual method (including added and removed methods) - my %Class_Type = get_Type($Class_Id, 1); + { # check absolute position of a virtual method (including added and removed methods) + my %Class_Type = getType($Class_Id, 1); my $ProblemType = "Virtual_Method_Position"; - if($CompleteSignature{1}{$Symbol}{"PureVirt"}) { + if($CompSign{1}{$Symbol}{"PureVirt"}) { $ProblemType = "Pure_Virtual_Method_Position"; } if(isUsedClass($Class_Id, 1, $Level)) @@ -13405,14 +4819,14 @@ my @Affected = ($Symbol, keys(%{$OverriddenMethods{1}{$Symbol}})); foreach my $ASymbol (@Affected) { - if(not symbolFilter($ASymbol, 1, "Affected", $Level)) { + if(not symbolFilter($ASymbol, $CompSign{1}{$ASymbol}, "Affected", $Level, 1)) { next; } - %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$MnglName}}}=( + %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$Symbol}}=( "Type_Name"=>$Class_Type{"Name"}, - "Old_Value"=>$CompleteSignature{1}{$Symbol}{"RelPos"}, - "New_Value"=>$CompleteSignature{2}{$PSymbol}{"RelPos"}, - "Target"=>get_Signature($Symbol, 1)); + "Old_Value"=>$CompSign{1}{$Symbol}{"RelPos"}, + "New_Value"=>$CompSign{2}{$PSymbol}{"RelPos"}, + "Target"=>$Symbol); } $VTableChanged_M{$Class_Type{"Name"}} = 1; } @@ -13420,44 +4834,46 @@ } } } - if($CompleteSignature{1}{$Symbol}{"PureVirt"} - or $CompleteSignature{2}{$PSymbol}{"PureVirt"}) + if($CompSign{1}{$Symbol}{"PureVirt"} + or $CompSign{2}{$PSymbol}{"PureVirt"}) { # do NOT check type changes in pure virtuals next; } + $CheckedSymbols{$Level}{$Symbol} = 1; + if($Symbol=~/\A(_Z|\?)/ - or keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})==keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}})) + or keys(%{$CompSign{1}{$Symbol}{"Param"}})==keys(%{$CompSign{2}{$PSymbol}{"Param"}})) { # C/C++: changes in parameters - foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})) + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}})) { # checking parameters mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 1); } } else { # C: added/removed parameters - foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}})) + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{2}{$PSymbol}{"Param"}})) { # checking added parameters - my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"}; + my $PType2_Id = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"type"}; my $PType2_Name = $TypeInfo{2}{$PType2_Id}{"Name"}; last if($PType2_Name eq "..."); - my $PName = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}; - my $PName_Old = (defined $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos})?$CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"}:""; + my $PName = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}; + my $PName_Old = (defined $CompSign{1}{$Symbol}{"Param"}{$ParamPos})?$CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"}:""; my $ParamPos_Prev = "-1"; if($PName=~/\Ap\d+\Z/i) { # added unnamed parameter ( pN ) - my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 1); - my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 2); + my @Positions1 = findParamPairByTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 1); + my @Positions2 = findParamPairByTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 2); if($#Positions1==-1 or $#Positions2>$#Positions1) { $ParamPos_Prev = "lost"; } } else { - $ParamPos_Prev = find_ParamPair_Pos_byName($PName, $Symbol, 1); + $ParamPos_Prev = findParamPairByName($PName, $Symbol, 1); } if($ParamPos_Prev eq "lost") { - if($ParamPos>keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})-1) + if($ParamPos>keys(%{$CompSign{1}{$Symbol}{"Param"}})-1) { my $ProblemType = "Added_Parameter"; if($PName=~/\Ap\d+\Z/) { @@ -13467,15 +4883,15 @@ "Target"=>$PName, "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), "Param_Type"=>$PType2_Name, - "New_Signature"=>get_Signature($Symbol, 2) ); + "New_Signature"=>$Symbol ); } else { - my %ParamType_Pure = get_PureType($PType2_Id, $TypeInfo{2}); - my $PairType_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; - my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{1}); + my %ParamType_Pure = getPureType($PType2_Id, 2); + my $PairType_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; + my %PairType_Pure = getPureType($PairType_Id, 1); if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType2_Name eq $TypeInfo{1}{$PairType_Id}{"Name"}) - and find_ParamPair_Pos_byName($PName_Old, $Symbol, 2) eq "lost") + and findParamPairByName($PName_Old, $Symbol, 2) eq "lost") { if($PName_Old!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/) { @@ -13485,7 +4901,7 @@ "Param_Type"=>$PType2_Name, "Old_Value"=>$PName_Old, "New_Value"=>$PName, - "New_Signature"=>get_Signature($Symbol, 2) ); + "New_Signature"=>$Symbol ); } } else @@ -13498,48 +4914,48 @@ "Target"=>$PName, "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), "Param_Type"=>$PType2_Name, - "New_Signature"=>get_Signature($Symbol, 2) ); + "New_Signature"=>$Symbol ); } } } } - foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})) + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}})) { # check relevant parameters - my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; - my $ParamName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"}; + my $PType1_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; + my $ParamName1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"}; # FIXME: find relevant parameter by name - if(defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}) + if(defined $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}) { - my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"}; - my $ParamName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}; + my $PType2_Id = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"type"}; + my $ParamName2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}; if($TypeInfo{1}{$PType1_Id}{"Name"} eq $TypeInfo{2}{$PType2_Id}{"Name"} or ($ParamName1!~/\Ap\d+\Z/i and $ParamName1 eq $ParamName2)) { mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 0); } } } - foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})) + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{1}{$Symbol}{"Param"}})) { # checking removed parameters - my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; + my $PType1_Id = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"type"}; my $PType1_Name = $TypeInfo{1}{$PType1_Id}{"Name"}; last if($PType1_Name eq "..."); - my $PName = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"}; - my $PName_New = (defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos})?$CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}:""; + my $PName = $CompSign{1}{$Symbol}{"Param"}{$ParamPos}{"name"}; + my $PName_New = (defined $CompSign{2}{$PSymbol}{"Param"}{$ParamPos})?$CompSign{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}:""; my $ParamPos_New = "-1"; if($PName=~/\Ap\d+\Z/i) { # removed unnamed parameter ( pN ) - my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 1); - my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 2); + my @Positions1 = findParamPairByTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 1); + my @Positions2 = findParamPairByTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 2); if($#Positions2==-1 or $#Positions2<$#Positions1) { $ParamPos_New = "lost"; } } else { - $ParamPos_New = find_ParamPair_Pos_byName($PName, $Symbol, 2); + $ParamPos_New = findParamPairByName($PName, $Symbol, 2); } if($ParamPos_New eq "lost") { - if($ParamPos>keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}})-1) + if($ParamPos>keys(%{$CompSign{2}{$PSymbol}{"Param"}})-1) { my $ProblemType = "Removed_Parameter"; if($PName=~/\Ap\d+\Z/) { @@ -13549,15 +4965,15 @@ "Target"=>$PName, "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), "Param_Type"=>$PType1_Name, - "New_Signature"=>get_Signature($Symbol, 2) ); + "New_Signature"=>$Symbol); } - elsif($ParamPos$PType1_Name, "Old_Value"=>$PName, "New_Value"=>$PName_New, - "New_Signature"=>get_Signature($Symbol, 2) ); + "New_Signature"=>$Symbol); } } else @@ -13580,15 +4996,15 @@ "Target"=>$PName, "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2), "Param_Type"=>$PType1_Name, - "New_Signature"=>get_Signature($Symbol, 2) ); + "New_Signature"=>$Symbol); } } } } } # checking return type - my $ReturnType1_Id = $CompleteSignature{1}{$Symbol}{"Return"}; - my $ReturnType2_Id = $CompleteSignature{2}{$PSymbol}{"Return"}; + my $ReturnType1_Id = $CompSign{1}{$Symbol}{"Return"}; + my $ReturnType2_Id = $CompSign{2}{$PSymbol}{"Return"}; my %RC_SubProblems = detectTypeChange($ReturnType1_Id, $ReturnType2_Id, "Return", $Level); foreach my $SubProblemType (keys(%RC_SubProblems)) @@ -13597,7 +5013,7 @@ my $Old_Value = $RC_SubProblems{$SubProblemType}{"Old_Value"}; my %ProblemTypes = (); - if($CompleteSignature{1}{$Symbol}{"Data"}) + if($CompSign{1}{$Symbol}{"Data"}) { if($SubProblemType eq "Return_Type_And_Size") { $ProblemTypes{"Global_Data_Type_And_Size"} = 1; @@ -13643,17 +5059,15 @@ or $SubProblemType eq "Return_Type_And_Size" or $SubProblemType eq "Return_Type_Format") { - if(checkDump(1, "2.6") and checkDump(2, "2.6")) + if(addedQual($Old_Value, $New_Value, "volatile")) { - if(addedQual($Old_Value, $New_Value, "volatile")) - { - $ProblemTypes{"Return_Value_Became_Volatile"} = 1; - if($Level ne "Source" - or not cmpBTypes($Old_Value, $New_Value, 1, 2)) { - $ProblemTypes{"Return_Type"} = 1; - } + $ProblemTypes{"Return_Value_Became_Volatile"} = 1; + if($Level ne "Source" + or not cmpBTypes($Old_Value, $New_Value, 1, 2)) { + $ProblemTypes{"Return_Type"} = 1; } } + if(my $RA = addedQual($Old_Value, $New_Value, "const")) { if($RA==2) { @@ -13670,29 +5084,24 @@ } } if($Level eq "Binary" - and not $CompleteSignature{1}{$Symbol}{"Data"}) + and not $CompSign{1}{$Symbol}{"Data"}) { - my ($Arch1, $Arch2) = (getArch(1), getArch(2)); - if($Arch1 eq "unknown" or $Arch2 eq "unknown") - { # if one of the architectures is unknown - # then set other arhitecture to unknown too - ($Arch1, $Arch2) = ("unknown", "unknown"); - } my (%Conv1, %Conv2) = (); + if($UseConv_Real{1}{"R"} and $UseConv_Real{2}{"R"}) { - %Conv1 = callingConvention_R_Real($CompleteSignature{1}{$Symbol}); - %Conv2 = callingConvention_R_Real($CompleteSignature{2}{$PSymbol}); + %Conv1 = callingConvention_R_Real($CompSign{1}{$Symbol}); + %Conv2 = callingConvention_R_Real($CompSign{2}{$PSymbol}); } else { - %Conv1 = callingConvention_R_Model($CompleteSignature{1}{$Symbol}, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1}); - %Conv2 = callingConvention_R_Model($CompleteSignature{2}{$PSymbol}, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2}); + %Conv1 = callingConvention_R_Model($CompSign{1}{$Symbol}, 1); + %Conv2 = callingConvention_R_Model($CompSign{2}{$PSymbol}, 2); } if($SubProblemType eq "Return_Type_Became_Void") { - if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})) + if(keys(%{$CompSign{1}{$Symbol}{"Param"}})) { # parameters stack has been affected if($Conv1{"Method"} eq "stack") { $ProblemTypes{"Return_Type_Became_Void_And_Stack_Layout"} = 1; @@ -13704,7 +5113,7 @@ } elsif($SubProblemType eq "Return_Type_From_Void") { - if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})) + if(keys(%{$CompSign{1}{$Symbol}{"Param"}})) { # parameters stack has been affected if($Conv2{"Method"} eq "stack") { $ProblemTypes{"Return_Type_From_Void_And_Stack_Layout"} = 1; @@ -13766,11 +5175,11 @@ my $AddProblems = {}; - if($CompleteSignature{1}{$Symbol}{"Data"}) + if($CompSign{1}{$Symbol}{"Data"}) { if($Level eq "Binary") { - if(get_PLevel($ReturnType1_Id, 1)==0) + if(getPLevel($ReturnType1_Id, 1)==0) { if(defined $Sub_SubProblems->{"DataType_Size"}) { # add "Global_Data_Size" problem @@ -13798,8 +5207,8 @@ if($Old_Size!=$New_Size) { $AddProblems->{"Global_Data_Size"}{"retval"} = { - "Old_Size"=>$Old_Size*$BYTE_SIZE, - "New_Size"=>$New_Size*$BYTE_SIZE }; + "Old_Size"=>$Old_Size*$BYTE, + "New_Size"=>$New_Size*$BYTE }; } } } @@ -13832,17 +5241,28 @@ } # checking object type - my $ObjTId1 = $CompleteSignature{1}{$Symbol}{"Class"}; - my $ObjTId2 = $CompleteSignature{2}{$PSymbol}{"Class"}; + my $ObjTId1 = $CompSign{1}{$Symbol}{"Class"}; + my $ObjTId2 = $CompSign{2}{$PSymbol}{"Class"}; if($ObjTId1 and $ObjTId2 - and not $CompleteSignature{1}{$Symbol}{"Static"}) + and not $CompSign{1}{$Symbol}{"Static"} + and not $CompSign{1}{$Symbol}{"Data"}) { - my $ThisPtr1_Id = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}."*const", 1); - my $ThisPtr2_Id = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}."*const", 2); - if($ThisPtr1_Id and $ThisPtr2_Id) + my ($ThisPtr1, $ThisPtr2) = (undef, undef); + if($CompSign{1}{$Symbol}{"Const"}) + { + $ThisPtr1 = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}." const*const", 1); + $ThisPtr2 = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}." const*const", 2); + } + else + { + $ThisPtr1 = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}."*const", 1); + $ThisPtr2 = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}."*const", 2); + } + + if($ThisPtr1 and $ThisPtr2) { @RecurTypes = (); - my $Sub_SubProblems = mergeTypes($ThisPtr1_Id, $ThisPtr2_Id, $Level); + my $Sub_SubProblems = mergeTypes($ThisPtr1, $ThisPtr2, $Level); foreach my $SubProblemType (keys(%{$Sub_SubProblems})) { foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}})) @@ -13854,9 +5274,12 @@ } } } + if($Level eq "Binary") { mergeVTables($Level); } + + # mark all affected symbols as "checked" foreach my $Symbol (keys(%{$CompatProblems{$Level}})) { $CheckedSymbols{$Level}{$Symbol} = 1; } @@ -13881,8 +5304,8 @@ sub cmpBTypes($$$$) { my ($T1, $T2, $V1, $V2) = @_; - $T1 = uncover_typedefs($T1, $V1); - $T2 = uncover_typedefs($T2, $V2); + $T1 = uncoverTypedefs($T1, $V1); + $T2 = uncoverTypedefs($T2, $V2); return (rmQuals($T1, "all") eq rmQuals($T2, "all")); } @@ -13901,8 +5324,9 @@ sub removedQual_I($$$$$) { my ($Old_Value, $New_Value, $V1, $V2, $Qual) = @_; - $Old_Value = uncover_typedefs($Old_Value, $V1); - $New_Value = uncover_typedefs($New_Value, $V2); + $Old_Value = uncoverTypedefs($Old_Value, $V1); + $New_Value = uncoverTypedefs($New_Value, $V2); + if($Old_Value eq $New_Value) { # equal types return 0; @@ -13939,9 +5363,19 @@ } # cleaning - while($Value=~/(\w+)/ and $1 ne $Qual) { - $Value=~s/\b$1\b//g; + while($Value=~/(\w+)/) + { + my $W = $1; + + if($W eq $Qual) { + $Value=~s/\b$W\b/\@/g; + } + else { + $Value=~s/\b$W\b//g; + } } + + $Value=~s/\@/$Qual/g; $Value=~s/[^\*\&\w]+//g; # modeling @@ -13977,16 +5411,11 @@ sub showVal($$$) { - my ($Value, $TypeId, $LibVersion) = @_; - my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion}); - my $TName = uncover_typedefs($PureType{"Name"}, $LibVersion); - if(substr($Value, 0, 2) eq "_Z") - { - if(my $Unmangled = $tr_name{$Value}) { - return $Unmangled; - } - } - elsif(defined $StringTypes{$TName} or $TName=~/string/i) + my ($Value, $TypeId, $LVer) = @_; + my %PureType = getPureType($TypeId, $LVer); + my $TName = uncoverTypedefs($PureType{"Name"}, $LVer); + + if(defined $StringTypes{$TName} or $TName=~/string/i) { # strings return "\"$Value\""; } @@ -13998,28 +5427,30 @@ { # other return "\'\'"; } + return $Value; } sub getRegs($$$) { - my ($LibVersion, $Symbol, $Pos) = @_; + my ($LVer, $Symbol, $Pos) = @_; - if(defined $CompleteSignature{$LibVersion}{$Symbol}{"Reg"}) + if(defined $CompSign{$LVer}{$Symbol}{"Reg"}) { my %Regs = (); - foreach my $Elem (sort keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}})) + foreach my $Elem (sort keys(%{$CompSign{$LVer}{$Symbol}{"Reg"}})) { - if($Elem=~/\A$Pos([\.\+]|\Z)/) { - $Regs{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}{$Elem}} = 1; + if(index($Elem, $Pos)==0 + and $Elem=~/\A$Pos([\.\+]|\Z)/) { + $Regs{$CompSign{$LVer}{$Symbol}{"Reg"}{$Elem}} = 1; } } return join(", ", sort keys(%Regs)); } - elsif(defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"} - and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{0} - and not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{0}{"offset"}) + elsif(defined $CompSign{$LVer}{$Symbol}{"Param"} + and defined $CompSign{$LVer}{$Symbol}{"Param"}{0} + and not defined $CompSign{$LVer}{$Symbol}{"Param"}{0}{"offset"}) { return "unknown"; } @@ -14029,197 +5460,170 @@ sub mergeParameters($$$$$$) { - my ($Symbol, $PSymbol, $ParamPos1, $ParamPos2, $Level, $ChkRnmd) = @_; - if(not $Symbol) { - return; - } - my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"type"}; - my $PName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"name"}; - my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"type"}; - my $PName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"name"}; - if(not $PType1_Id - or not $PType2_Id) { + my ($Symbol, $PSymbol, $ParamPos1, $ParamPos2, $Level, $CheckRenamed) = @_; + + my $PTid1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"type"}; + my $PTid2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"type"}; + + if(not $PTid1 + or not $PTid2) { return; } - if($Symbol=~/\A(_Z|\?)/) + my $PName1 = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"name"}; + my $PName2 = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"name"}; + + if(index($Symbol, "_Z")==0 + or index($Symbol, "?")==0) { # do not merge "this" if($PName1 eq "this" or $PName2 eq "this") { return; } } - my %Type1 = get_Type($PType1_Id, 1); - my %Type2 = get_Type($PType2_Id, 2); + my %Type1 = getType($PTid1, 1); + my %Type2 = getType($PTid2, 2); - my %PureType1 = get_PureType($PType1_Id, $TypeInfo{1}); + my %PureType1 = getPureType($PTid1, 1); - my %BaseType1 = get_BaseType($PType1_Id, 1); - my %BaseType2 = get_BaseType($PType2_Id, 2); + my %BaseType1 = getBaseType($PTid1, 1); + my %BaseType2 = getBaseType($PTid2, 2); - my $Parameter_Location = ($PName1)?$PName1:showPos($ParamPos1)." Parameter"; + my $ParamLoc = ($PName1)?$PName1:showPos($ParamPos1)." Parameter"; if($Level eq "Binary") { - if(checkDump(1, "2.6.1") and checkDump(2, "2.6.1")) - { # "reg" attribute added in ACC 1.95.1 (dump 2.6.1 format) - if($CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"} - and not $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"}) - { - %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Non_Register"}{$Parameter_Location}}=( - "Target"=>$PName1, - "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1) ); - } - elsif(not $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"} - and $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"}) - { - %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Register"}{$Parameter_Location}}=( - "Target"=>$PName1, - "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1) ); - } + if($CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"} + and not $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"}) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Non_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1) ); + } + elsif(not $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"} + and $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"}) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1) ); } if(defined $UsedDump{1}{"DWARF"} and defined $UsedDump{2}{"DWARF"}) { - if(checkDump(1, "3.0") and checkDump(2, "3.0")) + my $Old_Regs = getRegs(1, $Symbol, $ParamPos1); + my $New_Regs = getRegs(2, $PSymbol, $ParamPos2); + + my $Old_Offset = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"offset"}; + my $New_Offset = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"offset"}; + + if($Old_Regs ne "unknown" + and $New_Regs ne "unknown") { - my $Old_Regs = getRegs(1, $Symbol, $ParamPos1); - my $New_Regs = getRegs(2, $PSymbol, $ParamPos2); - - if($Old_Regs ne "unknown" - and $New_Regs ne "unknown") + if($Old_Regs and $New_Regs) { - if($Old_Regs and $New_Regs) - { - if($Old_Regs ne $New_Regs) - { - %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Register"}{$Parameter_Location}}=( - "Target"=>$PName1, - "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "Old_Value"=>$Old_Regs, - "New_Value"=>$New_Regs ); - } - } - elsif($Old_Regs and not $New_Regs) + if($Old_Regs ne $New_Regs) { - %{$CompatProblems{$Level}{$Symbol}{"Parameter_From_Register"}{$Parameter_Location}}=( - "Target"=>$PName1, - "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "Old_Value"=>$Old_Regs ); - } - elsif(not $Old_Regs and $New_Regs) - { - %{$CompatProblems{$Level}{$Symbol}{"Parameter_To_Register"}{$Parameter_Location}}=( + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Register"}{$ParamLoc}}=( "Target"=>$PName1, "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Old_Regs, "New_Value"=>$New_Regs ); } } - - if((my $Old_Offset = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"offset"}) ne "" - and (my $New_Offset = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"offset"}) ne "") + elsif($Old_Regs and not $New_Regs and $New_Offset ne "") + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_From_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Old_Regs ); + } + elsif(not $Old_Regs and $Old_Offset ne "" and $New_Regs) + { + %{$CompatProblems{$Level}{$Symbol}{"Parameter_To_Register"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "New_Value"=>$New_Regs ); + } + } + + if($Old_Offset ne "" + and $New_Offset ne "") + { + if($Old_Offset ne $New_Offset) { + my $Start1 = $CompSign{1}{$Symbol}{"Param"}{0}{"offset"}; + my $Start2 = $CompSign{2}{$PSymbol}{"Param"}{0}{"offset"}; + + $Old_Offset = $Old_Offset - $Start1; + $New_Offset = $New_Offset - $Start2; + if($Old_Offset ne $New_Offset) { - my $Start1 = $CompleteSignature{1}{$Symbol}{"Param"}{0}{"offset"}; - my $Start2 = $CompleteSignature{2}{$Symbol}{"Param"}{0}{"offset"}; - - $Old_Offset = $Old_Offset - $Start1; - $New_Offset = $New_Offset - $Start2; - - if($Old_Offset ne $New_Offset) - { - %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Offset"}{$Parameter_Location}}=( - "Target"=>$PName1, - "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "Old_Value"=>$Old_Offset, - "New_Value"=>$New_Offset ); - } + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Offset"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "Old_Value"=>$Old_Offset, + "New_Value"=>$New_Offset ); } } } } } - if(checkDump(1, "2.0") and checkDump(2, "2.0") - and $UsedDump{1}{"V"} ne "3.1" and $UsedDump{2}{"V"} ne "3.1") - { # "default" attribute added in ACC 1.22 (dump 2.0 format) - # broken in 3.1, fixed in 3.2 - my $Value_Old = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"default"}; - my $Value_New = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"default"}; - if(not checkDump(1, "2.13") - and checkDump(2, "2.13")) - { # support for old ABI dumps - if(defined $Value_Old and defined $Value_New) - { - if($PureType1{"Name"} eq "bool" - and $Value_Old eq "false" and $Value_New eq "0") - { # int class::method ( bool p = 0 ); - # old ABI dumps: "false" - # new ABI dumps: "0" - $Value_Old = "0"; - } - } - } - if(not checkDump(1, "2.18") - and checkDump(2, "2.18")) - { # support for old ABI dumps - if(not defined $Value_Old - and substr($Value_New, 0, 2) eq "_Z") { - $Value_Old = $Value_New; - } - } - if(defined $Value_Old) + + my $Value_Old = $CompSign{1}{$Symbol}{"Param"}{$ParamPos1}{"default"}; + my $Value_New = $CompSign{2}{$PSymbol}{"Param"}{$ParamPos2}{"default"}; + + if(defined $Value_Old) + { + $Value_Old = showVal($Value_Old, $PTid1, 1); + if(defined $Value_New) { - $Value_Old = showVal($Value_Old, $PType1_Id, 1); - if(defined $Value_New) - { - $Value_New = showVal($Value_New, $PType2_Id, 2); - if($Value_Old ne $Value_New) - { # FIXME: how to distinguish "0" and 0 (NULL) - %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Changed"}{$Parameter_Location}}=( - "Target"=>$PName1, - "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "Old_Value"=>$Value_Old, - "New_Value"=>$Value_New ); - } - } - else - { - %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Removed"}{$Parameter_Location}}=( + $Value_New = showVal($Value_New, $PTid2, 2); + if($Value_Old ne $Value_New) + { # FIXME: how to distinguish "0" and 0 (NULL) + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Changed"}{$ParamLoc}}=( "Target"=>$PName1, "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "Old_Value"=>$Value_Old ); + "Old_Value"=>$Value_Old, + "New_Value"=>$Value_New ); } } - elsif(defined $Value_New) + else { - $Value_New = showVal($Value_New, $PType2_Id, 2); - %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Added"}{$Parameter_Location}}=( + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Removed"}{$ParamLoc}}=( "Target"=>$PName1, "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "New_Value"=>$Value_New ); + "Old_Value"=>$Value_Old ); } } + elsif(defined $Value_New) + { + $Value_New = showVal($Value_New, $PTid2, 2); + %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Added"}{$ParamLoc}}=( + "Target"=>$PName1, + "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), + "New_Value"=>$Value_New ); + } - if($ChkRnmd) + if($CheckRenamed) { if($PName1 and $PName2 and $PName1 ne $PName2 - and $PType1_Id!=-1 and $PType2_Id!=-1 + and $PTid1!=-1 and $PTid2!=-1 and $PName1!~/\Ap\d+\Z/ and $PName2!~/\Ap\d+\Z/) { # except unnamed "..." value list (Id=-1) %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos1)." Parameter"}}=( "Target"=>$PName1, "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "Param_Type"=>$TypeInfo{1}{$PType1_Id}{"Name"}, + "Param_Type"=>$TypeInfo{1}{$PTid1}{"Name"}, "Old_Value"=>$PName1, "New_Value"=>$PName2, - "New_Signature"=>get_Signature($Symbol, 2) ); + "New_Signature"=>$Symbol); } } # checking type change (replace) - my %SubProblems = detectTypeChange($PType1_Id, $PType2_Id, "Parameter", $Level); + my %SubProblems = detectTypeChange($PTid1, $PTid2, "Parameter", $Level); foreach my $SubProblemType (keys(%SubProblems)) { # add new problems, remove false alarms @@ -14231,21 +5635,17 @@ or $SubProblemType eq "Parameter_Type_And_Size" or $SubProblemType eq "Parameter_Type_Format") { - if(checkDump(1, "2.6") and checkDump(2, "2.6")) - { - if(addedQual($Old_Value, $New_Value, "restrict")) { - %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}}; - } - elsif(removedQual($Old_Value, $New_Value, "restrict")) { - %{$SubProblems{"Parameter_Became_Non_Restrict"}} = %{$SubProblems{$SubProblemType}}; - } + if(addedQual($Old_Value, $New_Value, "restrict")) { + %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}}; } - if(checkDump(1, "2.6") and checkDump(2, "2.6")) - { - if(removedQual($Old_Value, $New_Value, "volatile")) { - %{$SubProblems{"Parameter_Became_Non_Volatile"}} = %{$SubProblems{$SubProblemType}}; - } + elsif(removedQual($Old_Value, $New_Value, "restrict")) { + %{$SubProblems{"Parameter_Became_Non_Restrict"}} = %{$SubProblems{$SubProblemType}}; } + + if(removedQual($Old_Value, $New_Value, "volatile")) { + %{$SubProblems{"Parameter_Became_Non_Volatile"}} = %{$SubProblems{$SubProblemType}}; + } + if($Type2{"Type"} eq "Const" and $BaseType2{"Name"} eq $Type1{"Name"} and $Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/) { # int to "int const" @@ -14311,29 +5711,26 @@ elsif($Level eq "Binary" and ($SubProblemType eq "Parameter_Type_And_Size" or $SubProblemType eq "Parameter_Type" or $SubProblemType eq "Parameter_Type_Format")) { - my ($Arch1, $Arch2) = (getArch(1), getArch(2)); - if($Arch1 eq "unknown" - or $Arch2 eq "unknown") - { # if one of the architectures is unknown - # then set other arhitecture to unknown too - ($Arch1, $Arch2) = ("unknown", "unknown"); - } my (%Conv1, %Conv2) = (); + if($UseConv_Real{1}{"P"} and $UseConv_Real{2}{"P"}) { # real - %Conv1 = callingConvention_P_Real($CompleteSignature{1}{$Symbol}, $ParamPos1); - %Conv2 = callingConvention_P_Real($CompleteSignature{2}{$Symbol}, $ParamPos2); + %Conv1 = callingConvention_P_Real($CompSign{1}{$Symbol}, $ParamPos1); + %Conv2 = callingConvention_P_Real($CompSign{2}{$PSymbol}, $ParamPos2); } else { # model - %Conv1 = callingConvention_P_Model($CompleteSignature{1}{$Symbol}, $ParamPos1, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1}); - %Conv2 = callingConvention_P_Model($CompleteSignature{2}{$Symbol}, $ParamPos2, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2}); + %Conv1 = callingConvention_P_Model($CompSign{1}{$Symbol}, $ParamPos1, 1); + %Conv2 = callingConvention_P_Model($CompSign{2}{$PSymbol}, $ParamPos2, 2); } + + my $ArrayType = ($Type1{"Type"} eq "Array" and $Type2{"Type"} eq "Array"); # Fortran + if($Conv1{"Method"} eq $Conv2{"Method"}) { if($Conv1{"Method"} eq "stack") { - if($Old_Size ne $New_Size) { # FIXME: isMemPadded, getOffset + if($Old_Size ne $New_Size and not $ArrayType) { # FIXME: isMemPadded, getOffset $NewProblemType = "Parameter_Type_And_Stack"; } } @@ -14357,42 +5754,47 @@ $SubProblems{$SubProblemType}{"Old_Reg"} = $Conv1{"Registers"}; $SubProblems{$SubProblemType}{"New_Reg"} = $Conv2{"Registers"}; } - %{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}=( + %{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$ParamLoc}}=( "Target"=>$PName1, "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1), - "New_Signature"=>get_Signature($Symbol, 2) ); - @{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}}; + "New_Signature"=>$Symbol); + @{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$ParamLoc}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}}; } @RecurTypes = (); # checking type definition changes - my $Sub_SubProblems = mergeTypes($PType1_Id, $PType2_Id, $Level); + my $Sub_SubProblems = mergeTypes($PTid1, $PTid2, $Level); foreach my $SubProblemType (keys(%{$Sub_SubProblems})) { - foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}})) + foreach my $SubLoc (keys(%{$Sub_SubProblems->{$SubProblemType}})) { my $NewProblemType = $SubProblemType; if($SubProblemType eq "DataType_Size") { - if($PureType1{"Type"}!~/\A(Pointer|Ref)\Z/ and $SubLocation!~/\-\>/) + if($PureType1{"Type"} ne "Pointer" + and $PureType1{"Type"} ne "Ref" + and index($SubLoc, "->")==-1) { # stack has been affected $NewProblemType = "DataType_Size_And_Stack"; } } - my $NewLocation = ($SubLocation)?$Parameter_Location."->".$SubLocation:$Parameter_Location; - $CompatProblems{$Level}{$Symbol}{$NewProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation}; + my $NewLoc = $ParamLoc; + if($SubLoc) { + $NewLoc .= "->".$SubLoc; + } + $CompatProblems{$Level}{$Symbol}{$NewProblemType}{$NewLoc} = $Sub_SubProblems->{$SubProblemType}{$SubLoc}; } } } -sub find_ParamPair_Pos_byName($$$) +sub findParamPairByName($$$) { - my ($Name, $Symbol, $LibVersion) = @_; - foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}})) + my ($Name, $Symbol, $LVer) = @_; + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) { - next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}); - if($CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"name"} eq $Name) + next if(not defined $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}); + if($CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}{"name"} eq $Name) { return $ParamPos; } @@ -14400,29 +5802,23 @@ return "lost"; } -sub find_ParamPair_Pos_byTypeAndPos($$$$$) +sub findParamPairByTypeAndPos($$$$$) { - my ($TypeName, $MediumPos, $Order, $Symbol, $LibVersion) = @_; + my ($TypeName, $MediumPos, $Order, $Symbol, $LVer) = @_; my @Positions = (); - foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}})) + foreach my $ParamPos (sort {$a<=>$b} keys(%{$CompSign{$LVer}{$Symbol}{"Param"}})) { next if($Order eq "backward" and $ParamPos>$MediumPos); next if($Order eq "forward" and $ParamPos<$MediumPos); - next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}); - my $PTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"type"}; - if($TypeInfo{$LibVersion}{$PTypeId}{"Name"} eq $TypeName) { + next if(not defined $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}); + my $PTypeId = $CompSign{$LVer}{$Symbol}{"Param"}{$ParamPos}{"type"}; + if($TypeInfo{$LVer}{$PTypeId}{"Name"} eq $TypeName) { push(@Positions, $ParamPos); } } return @Positions; } -sub getTypeIdByName($$) -{ - my ($TypeName, $LibVersion) = @_; - return $TName_Tid{$LibVersion}{formatName($TypeName, "T")}; -} - sub diffTypes($$$) { if(defined $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]}) { @@ -14444,8 +5840,8 @@ { my ($Type1_Id, $Type2_Id, $Level) = @_; - my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1}); - my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2}); + my %Type1_Pure = getPureType($Type1_Id, 1); + my %Type2_Pure = getPureType($Type2_Id, 2); if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}) { # equal types @@ -14455,6 +5851,10 @@ { # from void* to something return 0; } + if($Type2_Pure{"Name"} eq "void") + { # from something to void* + return 0; + } if($Type1_Pure{"Name"}=~/\*/ or $Type2_Pure{"Name"}=~/\*/) { # compared in detectTypeChange() @@ -14559,8 +5959,8 @@ if($MT1{"Name"} ne $MT2{"Name"} or isAnon($MT1{"Name"}) or isAnon($MT2{"Name"})) { - my $PL1 = get_PLevel($MT1{"Tid"}, 1); - my $PL2 = get_PLevel($MT2{"Tid"}, 2); + my $PL1 = getPLevel($MT1{"Tid"}, 1); + my $PL2 = getPLevel($MT2{"Tid"}, 2); if($PL1 ne $PL2) { # different pointer level @@ -14568,8 +5968,8 @@ } # compare base types - my %BT1 = get_BaseType($MT1{"Tid"}, 1); - my %BT2 = get_BaseType($MT2{"Tid"}, 2); + my %BT1 = getBaseType($MT1{"Tid"}, 1); + my %BT2 = getBaseType($MT2{"Tid"}, 2); if(diffTypes($BT1{"Tid"}, $BT2{"Tid"}, $Level)) { # different types @@ -14594,18 +5994,53 @@ return (); } my %LocalProblems = (); - my %Type1 = get_Type($Type1_Id, 1); - my %Type2 = get_Type($Type2_Id, 2); - my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1}); - my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2}); - my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type1_Pure{"Tid"}, $TypeInfo{1}):get_BaseType($Type1_Id, 1); - my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type2_Pure{"Tid"}, $TypeInfo{2}):get_BaseType($Type2_Id, 2); - - my $Type1_PLevel = get_PLevel($Type1_Id, 1); - my $Type2_PLevel = get_PLevel($Type2_Id, 2); - return () if(not $Type1{"Name"} or not $Type2{"Name"}); - return () if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"}); - return () if($Type1_PLevel eq "" or $Type2_PLevel eq ""); + + my %Type1 = getType($Type1_Id, 1); + my %Type2 = getType($Type2_Id, 2); + + if(not $Type1{"Name"} or not $Type2{"Name"}) { + return (); + } + + my %Type1_Pure = getPureType($Type1_Id, 1); + my %Type2_Pure = getPureType($Type2_Id, 2); + + if(defined $In::Opt{"SkipTypedefUncover"}) + { + if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}) { + return (); + } + + if(cmpBTypes($Type1_Pure{"Name"}, $Type2_Pure{"Name"}, 1, 2)) { + return (); + } + } + + my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?getOneStepBaseType($Type1_Pure{"Tid"}, 1):getBaseType($Type1_Id, 1); + my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?getOneStepBaseType($Type2_Pure{"Tid"}, 2):getBaseType($Type2_Id, 2); + + if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"}) { + return (); + } + + if(defined $UsedDump{1}{"DWARF"}) + { + if($Type1_Pure{"Name"} eq "__unknown__" + or $Type2_Pure{"Name"} eq "__unknown__" + or $Type1_Base{"Name"} eq "__unknown__" + or $Type2_Base{"Name"} eq "__unknown__") + { # Error ABI dump + return (); + } + } + + my $Type1_PLevel = getPLevel($Type1_Id, 1); + my $Type2_PLevel = getPLevel($Type2_Id, 2); + + if($Type1_PLevel eq "" or $Type2_PLevel eq "") { + return (); + } + if($Type1_Base{"Name"} ne $Type2_Base{"Name"} and ($Type1{"Name"} eq $Type2{"Name"} or ($Type1_PLevel>=1 and $Type1_PLevel==$Type2_PLevel and $Type1_Base{"Name"} ne "void" and $Type2_Base{"Name"} ne "void"))) @@ -14636,8 +6071,8 @@ %{$LocalProblems{$Prefix."_BaseType_And_Size"}}=( "Old_Value"=>$Type1_Base{"Name"}, "New_Value"=>$Type2_Base{"Name"}, - "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1_Base{"Size"}*$BYTE, + "New_Size"=>$Type2_Base{"Size"}*$BYTE); } else { @@ -14646,16 +6081,16 @@ %{$LocalProblems{$Prefix."_BaseType_Format"}}=( "Old_Value"=>$Type1_Base{"Name"}, "New_Value"=>$Type2_Base{"Name"}, - "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1_Base{"Size"}*$BYTE, + "New_Size"=>$Type2_Base{"Size"}*$BYTE); } elsif(tNameLock($Type1_Base{"Tid"}, $Type2_Base{"Tid"})) { %{$LocalProblems{$Prefix."_BaseType"}}=( "Old_Value"=>$Type1_Base{"Name"}, "New_Value"=>$Type2_Base{"Name"}, - "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1_Base{"Size"}*$BYTE, + "New_Size"=>$Type2_Base{"Size"}*$BYTE); } } } @@ -14664,31 +6099,34 @@ { # type change if($Type1{"Name"}!~/anon\-/ and $Type2{"Name"}!~/anon\-/) { + my $ArrayType = ($Type1{"Type"} eq "Array" and $Type2{"Type"} eq "Array"); # Fortran + if($Prefix eq "Return" and $Type1_Pure{"Name"} eq "void") { %{$LocalProblems{"Return_Type_From_Void"}}=( "New_Value"=>$Type2{"Name"}, - "New_Size"=>$Type2{"Size"}*$BYTE_SIZE); + "New_Size"=>$Type2{"Size"}*$BYTE); } elsif($Prefix eq "Return" and $Type2_Pure{"Name"} eq "void") { %{$LocalProblems{"Return_Type_Became_Void"}}=( "Old_Value"=>$Type1{"Name"}, - "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1{"Size"}*$BYTE); } else { if($Level eq "Binary" and $Type1{"Size"} and $Type2{"Size"} - and $Type1{"Size"} ne $Type2{"Size"}) + and $Type1{"Size"} ne $Type2{"Size"} + and not $ArrayType) { %{$LocalProblems{$Prefix."_Type_And_Size"}}=( "Old_Value"=>$Type1{"Name"}, "New_Value"=>$Type2{"Name"}, - "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE); } else { @@ -14697,16 +6135,16 @@ %{$LocalProblems{$Prefix."_Type_Format"}}=( "Old_Value"=>$Type1{"Name"}, "New_Value"=>$Type2{"Name"}, - "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE); } elsif(tNameLock($Type1_Id, $Type2_Id)) { # FIXME: correct this condition %{$LocalProblems{$Prefix."_Type"}}=( "Old_Value"=>$Type1{"Name"}, "New_Value"=>$Type2{"Name"}, - "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE, - "New_Size"=>$Type2{"Size"}*$BYTE_SIZE); + "Old_Size"=>$Type1{"Size"}*$BYTE, + "New_Size"=>$Type2{"Size"}*$BYTE); } } } @@ -14760,22 +6198,6 @@ sub tNameLock($$) { my ($Tid1, $Tid2) = @_; - my $Changed = 0; - if(differentDumps("G")) - { # different GCC versions - $Changed = 1; - } - elsif(differentDumps("V")) - { # different versions of ABI dumps - if(not checkDump(1, "2.20") - or not checkDump(2, "2.20")) - { # latest names update - # 2.6: added restrict qualifier - # 2.13: added missed typedefs to qualified types - # 2.20: prefix for struct, union and enum types - $Changed = 1; - } - } my $TN1 = $TypeInfo{1}{$Tid1}{"Name"}; my $TN2 = $TypeInfo{2}{$Tid2}{"Name"}; @@ -14783,49 +6205,23 @@ my $TT1 = $TypeInfo{1}{$Tid1}{"Type"}; my $TT2 = $TypeInfo{2}{$Tid2}{"Type"}; - if($Changed) + if($In::ABI{1}{"GccVersion"} ne $In::ABI{2}{"GccVersion"}) { # different formats - my %Base1 = get_Type($Tid1, 1); + my %Base1 = getType($Tid1, 1); while(defined $Base1{"Type"} and $Base1{"Type"} eq "Typedef") { - %Base1 = get_OneStep_BaseType($Base1{"Tid"}, $TypeInfo{1}); + %Base1 = getOneStepBaseType($Base1{"Tid"}, 1); } - my %Base2 = get_Type($Tid2, 2); + my %Base2 = getType($Tid2, 2); while(defined $Base2{"Type"} and $Base2{"Type"} eq "Typedef") { - %Base2 = get_OneStep_BaseType($Base2{"Tid"}, $TypeInfo{2}); + %Base2 = getOneStepBaseType($Base2{"Tid"}, 2); } - my $BName1 = uncover_typedefs($Base1{"Name"}, 1); - my $BName2 = uncover_typedefs($Base2{"Name"}, 2); + my $BName1 = uncoverTypedefs($Base1{"Name"}, 1); + my $BName2 = uncoverTypedefs($Base2{"Name"}, 2); + if($BName1 eq $BName2) { # equal base types return 0; } - - if(not checkDump(1, "2.13") - or not checkDump(2, "2.13")) - { # broken array names in ABI dumps < 2.13 - if($TT1 eq "Array" - and $TT2 eq "Array") { - return 0; - } - } - - if(not checkDump(1, "2.6") - or not checkDump(2, "2.6")) - { # added restrict attribute in 2.6 - if($TN1!~/\brestrict\b/ - and $TN2=~/\brestrict\b/) { - return 0; - } - } - - if(not checkDump(1, "2.20") - or not checkDump(2, "2.20")) - { # added type prefix in 2.20 - if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/ - or $TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) { - return 0; - } - } } else { @@ -14871,453 +6267,6 @@ return 1; } -sub differentDumps($) -{ - my $Check = $_[0]; - if(defined $Cache{"differentDumps"}{$Check}) { - return $Cache{"differentDumps"}{$Check}; - } - if($UsedDump{1}{"V"} and $UsedDump{2}{"V"}) - { - if($Check eq "G") - { - if(getGccVersion(1) ne getGccVersion(2)) - { # different GCC versions - return ($Cache{"differentDumps"}{$Check}=1); - } - } - if($Check eq "V") - { - if(cmpVersions(formatVersion($UsedDump{1}{"V"}, 2), - formatVersion($UsedDump{2}{"V"}, 2))!=0) - { # different dump versions (skip micro version) - return ($Cache{"differentDumps"}{$Check}=1); - } - } - } - return ($Cache{"differentDumps"}{$Check}=0); -} - -sub formatVersion($$) -{ # cut off version digits - my ($V, $Digits) = @_; - my @Elems = split(/\./, $V); - return join(".", splice(@Elems, 0, $Digits)); -} - -sub htmlSpecChars($) -{ - my $Str = $_[0]; - if(not $Str) { - return $Str; - } - $Str=~s/\&([^#]|\Z)/&$1/g; - $Str=~s//->/g; # − - $Str=~s/>/>/g; - $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g; - $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g; - $Str=~s/ / /g; #   - $Str=~s/\@SP\@/ /g; - $Str=~s/\n//g; - $Str=~s/\"/"/g; - $Str=~s/\'/'/g; - return $Str; -} - -sub xmlSpecChars($) -{ - my $Str = $_[0]; - if(not $Str) { - return $Str; - } - - $Str=~s/\&([^#]|\Z)/&$1/g; - $Str=~s//>/g; - - $Str=~s/\"/"/g; - $Str=~s/\'/'/g; - - return $Str; -} - -sub xmlSpecChars_R($) -{ - my $Str = $_[0]; - if(not $Str) { - return $Str; - } - - $Str=~s/&/&/g; - $Str=~s/<//g; - - $Str=~s/"/"/g; - $Str=~s/'/'/g; - - return $Str; -} - -sub black_name($) -{ - my $Name = $_[0]; - return "".highLight_Signature($Name).""; -} - -sub highLight_Signature($) -{ - my $Signature = $_[0]; - return highLight_Signature_PPos_Italic($Signature, "", 0, 0, 0); -} - -sub highLight_Signature_Italic_Color($) -{ - my $Signature = $_[0]; - return highLight_Signature_PPos_Italic($Signature, "", 1, 1, 1); -} - -sub separate_symbol($) -{ - my $Symbol = $_[0]; - my ($Name, $Spec, $Ver) = ($Symbol, "", ""); - if($Symbol=~/\A([^\@\$\?]+)([\@\$]+)([^\@\$]+)\Z/) { - ($Name, $Spec, $Ver) = ($1, $2, $3); - } - return ($Name, $Spec, $Ver); -} - -sub cut_f_attrs($) -{ - if($_[0]=~s/(\))((| (const volatile|const|volatile))(| \[static\]))\Z/$1/) { - return $2; - } - return ""; -} - -sub highLight_Signature_PPos_Italic($$$$$) -{ - my ($FullSignature, $Param_Pos, $ItalicParams, $ColorParams, $ShowReturn) = @_; - $Param_Pos = "" if(not defined $Param_Pos); - my ($Signature, $VersionSpec, $SymbolVersion) = separate_symbol($FullSignature); - my $Return = ""; - if($ShowRetVal and $Signature=~s/([^:]):([^:].+?)\Z/$1/g) { - $Return = $2; - } - my $SCenter = find_center($Signature, "("); - if(not $SCenter) - { # global data - $Signature = htmlSpecChars($Signature); - $Signature=~s!(\[data\])!$1!g; - $Signature .= (($SymbolVersion)?" $VersionSpec $SymbolVersion":""); - if($Return and $ShowReturn) { - $Signature .= "  :  ".htmlSpecChars($Return).""; - } - return $Signature; - } - my ($Begin, $End) = (substr($Signature, 0, $SCenter), ""); - $Begin.=" " if($Begin!~/ \Z/); - $End = cut_f_attrs($Signature); - my @Parts = (); - my ($Short, $Params) = split_Signature($Signature); - my @SParts = separate_Params($Params, 1, 1); - foreach my $Pos (0 .. $#SParts) - { - my $Part = $SParts[$Pos]; - $Part=~s/\A\s+|\s+\Z//g; - my ($Part_Styled, $ParamName) = (htmlSpecChars($Part), ""); - if($Part=~/\([\*]+(\w+)\)/i) { - $ParamName = $1;#func-ptr - } - elsif($Part=~/(\w+)[\,\)]*\Z/i) { - $ParamName = $1; - } - if(not $ParamName) - { - push(@Parts, $Part_Styled); - next; - } - if($ItalicParams and not $TName_Tid{1}{$Part} - and not $TName_Tid{2}{$Part}) - { - my $Style = "param"; - if($Param_Pos ne "" - and $Pos==$Param_Pos) { - $Style = "focus_p"; - } - elsif($ColorParams) { - $Style = "color_p"; - } - $Part_Styled =~ s!(\W)$ParamName([\,\)]|\Z)!$1$ParamName$2!ig; - } - $Part_Styled=~s/,(\w)/, $1/g; - push(@Parts, $Part_Styled); - } - if(@Parts) - { - foreach my $Num (0 .. $#Parts) - { - if($Num==$#Parts) - { # add ")" to the last parameter - $Parts[$Num] = "".$Parts[$Num]." )"; - } - elsif(length($Parts[$Num])<=45) { - $Parts[$Num] = "".$Parts[$Num].""; - } - } - $Signature = htmlSpecChars($Begin)."( ".join(" ", @Parts)."".$End; - } - else { - $Signature = htmlSpecChars($Begin)."( )".$End; - } - if($Return and $ShowReturn) { - $Signature .= "  :  ".htmlSpecChars($Return).""; - } - $Signature=~s!\[\]![ ]!g; - $Signature=~s!operator=!operator =!g; - $Signature=~s!(\[in-charge\]|\[not-in-charge\]|\[in-charge-deleting\]|\[static\])!$1!g; - if($SymbolVersion) { - $Signature .= " $VersionSpec $SymbolVersion"; - } - return $Signature; -} - -sub split_Signature($) -{ - my $Signature = $_[0]; - if(my $ShortName = substr($Signature, 0, find_center($Signature, "("))) - { - $Signature=~s/\A\Q$ShortName\E\(//g; - cut_f_attrs($Signature); - $Signature=~s/\)\Z//; - return ($ShortName, $Signature); - } - - # error - return ($Signature, ""); -} - -sub separate_Params($$$) -{ - my ($Params, $Comma, $Sp) = @_; - my @Parts = (); - my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); - my $Part = 0; - foreach my $Pos (0 .. length($Params) - 1) - { - my $S = substr($Params, $Pos, 1); - if(defined $B{$S}) { - $B{$S} += 1; - } - if($S eq "," and - $B{"("}==$B{")"} and $B{"<"}==$B{">"}) - { - if($Comma) - { # include comma - $Parts[$Part] .= $S; - } - $Part += 1; - } - else { - $Parts[$Part] .= $S; - } - } - if(not $Sp) - { # remove spaces - foreach (@Parts) - { - s/\A //g; - s/ \Z//g; - } - } - return @Parts; -} - -sub find_center($$) -{ - my ($Sign, $Target) = @_; - my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 ); - my $Center = 0; - if($Sign=~s/(operator([^\w\s\(\)]+|\(\)))//g) - { # operators - $Center+=length($1); - } - foreach my $Pos (0 .. length($Sign)-1) - { - my $S = substr($Sign, $Pos, 1); - if($S eq $Target) - { - if($B{"("}==$B{")"} - and $B{"<"}==$B{">"}) { - return $Center; - } - } - if(defined $B{$S}) { - $B{$S}+=1; - } - $Center+=1; - } - return 0; -} - -sub appendFile($$) -{ - my ($Path, $Content) = @_; - return if(not $Path); - if(my $Dir = get_dirname($Path)) { - mkpath($Dir); - } - open(FILE, ">>", $Path) || die ("can't open file \'$Path\': $!\n"); - print FILE $Content; - close(FILE); -} - -sub writeFile($$) -{ - my ($Path, $Content) = @_; - return if(not $Path); - if(my $Dir = get_dirname($Path)) { - mkpath($Dir); - } - open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n"); - print FILE $Content; - close(FILE); -} - -sub readFile($) -{ - my $Path = $_[0]; - return "" if(not $Path or not -f $Path); - open(FILE, $Path); - local $/ = undef; - my $Content = ; - close(FILE); - if($Path!~/\.(tu|class|abi)\Z/) { - $Content=~s/\r/\n/g; - } - return $Content; -} - -sub get_filename($) -{ # much faster than basename() from File::Basename module - if(defined $Cache{"get_filename"}{$_[0]}) { - return $Cache{"get_filename"}{$_[0]}; - } - if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) { - return ($Cache{"get_filename"}{$_[0]}=$1); - } - return ($Cache{"get_filename"}{$_[0]}=""); -} - -sub get_dirname($) -{ # much faster than dirname() from File::Basename module - if(defined $Cache{"get_dirname"}{$_[0]}) { - return $Cache{"get_dirname"}{$_[0]}; - } - if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) { - return ($Cache{"get_dirname"}{$_[0]}=$1); - } - return ($Cache{"get_dirname"}{$_[0]}=""); -} - -sub separate_path($) { - return (get_dirname($_[0]), get_filename($_[0])); -} - -sub esc($) -{ - my $Str = $_[0]; - $Str=~s/([()\[\]{}$ &'"`;,<>\+])/\\$1/g; - return $Str; -} - -sub readLineNum($$) -{ - my ($Path, $Num) = @_; - return "" if(not $Path or not -f $Path); - open(FILE, $Path); - foreach (1 ... $Num) { - ; - } - my $Line = ; - close(FILE); - return $Line; -} - -sub readAttributes($$) -{ - my ($Path, $Num) = @_; - return () if(not $Path or not -f $Path); - my %Attributes = (); - if(readLineNum($Path, $Num)=~//) - { - foreach my $AttrVal (split(/;/, $1)) - { - if($AttrVal=~/(.+):(.+)/) - { - my ($Name, $Value) = ($1, $2); - $Attributes{$Name} = $Value; - } - } - } - return \%Attributes; -} - -sub is_abs($) { - return ($_[0]=~/\A(\/|\w+:[\/\\])/); -} - -sub get_abs_path($) -{ # abs_path() should NOT be called for absolute inputs - # because it can change them - my $Path = $_[0]; - if(not is_abs($Path)) { - $Path = abs_path($Path); - } - return $Path; -} - -sub get_OSgroup() -{ - my $N = $Config{"osname"}; - if($N=~/macos|darwin|rhapsody/i) { - return "macos"; - } - elsif($N=~/freebsd|openbsd|netbsd/i) { - return "bsd"; - } - elsif($N=~/haiku|beos/i) { - return "beos"; - } - elsif($N=~/symbian|epoc/i) { - return "symbian"; - } - elsif($N=~/win/i) { - return "windows"; - } - else { - return $N; - } -} - -sub getGccVersion($) -{ - my $LibVersion = $_[0]; - if($GCC_VERSION{$LibVersion}) - { # dump version - return $GCC_VERSION{$LibVersion}; - } - elsif($UsedDump{$LibVersion}{"V"}) - { # old-version dumps - return "unknown"; - } - my $GccVersion = get_dumpversion($GCC_PATH); # host version - if(not $GccVersion) { - return "unknown"; - } - return $GccVersion; -} - sub showArch($) { my $Arch = $_[0]; @@ -15328,35 +6277,15 @@ return $Arch; } -sub getArch($) -{ - my $LibVersion = $_[0]; - - if($TargetArch) { - return $TargetArch; - } - elsif($CPU_ARCH{$LibVersion}) - { # dump - return $CPU_ARCH{$LibVersion}; - } - elsif($UsedDump{$LibVersion}{"V"}) - { # old-version dumps - return "unknown"; - } - - return getArch_GCC($LibVersion); -} - -sub get_Report_Title($) +sub getReportTitle($) { my $Level = $_[0]; - my $ArchInfo = " on ".showArch(getArch(1)).""; - if(getArch(1) ne getArch(2) - or getArch(1) eq "unknown" + my $ArchInfo = " on ".showArch($In::ABI{1}{"Arch"}).""; + if($In::ABI{1}{"Arch"} ne $In::ABI{2}{"Arch"} or $Level eq "Source") { # don't show architecture in the header - $ArchInfo=""; + $ArchInfo = ""; } my $Title = ""; if($Level eq "Source") { @@ -15369,13 +6298,13 @@ $Title .= "API compatibility"; } - my $V1 = $Descriptor{1}{"Version"}; - my $V2 = $Descriptor{2}{"Version"}; + my $V1 = $In::Desc{1}{"Version"}; + my $V2 = $In::Desc{2}{"Version"}; if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"}) { - my $M1 = $UsedDump{1}{"M"}; - my $M2 = $UsedDump{2}{"M"}; + my $M1 = $In::ABI{1}{"LibraryName"}; + my $M2 = $In::ABI{2}{"LibraryName"}; my $M1S = $M1; my $M2S = $M2; @@ -15386,7 +6315,7 @@ if($M1S eq $M2S and $V1 ne "X" and $V2 ne "Y") { - $Title .= " report for the $M1S $TargetComponent"; + $Title .= " report for the $M1S ".$In::Opt{"TargetComponent"}; $Title .= " between ".$V1." and ".$V2." versions"; } else @@ -15397,34 +6326,34 @@ } else { - $Title .= " report for the $TargetTitle $TargetComponent"; + $Title .= " report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}; $Title .= " between ".$V1." and ".$V2." versions"; } $Title .= $ArchInfo; - if($AppPath) { - $Title .= " (relating to the portability of application ".get_filename($AppPath).")"; + if($In::Opt{"AppPath"}) { + $Title .= " (relating to the portability of application ".getFilename($In::Opt{"AppPath"}).")"; } $Title = "

".$Title."

\n"; return $Title; } -sub get_CheckedHeaders($) +sub getCheckedHeaders($) { - my $LibVersion = $_[0]; + my $LVer = $_[0]; my @Headers = (); - foreach my $Path (keys(%{$Registered_Headers{$LibVersion}})) + foreach my $Path (keys(%{$In::ABI{$LVer}{"Headers"}})) { - my $File = get_filename($Path); + my $Name = getFilename($Path); - if(not is_target_header($File, $LibVersion)) { + if(not isTargetHeader($Name, $LVer)) { next; } - if(skipHeader($File, $LibVersion)) { + if(skipHeader($Name, $LVer)) { next; } @@ -15434,47 +6363,58 @@ return @Headers; } -sub get_SourceInfo() +sub getSourceInfo() { my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", ""); - if(my @Headers = get_CheckedHeaders(1)) + if(my @Headers = getCheckedHeaders(1)) { - $CheckedHeaders = "

Header Files (".($#Headers+1).")


\n"; + $CheckedHeaders = ""; + if($In::Opt{"OldStyle"}) { + $CheckedHeaders .= "

Header Files (".($#Headers+1).")

"; + } + else { + $CheckedHeaders .= "

Header Files  ".($#Headers+1)." 

"; + } + $CheckedHeaders .= "
\n"; $CheckedHeaders .= "
\n"; - foreach my $Header_Path (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers) - { - my $Identity = $Registered_Headers{1}{$Header_Path}{"Identity"}; - my $Name = get_filename($Identity); - my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":""; - $CheckedHeaders .= $Name.$Comment."
\n"; + foreach my $Path (sort {lc($a) cmp lc($b)} @Headers) { + $CheckedHeaders .= getFilename($Path)."
\n"; } $CheckedHeaders .= "
\n"; $CheckedHeaders .= "
$TOP_REF
\n"; } - if(my @Sources = keys(%{$Registered_Sources{1}})) + if(my @Sources = keys(%{$In::ABI{1}{"Sources"}})) { - $CheckedSources = "

Source Files (".($#Sources+1).")


\n"; + $CheckedSources = ""; + if($In::Opt{"OldStyle"}) { + $CheckedSources .= "

Source Files (".($#Sources+1).")

"; + } + else { + $CheckedSources .= "

Source Files  ".($#Sources+1)." 

"; + } + $CheckedSources .= "
\n"; $CheckedSources .= "
\n"; - foreach my $Header_Path (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources) - { - my $Identity = $Registered_Sources{1}{$Header_Path}{"Identity"}; - my $Name = get_filename($Identity); - my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":""; - $CheckedSources .= $Name.$Comment."
\n"; + foreach my $Path (sort {lc($a) cmp lc($b)} @Sources) { + $CheckedSources .= getFilename($Path)."
\n"; } $CheckedSources .= "
\n"; $CheckedSources .= "
$TOP_REF
\n"; } - if(not $CheckHeadersOnly) + if(not $In::Opt{"CheckHeadersOnly"}) { - $CheckedLibs = "

".get_ObjTitle()." (".keys(%{$Library_Symbol{1}}).")


\n"; + $CheckedLibs = ""; + if($In::Opt{"OldStyle"}) { + $CheckedLibs .= "

".getObjTitle()." (".keys(%{$In::ABI{1}{"Symbols"}}).")

"; + } + else { + $CheckedLibs .= "

".getObjTitle()."  ".keys(%{$In::ABI{1}{"Symbols"}})." 

"; + } + $CheckedLibs .= "
\n"; $CheckedLibs .= "
\n"; - foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$Library_Symbol{1}})) - { - $Library.=" (.$LIB_EXT)" if($Library!~/\.\w+\Z/); + foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{1}{"Symbols"}})) { $CheckedLibs .= $Library."
\n"; } $CheckedLibs .= "
\n"; @@ -15484,50 +6424,46 @@ return $CheckedHeaders.$CheckedSources.$CheckedLibs; } -sub get_ObjTitle() +sub getObjTitle() { if(defined $UsedDump{1}{"DWARF"}) { return "Objects"; } - else { - return ucfirst($SLIB_TYPE)." Libraries"; - } + + return "Libraries"; } -sub get_TypeProblems_Count($$$) +sub getTypeProblemsCount($$) { - my ($TypeChanges, $TargetPriority, $Level) = @_; - my $Type_Problems_Count = 0; - foreach my $Type_Name (sort keys(%{$TypeChanges})) + my ($TargetSeverity, $Level) = @_; + my $Count = 0; + + foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}})) { my %Kinds_Target = (); - foreach my $Kind (keys(%{$TypeChanges->{$Type_Name}})) + foreach my $Kind (keys(%{$TypeChanges{$Level}{$Type_Name}})) { - foreach my $Location (keys(%{$TypeChanges->{$Type_Name}{$Kind}})) + if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) { + next; + } + + foreach my $Loc (keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}})) { - my $Target = $TypeChanges->{$Type_Name}{$Kind}{$Location}{"Target"}; - my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; - next if($Severity ne $TargetPriority); + my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Loc}{"Target"}; + if($Kinds_Target{$Kind}{$Target}) { next; } - if(my $MaxSeverity = $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target}) - { - if($Severity_Val{$MaxSeverity}>$Severity_Val{$Severity}) - { # select a problem with the highest priority - next; - } - } $Kinds_Target{$Kind}{$Target} = 1; - $Type_Problems_Count += 1; + $Count += 1; } } } - return $Type_Problems_Count; + return $Count; } -sub get_Summary($) +sub getSummary($) { my $Level = $_[0]; my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High, @@ -15535,11 +6471,11 @@ %{$RESULT{$Level}} = ( "Problems"=>0, "Warnings"=>0, - "Affected"=>0 ); + "Affected"=>0); # check rules - foreach my $Interface (sort keys(%{$CompatProblems{$Level}})) + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) { - foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}})) + foreach my $Kind (keys(%{$CompatProblems{$Level}{$Symbol}})) { if(not defined $CompatRules{$Level}{$Kind}) { # unknown rule @@ -15548,7 +6484,7 @@ printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")"); $UnknownRules{$Level}{$Kind}=1; } - delete($CompatProblems{$Level}{$Interface}{$Kind}); + delete($CompatProblems{$Level}{$Symbol}{$Kind}); } } } @@ -15567,22 +6503,22 @@ } } } - foreach my $Interface (sort keys(%{$CompatProblems{$Level}})) + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) { - foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}})) + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) { if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols") { - foreach my $Location (sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}})) + my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; + foreach my $Loc (sort keys(%{$CompatProblems{$Level}{$Symbol}{$Kind}})) { - my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; if($Kind eq "Added_Symbol") { $Added += 1; } elsif($Kind eq "Removed_Symbol") { $Removed += 1; - $TotalAffected{$Level}{$Interface} = $Severity; + $TotalAffected{$Level}{$Symbol} = $Severity; } else { @@ -15598,74 +6534,80 @@ elsif($Severity eq "Low") { $I_Problems_Low += 1; } - if(($Severity ne "Low" or $StrictCompat) + if(($Severity ne "Low" or $In::Opt{"StrictCompat"}) and $Severity ne "Safe") { - $TotalAffected{$Level}{$Interface} = $Severity; + $TotalAffected{$Level}{$Symbol} = $Severity; } } } } } } - my %TypeChanges = (); - foreach my $Interface (sort keys(%{$CompatProblems{$Level}})) + + my %MethodTypeIndex = (); + + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) { - foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}})) + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) { if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types") { - foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}})) + my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; + if(($Severity ne "Low" or $In::Opt{"StrictCompat"}) + and $Severity ne "Safe") { - my $Type_Name = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"}; - my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"}; - my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; - my $MaxSeverity = $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target}; - - if($MaxSeverity and $Severity_Val{$MaxSeverity}>$Severity_Val{$Severity}) - { # select a problem with the highest priority - next; - } - - if(($Severity ne "Low" or $StrictCompat) - and $Severity ne "Safe") + if(my $Sev = $TotalAffected{$Level}{$Symbol}) { - if(defined $TotalAffected{$Level}{$Interface}) - { - if($Severity_Val{$Severity}>$Severity_Val{$TotalAffected{$Level}{$Interface}}) { - $TotalAffected{$Level}{$Interface} = $Severity; - } - } - else { - $TotalAffected{$Level}{$Interface} = $Severity; + if($Severity_Val{$Severity}>$Severity_Val{$Sev}) { + $TotalAffected{$Level}{$Symbol} = $Severity; } } - - $TypeChanges{$Type_Name}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}; - - if($MaxSeverity) - { - if($Severity_Val{$Severity}>$Severity_Val{$MaxSeverity}) { - $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target} = $Severity; - } + else { + $TotalAffected{$Level}{$Symbol} = $Severity; + } + } + + my $LSK = $CompatProblems{$Level}{$Symbol}{$Kind}; + my (@Locs1, @Locs2) = (); + foreach my $Loc (sort keys(%{$LSK})) + { + if(index($Loc, "retval")==0 or index($Loc, "this")==0) { + push(@Locs2, $Loc); } else { - $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target} = $Severity; + push(@Locs1, $Loc); + } + } + + foreach my $Loc (@Locs1, @Locs2) + { + my $Type = $LSK->{$Loc}{"Type_Name"}; + my $Target = $LSK->{$Loc}{"Target"}; + + if(defined $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target}) + { # one location for one type and target + next; } + $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target} = 1; + + $TypeChanges{$Level}{$Type}{$Kind}{$Loc} = $LSK->{$Loc}; + $TypeProblemsIndex{$Level}{$Type}{$Kind}{$Loc}{$Symbol} = 1; } } } } - $T_Problems_High = get_TypeProblems_Count(\%TypeChanges, "High", $Level); - $T_Problems_Medium = get_TypeProblems_Count(\%TypeChanges, "Medium", $Level); - $T_Problems_Low = get_TypeProblems_Count(\%TypeChanges, "Low", $Level); - $T_Other = get_TypeProblems_Count(\%TypeChanges, "Safe", $Level); + # clean memory + %MethodTypeIndex = (); - %TypeChanges = (); # free memory + $T_Problems_High = getTypeProblemsCount("High", $Level); + $T_Problems_Medium = getTypeProblemsCount("Medium", $Level); + $T_Problems_Low = getTypeProblemsCount("Low", $Level); + $T_Other = getTypeProblemsCount("Safe", $Level); # changed and removed public symbols my $SCount = keys(%{$CheckedSymbols{$Level}}); - if($ExtendedCheck) + if($In::Opt{"ExtendedCheck"}) { # don't count external_func_0 for constants $SCount-=1; } @@ -15685,7 +6627,7 @@ $RESULT{$Level}{"Affected"} = 0; } - $RESULT{$Level}{"Affected"} = show_number($RESULT{$Level}{"Affected"}); + $RESULT{$Level}{"Affected"} = showNum($RESULT{$Level}{"Affected"}); if($RESULT{$Level}{"Affected"}>=100) { $RESULT{$Level}{"Affected"} = 100; } @@ -15693,7 +6635,7 @@ $RESULT{$Level}{"Problems"} += $Removed; $RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High; $RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium; - if($StrictCompat) { + if($In::Opt{"StrictCompat"}) { $RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low; } else { @@ -15718,7 +6660,7 @@ if($C_Problems_Low) { - if($StrictCompat) { + if($In::Opt{"StrictCompat"}) { $RESULT{$Level}{"Problems"} += $C_Problems_Low; } else { @@ -15739,62 +6681,75 @@ $TotalTypes = keys(%{$TName_Tid{1}}); } - my ($Arch1, $Arch2) = (getArch(1), getArch(2)); - my ($GccV1, $GccV2) = (getGccVersion(1), getGccVersion(2)); + my $TotalSymbols = keys(%{$CheckedSymbols{$Level}}); + + if($In::Opt{"ExtendedCheck"}) { + $TotalSymbols -= keys(%ExtendedSymbols); + } + + my $AnyChanged = ($Added or $Removed or $I_Problems_High or $I_Problems_Medium or $I_Problems_Low or $T_Problems_High or + $C_Problems_Low or $T_Problems_Medium or $T_Problems_Low or $I_Other or $T_Other or $C_Other); + + my ($Arch1, $Arch2) = ($In::ABI{1}{"Arch"}, $In::ABI{2}{"Arch"}); + my ($GccV1, $GccV2) = ($In::ABI{1}{"GccVersion"}, $In::ABI{2}{"GccVersion"}); + my ($ClangV1, $ClangV2) = ($In::ABI{1}{"ClangVersion"}, $In::ABI{2}{"ClangVersion"}); my ($TestInfo, $TestResults, $Problem_Summary) = (); - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML # test info - $TestInfo .= " $TargetLibraryName\n"; + $TestInfo .= " ".$In::Opt{"TargetLib"}."\n"; $TestInfo .= " \n"; - $TestInfo .= " ".$Descriptor{1}{"Version"}."\n"; + $TestInfo .= " ".$In::Desc{1}{"Version"}."\n"; $TestInfo .= " $Arch1\n"; - $TestInfo .= " $GccV1\n"; + if($GccV1) { + $TestInfo .= " $GccV1\n"; + } + elsif($ClangV1) { + $TestInfo .= " $ClangV1\n"; + } $TestInfo .= " \n"; $TestInfo .= " \n"; - $TestInfo .= " ".$Descriptor{2}{"Version"}."\n"; + $TestInfo .= " ".$In::Desc{2}{"Version"}."\n"; $TestInfo .= " $Arch2\n"; - $TestInfo .= " $GccV2\n"; + if($GccV2) { + $TestInfo .= " $GccV2\n"; + } + elsif($ClangV2) { + $TestInfo .= " $ClangV2\n"; + } $TestInfo .= " \n"; $TestInfo = "\n".$TestInfo."\n\n"; # test results - if(my @Headers = keys(%{$Registered_Headers{1}})) + if(my @Headers = keys(%{$In::ABI{1}{"Headers"}})) { $TestResults .= " \n"; - foreach my $Name (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers) - { - my $Identity = $Registered_Headers{1}{$Name}{"Identity"}; - my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":""; - $TestResults .= " ".get_filename($Name).$Comment."\n"; + foreach my $Name (sort {lc($a) cmp lc($b)} @Headers) { + $TestResults .= " ".getFilename($Name)."\n"; } $TestResults .= " \n"; } - if(my @Sources = keys(%{$Registered_Sources{1}})) + if(my @Sources = keys(%{$In::ABI{1}{"Sources"}})) { $TestResults .= " \n"; - foreach my $Name (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources) - { - my $Identity = $Registered_Sources{1}{$Name}{"Identity"}; - my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":""; - $TestResults .= " ".get_filename($Name).$Comment."\n"; + foreach my $Name (sort {lc($a) cmp lc($b)} @Sources) { + $TestResults .= " ".getFilename($Name)."\n"; } $TestResults .= " \n"; } $TestResults .= " \n"; - foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$Library_Symbol{1}})) + foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{1}{"Symbols"}})) { - $Library.=" (.$LIB_EXT)" if($Library!~/\.\w+\Z/); $TestResults .= " $Library\n"; } $TestResults .= " \n"; - $TestResults .= " ".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))."\n"; + $TestResults .= " ".$TotalSymbols."\n"; $TestResults .= " ".$TotalTypes."\n"; $TestResults .= " ".$RESULT{$Level}{"Verdict"}."\n"; @@ -15825,7 +6780,7 @@ $Problem_Summary = "\n".$Problem_Summary."\n\n"; - return ($TestInfo.$TestResults.$Problem_Summary, ""); + return ($TestInfo.$TestResults.$Problem_Summary, "", $AnyChanged); } else { # HTML @@ -15833,57 +6788,78 @@ $TestInfo = "

Test Info


\n"; $TestInfo .= "\n"; - if($TargetComponent eq "library") { - $TestInfo .= "\n"; + if($In::Opt{"TargetComponent"} eq "library") { + $TestInfo .= "\n"; } else { - $TestInfo .= "\n"; + $TestInfo .= "\n"; } my (@VInf1, @VInf2, $AddTestInfo) = (); - if($Arch1 ne "unknown" - and $Arch2 ne "unknown") - { # CPU arch - if($Arch1 eq $Arch2) - { # go to the separate section - $AddTestInfo .= "\n"; - } - else - { # go to the version number - push(@VInf1, showArch($Arch1)); - push(@VInf2, showArch($Arch2)); - } + + # CPU arch + if($Arch1 eq $Arch2) + { # go to the separate section + $AddTestInfo .= "\n"; } - if($GccV1 ne "unknown" - and $GccV2 ne "unknown" - and $OStarget ne "windows") - { # GCC version - if($GccV1 eq $GccV2) - { # go to the separate section - $AddTestInfo .= "\n"; + else + { # go to the version number + push(@VInf1, showArch($Arch1)); + push(@VInf2, showArch($Arch2)); + } + + if($Level eq "Binary" + and $In::Opt{"Target"} ne "windows") + { + if($GccV1 and $GccV2) + { # GCC version + if($GccV1 eq $GccV2) + { # go to the separate section + $AddTestInfo .= "\n"; + } + else + { # go to the version number + push(@VInf1, "gcc ".$GccV1); + push(@VInf2, "gcc ".$GccV2); + } } - else - { # go to the version number + elsif($ClangV1 and $ClangV2) + { # Clang version + if($ClangV1 eq $ClangV2) + { # go to the separate section + $AddTestInfo .= "\n"; + } + else + { # go to the version number + push(@VInf1, "clang ".$ClangV1); + push(@VInf2, "clang ".$ClangV2); + } + } + elsif($GccV1 and $ClangV2) + { push(@VInf1, "gcc ".$GccV1); + push(@VInf2, "clang ".$ClangV2); + } + elsif($ClangV1 and $GccV2) + { + push(@VInf1, "clang ".$ClangV1); push(@VInf2, "gcc ".$GccV2); } } # show long version names with GCC version and CPU architecture name (if different) - $TestInfo .= "\n"; - $TestInfo .= "\n"; + $TestInfo .= "\n"; + $TestInfo .= "\n"; $TestInfo .= $AddTestInfo; - #if($COMMON_LANGUAGE{1}) { - # $TestInfo .= "\n"; - #} - if($ExtendedCheck) { + + if($In::Opt{"ExtendedCheck"}) { $TestInfo .= "\n"; } - if($JoinReport) + if($In::Opt{"JoinReport"}) { if($Level eq "Binary") { $TestInfo .= "\n"; # Run-time } - if($Level eq "Source") { + elsif($Level eq "Source") { $TestInfo .= "\n"; # Build-time } } @@ -15893,37 +6869,49 @@ $TestResults = "

Test Results


\n"; $TestResults .= "
Library Name$TargetTitle
Library Name".$In::Opt{"TargetTitle"}."
Module Name$TargetTitle
Module Name".$In::Opt{"TargetTitle"}."
CPU Type".showArch($Arch1)."
Arch".showArch($Arch1)."
GCC Version$GccV1
GCC Version$GccV1
Clang Version$ClangV1
Version #1".$Descriptor{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."
Version #2".$Descriptor{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."
Version #1".$In::Desc{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."
Version #2".$In::Desc{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."
Language".$COMMON_LANGUAGE{1}."
ModeExtended
SubjectBinary Compatibility
SubjectSource Compatibility
"; - if(my @Headers = get_CheckedHeaders(1)) + if(my @Headers = getCheckedHeaders(1)) { my $Headers_Link = "".($#Headers + 1).""; $TestResults .= "\n"; } - if(my @Sources = keys(%{$Registered_Sources{1}})) + if(my @Sources = keys(%{$In::ABI{1}{"Sources"}})) { my $Src_Link = "".($#Sources + 1).""; $TestResults .= "\n"; } - if(not $ExtendedCheck) + if(not $In::Opt{"ExtendedCheck"}) { my $Libs_Link = "0"; - $Libs_Link = "".keys(%{$Library_Symbol{1}})."" if(keys(%{$Library_Symbol{1}})>0); - $TestResults .= "\n"; + $Libs_Link = "".keys(%{$In::ABI{1}{"Symbols"}})."" if(keys(%{$In::ABI{1}{"Symbols"}})>0); + $TestResults .= "\n"; } - $TestResults .= "\n"; + $TestResults .= "\n"; my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";"; - if($JoinReport) { + if($In::Opt{"JoinReport"}) { $META_DATA = "kind:".lc($Level).";".$META_DATA; } - $TestResults .= ""; - if($RESULT{$Level}{"Verdict"} eq "incompatible") { - $TestResults .= ""; + + my $BC_Rate = showNum(100 - $RESULT{$Level}{"Affected"}); + + $TestResults .= "\n"; + if($RESULT{$Level}{"Verdict"} eq "incompatible") + { + my $Cl = "incompatible"; + if($BC_Rate>=90) { + $Cl = "warning"; + } + elsif($BC_Rate>=80) { + $Cl = "almost_compatible"; + } + + $TestResults .= "\n"; } else { - $TestResults .= ""; + $TestResults .= "\n"; } $TestResults .= "\n"; $TestResults .= "
Total Header Files".$Headers_Link."
Total Source Files".$Src_Link."
Total ".get_ObjTitle()."".($CheckHeadersOnly?"0 (not analyzed)":$Libs_Link)."
Total ".getObjTitle()."".($In::Opt{"CheckHeadersOnly"}?"0 (not analyzed)":$Libs_Link)."
Total Symbols / Types".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))." / ".$TotalTypes."
Total Symbols / Types".$TotalSymbols." / ".$TotalTypes."
VerdictIncompatible
(".$RESULT{$Level}{"Affected"}."%)
Compatibility".$BC_Rate."%Compatible100%
\n"; @@ -15937,7 +6925,7 @@ my $Added_Link = "0"; if($Added>0) { - if($JoinReport) { + if($In::Opt{"JoinReport"}) { $Added_Link = "$Added"; } else { @@ -15945,12 +6933,12 @@ } } $META_DATA .= "added:$Added;"; - $Problem_Summary .= "Added Symbols-$Added_Link\n"; + $Problem_Summary .= "Added Symbols-$Added_Link\n"; my $Removed_Link = "0"; if($Removed>0) { - if($JoinReport) { + if($In::Opt{"JoinReport"}) { $Removed_Link = "$Removed" } else { @@ -15959,73 +6947,73 @@ } $META_DATA .= "removed:$Removed;"; $Problem_Summary .= "Removed Symbols"; - $Problem_Summary .= "High$Removed_Link\n"; + $Problem_Summary .= "High$Removed_Link\n"; my $TH_Link = "0"; - $TH_Link = "$T_Problems_High" if($T_Problems_High>0); + $TH_Link = "$T_Problems_High" if($T_Problems_High>0); $META_DATA .= "type_problems_high:$T_Problems_High;"; $Problem_Summary .= "Problems with
Data Types"; - $Problem_Summary .= "High$TH_Link\n"; + $Problem_Summary .= "High$TH_Link\n"; my $TM_Link = "0"; - $TM_Link = "$T_Problems_Medium" if($T_Problems_Medium>0); + $TM_Link = "$T_Problems_Medium" if($T_Problems_Medium>0); $META_DATA .= "type_problems_medium:$T_Problems_Medium;"; - $Problem_Summary .= "Medium$TM_Link\n"; + $Problem_Summary .= "Medium$TM_Link\n"; my $TL_Link = "0"; - $TL_Link = "$T_Problems_Low" if($T_Problems_Low>0); + $TL_Link = "$T_Problems_Low" if($T_Problems_Low>0); $META_DATA .= "type_problems_low:$T_Problems_Low;"; - $Problem_Summary .= "Low$TL_Link\n"; + $Problem_Summary .= "Low$TL_Link\n"; my $IH_Link = "0"; - $IH_Link = "$I_Problems_High" if($I_Problems_High>0); + $IH_Link = "$I_Problems_High" if($I_Problems_High>0); $META_DATA .= "interface_problems_high:$I_Problems_High;"; $Problem_Summary .= "Problems with
Symbols"; - $Problem_Summary .= "High$IH_Link\n"; + $Problem_Summary .= "High$IH_Link\n"; my $IM_Link = "0"; - $IM_Link = "$I_Problems_Medium" if($I_Problems_Medium>0); + $IM_Link = "$I_Problems_Medium" if($I_Problems_Medium>0); $META_DATA .= "interface_problems_medium:$I_Problems_Medium;"; - $Problem_Summary .= "Medium$IM_Link\n"; + $Problem_Summary .= "Medium$IM_Link\n"; my $IL_Link = "0"; - $IL_Link = "$I_Problems_Low" if($I_Problems_Low>0); + $IL_Link = "$I_Problems_Low" if($I_Problems_Low>0); $META_DATA .= "interface_problems_low:$I_Problems_Low;"; - $Problem_Summary .= "Low$IL_Link\n"; + $Problem_Summary .= "Low$IL_Link\n"; my $ChangedConstants_Link = "0"; if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) { - $ChangedConstants_Link = "$C_Problems_Low"; + $ChangedConstants_Link = "$C_Problems_Low"; } $META_DATA .= "changed_constants:$C_Problems_Low;"; - $Problem_Summary .= "Problems with
ConstantsLow$ChangedConstants_Link\n"; + $Problem_Summary .= "Problems with
ConstantsLow$ChangedConstants_Link\n"; # Safe Changes if($T_Other) { - my $TS_Link = "$T_Other"; - $Problem_Summary .= "Other Changes
in Data Types-$TS_Link\n"; + my $TS_Link = "$T_Other"; + $Problem_Summary .= "Other Changes
in Data Types-$TS_Link\n"; $META_DATA .= "type_changes_other:$T_Other;"; } if($I_Other) { - my $IS_Link = "$I_Other"; - $Problem_Summary .= "Other Changes
in Symbols-$IS_Link\n"; + my $IS_Link = "$I_Other"; + $Problem_Summary .= "Other Changes
in Symbols-$IS_Link\n"; $META_DATA .= "interface_changes_other:$I_Other;"; } if($C_Other) { - my $CS_Link = "$C_Other"; - $Problem_Summary .= "Other Changes
in Constants-$CS_Link\n"; + my $CS_Link = "$C_Other"; + $Problem_Summary .= "Other Changes
in Constants-$CS_Link\n"; $META_DATA .= "constant_changes_other:$C_Other;"; } $META_DATA .= "tool_version:$TOOL_VERSION"; $Problem_Summary .= "\n"; - # $TestInfo = getLegend().$TestInfo; - return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA); + + return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA, $AnyChanged); } } @@ -16033,69 +7021,22 @@ { my ($Subj, $Act, $Num) = @_; my %Style = ( - "A"=>"new", - "R"=>"failed", - "S"=>"passed", - "L"=>"warning", - "M"=>"failed", - "H"=>"failed" + "Added"=>"new", + "Removed"=>"failed", + "Safe"=>"passed", + "Low"=>"warning", + "Medium"=>"failed", + "High"=>"failed" ); + if($Num>0) { return " class='".$Style{$Act}."'"; } + return ""; } -sub show_number($) -{ - if($_[0]) - { - my $Num = cut_off_number($_[0], 2, 0); - if($Num eq "0") - { - foreach my $P (3 .. 7) - { - $Num = cut_off_number($_[0], $P, 1); - if($Num ne "0") { - last; - } - } - } - if($Num eq "0") { - $Num = $_[0]; - } - return $Num; - } - return $_[0]; -} - -sub cut_off_number($$$) -{ - my ($num, $digs_to_cut, $z) = @_; - if($num!~/\./) - { - $num .= "."; - foreach (1 .. $digs_to_cut-1) { - $num .= "0"; - } - } - elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1) - { - foreach (1 .. $digs_to_cut - 1 - length($1)) { - $num .= "0"; - } - } - elsif($num=~/\d+\.(\d){$digs_to_cut,}/) { - $num=sprintf("%.".($digs_to_cut-1)."f", $num); - } - $num=~s/\.[0]+\Z//g; - if($z) { - $num=~s/(\.[1-9]+)[0]+\Z/$1/g; - } - return $num; -} - -sub get_Report_ChangedConstants($$) +sub getReportChangedConstants($$) { my ($TargetSeverity, $Level) = @_; my $CHANGED_CONSTANTS = ""; @@ -16104,9 +7045,15 @@ foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}})) { my $Header = $Constants{1}{$Constant}{"Header"}; + if(not $Header) { + $Header = $Constants{1}{$Constant}{"Source"}; + } if(not $Header) { # added - $Header = $Constants{2}{$Constant}{"Header"} + $Header = $Constants{2}{$Constant}{"Header"}; + if(not $Header) { + $Header = $Constants{2}{$Constant}{"Source"} + } } foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}})) @@ -16121,7 +7068,7 @@ } } - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) { @@ -16151,7 +7098,7 @@ } else { # HTML - my $Number = 0; + my $ProblemsNum = 0; foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) { $CHANGED_CONSTANTS .= "$HeaderName
\n"; @@ -16163,13 +7110,13 @@ { my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind}); my $Effect = $CompatRules{$Level}{$Kind}{"Effect"}; - $Report .= "\n1\n".$Change."\n$Effect\n\n"; - $Number += 1; + $Report .= "\n1\n".$Change."\n$Effect\n\n"; + $ProblemsNum += 1; } if($Report) { - $Report = $ContentDivStart."\n\n\n\n\n\n".$Report."
ChangeEffect
\n
\n$ContentDivEnd\n"; - $Report = $ContentSpanStart."[+] ".$Constant.$ContentSpanEnd."
\n".$Report; + $Report = $ContentDivStart."\n\n\n\n\n\n".$Report."
ChangeEffect
\n
\n$ContentDivEnd\n"; + $Report = $ContentSpanStart."[+] ".$Constant.$ContentSpanEnd."
\n".$Report; $Report = insertIDs($Report); } $CHANGED_CONSTANTS .= $Report; @@ -16183,7 +7130,13 @@ { # Safe Changes $Title = "Other Changes in Constants"; } - $CHANGED_CONSTANTS = "

$Title ($Number)


\n".$CHANGED_CONSTANTS.$TOP_REF."
\n"; + if($In::Opt{"OldStyle"}) { + $CHANGED_CONSTANTS = "

$Title ($ProblemsNum)


\n".$CHANGED_CONSTANTS; + } + else { + $CHANGED_CONSTANTS = "

$Title  $ProblemsNum 


\n".$CHANGED_CONSTANTS; + } + $CHANGED_CONSTANTS = "\n".$CHANGED_CONSTANTS.$TOP_REF."
\n"; } } return $CHANGED_CONSTANTS; @@ -16193,9 +7146,7 @@ { my ($Header, $Library, $NameSpace) = @_; my $Title = ""; - if($Library and $Library!~/\.\w+\Z/) { - $Library .= " (.$LIB_EXT)"; - } + if($Header and $Library) { $Title .= "$Header"; @@ -16207,34 +7158,40 @@ elsif($Header) { $Title .= "$Header
\n"; } + if($NameSpace) { $Title .= "namespace $NameSpace
\n"; } + return $Title; } -sub get_Report_Added($) +sub getReportAdded($) { my $Level = $_[0]; my $ADDED_INTERFACES = ""; my %ReportMap = (); - foreach my $Interface (sort keys(%{$CompatProblems{$Level}})) + foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) { - foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}})) + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) { if($Kind eq "Added_Symbol") { - my $HeaderName = $CompleteSignature{2}{$Interface}{"Header"}; - my $DyLib = $Symbol_Library{2}{$Interface}; - if($Level eq "Source" and $ReportFormat eq "html") - { # do not show library name in HTML report + my $HeaderName = $CompSign{2}{$Symbol}{"Header"}; + if(not $HeaderName) { + $HeaderName = $CompSign{2}{$Symbol}{"Source"}; + } + + my $DyLib = $In::ABI{2}{"SymLib"}{$Symbol}; + if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html") + { # do not show library name in the HTML report $DyLib = ""; } - $ReportMap{$HeaderName}{$DyLib}{$Interface} = 1; + $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1; } } } - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) { @@ -16242,8 +7199,8 @@ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) { $ADDED_INTERFACES .= " \n"; - foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { - $ADDED_INTERFACES .= " $Interface\n"; + foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { + $ADDED_INTERFACES .= " $Symbol\n"; } $ADDED_INTERFACES .= " \n"; } @@ -16259,37 +7216,26 @@ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) { my %NameSpaceSymbols = (); - foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { - $NameSpaceSymbols{select_Symbol_NS($Interface, 2)}{$Interface} = 1; + foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { + $NameSpaceSymbols{selectSymbolNs($Symbol, 2)}{$Symbol} = 1; } foreach my $NameSpace (sort keys(%NameSpaceSymbols)) { $ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace); - my @SortedInterfaces = sort {lc(get_Signature($a, 2)) cmp lc(get_Signature($b, 2))} keys(%{$NameSpaceSymbols{$NameSpace}}); - foreach my $Interface (@SortedInterfaces) + my @SortedInterfaces = sort {lc($CompSign{2}{$a}{"Unmangled"}) cmp lc($CompSign{2}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}}); + foreach my $Symbol (@SortedInterfaces) { $Added_Number += 1; - my $Signature = get_Signature($Interface, 2); + my $Signature = highLight_ItalicColor($Symbol, 2); if($NameSpace) { - $Signature=~s/\b\Q$NameSpace\E::\b//g; + $Signature = cutNs($Signature, $NameSpace); } - if($Interface=~/\A(_Z|\?)/) - { - if($Signature) { - $ADDED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."
\n".$ContentDivStart."[symbol: $Interface]\n
\n
\n".$ContentDivEnd."\n"); - } - else { - $ADDED_INTERFACES .= "".$Interface."
\n"; - } + + if($Symbol=~/\A(_Z|\?)/) { + $ADDED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol\n
\n
\n".$ContentDivEnd."\n"); } - else - { - if($Signature) { - $ADDED_INTERFACES .= "".highLight_Signature_Italic_Color($Signature)."
\n"; - } - else { - $ADDED_INTERFACES .= "".$Interface."
\n"; - } + else { + $ADDED_INTERFACES .= "".$Signature."
\n"; } } $ADDED_INTERFACES .= "
\n"; @@ -16299,16 +7245,22 @@ if($ADDED_INTERFACES) { my $Anchor = ""; - if($JoinReport) { + if($In::Opt{"JoinReport"}) { $Anchor = ""; } - $ADDED_INTERFACES = $Anchor."

Added Symbols ($Added_Number)


\n".$ADDED_INTERFACES.$TOP_REF."
\n"; + if($In::Opt{"OldStyle"}) { + $ADDED_INTERFACES = "

Added Symbols ($Added_Number)


\n".$ADDED_INTERFACES; + } + else { + $ADDED_INTERFACES = "

Added Symbols  $Added_Number 


\n".$ADDED_INTERFACES; + } + $ADDED_INTERFACES = $Anchor.$ADDED_INTERFACES.$TOP_REF."
\n"; } } return $ADDED_INTERFACES; } -sub get_Report_Removed($) +sub getReportRemoved($) { my $Level = $_[0]; my $REMOVED_INTERFACES = ""; @@ -16319,9 +7271,13 @@ { if($Kind eq "Removed_Symbol") { - my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"}; - my $DyLib = $Symbol_Library{1}{$Symbol}; - if($Level eq "Source" and $ReportFormat eq "html") + my $HeaderName = $CompSign{1}{$Symbol}{"Header"}; + if(not $HeaderName) { + $HeaderName = $CompSign{1}{$Symbol}{"Source"}; + } + + my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol}; + if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html") { # do not show library name in HTML report $DyLib = ""; } @@ -16329,7 +7285,7 @@ } } } - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) { @@ -16355,37 +7311,24 @@ { my %NameSpaceSymbols = (); foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { - $NameSpaceSymbols{select_Symbol_NS($Interface, 1)}{$Interface} = 1; + $NameSpaceSymbols{selectSymbolNs($Interface, 1)}{$Interface} = 1; } foreach my $NameSpace (sort keys(%NameSpaceSymbols)) { $REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace); - my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NameSpaceSymbols{$NameSpace}}); + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}}); foreach my $Symbol (@SortedInterfaces) { $Removed_Number += 1; - my $SubReport = ""; - my $Signature = get_Signature($Symbol, 1); + my $Signature = highLight_ItalicColor($Symbol, 1); if($NameSpace) { - $Signature=~s/\b\Q$NameSpace\E::\b//g; + $Signature = cutNs($Signature, $NameSpace); } - if($Symbol=~/\A(_Z|\?)/) - { - if($Signature) { - $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."
\n".$ContentDivStart."[symbol: $Symbol]\n
\n
\n".$ContentDivEnd."\n"); - } - else { - $REMOVED_INTERFACES .= "".$Symbol."
\n"; - } + if($Symbol=~/\A(_Z|\?)/) { + $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol\n
\n
\n".$ContentDivEnd."\n"); } - else - { - if($Signature) { - $REMOVED_INTERFACES .= "".highLight_Signature_Italic_Color($Signature)."
\n"; - } - else { - $REMOVED_INTERFACES .= "".$Symbol."
\n"; - } + else { + $REMOVED_INTERFACES .= "".$Signature."
\n"; } } } @@ -16395,10 +7338,17 @@ if($REMOVED_INTERFACES) { my $Anchor = ""; - if($JoinReport) { + if($In::Opt{"JoinReport"}) { $Anchor = ""; } - $REMOVED_INTERFACES = $Anchor."

Removed Symbols ($Removed_Number)


\n".$REMOVED_INTERFACES.$TOP_REF."
\n"; + if($In::Opt{"OldStyle"}) { + $REMOVED_INTERFACES = "

Removed Symbols ($Removed_Number)


\n".$REMOVED_INTERFACES; + } + else { + $REMOVED_INTERFACES = "

Removed Symbols  $Removed_Number 


\n".$REMOVED_INTERFACES; + } + + $REMOVED_INTERFACES = $Anchor.$REMOVED_INTERFACES.$TOP_REF."
\n"; } } return $REMOVED_INTERFACES; @@ -16407,13 +7357,20 @@ sub getXmlParams($$) { my ($Content, $Problem) = @_; - return "" if(not $Content or not $Problem); + my %XMLparams = (); foreach my $Attr (sort {$b cmp $a} keys(%{$Problem})) { my $Macro = "\@".lc($Attr); - if($Content=~/\Q$Macro\E/) { - $XMLparams{lc($Attr)} = $Problem->{$Attr}; + if($Content=~/\Q$Macro\E/) + { + my $Value = $Problem->{$Attr}; + + if($Attr eq "Param_Pos") { + $Value = showPos($Value); + } + + $XMLparams{lc($Attr)} = $Value; } } my @PString = (); @@ -16423,28 +7380,29 @@ if(@PString) { return " ".join(" ", @PString); } - else { - return ""; - } + return ""; } sub addMarkup($) { my $Content = $_[0]; + # auto-markup $Content=~s/\n[ ]*//; # spaces $Content=~s!(\@\w+\s*\(\@\w+\))!$1!g; # @old_type (@old_size) $Content=~s!(... \(\w+\))!$1!g; # ... (va_list) $Content=~s!(.+?)!$1!g; $Content=~s!([2-9]\))!
$1!g; # 1), 2), ... + if($Content=~/\ANOTE:/) { # notes $Content=~s!(NOTE):!$1:!g; } else { - $Content=~s!(NOTE):!
$1:!g; + $Content=~s!(NOTE):!

$1:!g; } $Content=~s! (out)-! $1-!g; # out-parameters + my @Keywords = ( "void", "const", @@ -16461,17 +7419,19 @@ $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1$2$3!ig; # intrinsic types, modifiers # Markdown - $Content=~s!\*\*([\w\-]+)\*\*!$1!ig; - $Content=~s!\*([\w\-]+)\*!$1!ig; + $Content=~s!\*\*([\w\-]+?)\*\*!$1!ig; + $Content=~s!\*([\w\-]+?)\*!$1!ig; + return $Content; } sub applyMacroses($$$$) { my ($Level, $Kind, $Content, $Problem) = @_; - return "" if(not $Content or not $Problem); - $Problem->{"Word_Size"} = $WORD_SIZE{2}; + + $Problem->{"Word_Size"} = $In::ABI{2}{"WordSize"}; $Content = addMarkup($Content); + # macros foreach my $Attr (sort {$b cmp $a} keys(%{$Problem})) { @@ -16486,21 +7446,17 @@ next; } - if($Kind!~/\A(Changed|Added|Removed)_Constant\Z/ - and $Kind!~/_Type_/ - and $Value=~/\s\(/ and $Value!~/['"]/) - { # functions - $Value=~s/\s*\[[\w\-]+\]//g; # remove quals - $Value=~s/\s[a-z]\w*(\)|,)/$1/ig; # remove parameter names - $Value = black_name($Value); + if($Attr eq "Param_Pos") { + $Value = showPos($Value); } - elsif($Value=~/\s/) { - $Value = "".htmlSpecChars($Value).""; + + if($Value=~/\s/) { + $Value = "".specChars($Value).""; } elsif($Value=~/\A\d+\Z/ and ($Attr eq "Old_Size" or $Attr eq "New_Size")) { # bits to bytes - if($Value % $BYTE_SIZE) + if($Value % $BYTE) { # bits if($Value==1) { $Value = "".$Value." bit"; @@ -16511,7 +7467,7 @@ } else { # bytes - $Value /= $BYTE_SIZE; + $Value /= $BYTE; if($Value==1) { $Value = "".$Value." byte"; } @@ -16522,7 +7478,28 @@ } else { - $Value = "".htmlSpecChars($Value).""; + my $Fmt = "Class|Name|Qual|HTML|Desc"; + if($Kind!~/Overridden/) { + $Fmt = "Name|Qual|HTML|Desc"; + } + + my $V1 = (defined $CompSign{1}{$Value} and defined $CompSign{1}{$Value}{"ShortName"}); + my $V2 = (defined $CompSign{2}{$Value} and defined $CompSign{2}{$Value}{"ShortName"}); + + if($Kind!~/Symbol_Became|Symbol_Changed|Method_Became/ + and ($V1 or $V2)) + { # symbols + if($V1) { + $Value = blackName(getSignature($Value, 1, $Fmt)); + } + else { + $Value = blackName(getSignature($Value, 2, $Fmt)); + } + } + else + { + $Value = "".specChars($Value).""; + } } $Content=~s/\Q$Macro\E/$Value/g; } @@ -16538,7 +7515,7 @@ return $Content; } -sub get_Report_SymbolProblems($$) +sub getReportSymbolProblems($$) { my ($TargetSeverity, $Level) = @_; my $INTERFACE_PROBLEMS = ""; @@ -16546,51 +7523,51 @@ foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) { - my ($SN, $SS, $SV) = separate_symbol($Symbol); + my ($SN, $SS, $SV) = symbolParts($Symbol); if($SV and defined $CompatProblems{$Level}{$SN}) { next; } + + if(not defined $CompSign{1}{$Symbol}) + { # added symbols + next; + } + + my $HeaderName = $CompSign{1}{$Symbol}{"Header"}; + if(not $HeaderName) { + $HeaderName = $CompSign{1}{$Symbol}{"Source"}; + } + + my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol}; + if(not $DyLib and my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol}) + { # Symbol with Version + $DyLib = $In::ABI{1}{"SymLib"}{$VSym}; + } + if(not $DyLib) + { # const global data + $DyLib = ""; + } + if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html") + { # do not show library name in HTML report + $DyLib = ""; + } + foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}})) { if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols" and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol") { - my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"}; - my $DyLib = $Symbol_Library{1}{$Symbol}; - if(not $DyLib and my $VSym = $SymVer{1}{$Symbol}) - { # Symbol with Version - $DyLib = $Symbol_Library{1}{$VSym}; - } - if(not $DyLib) - { # const global data - $DyLib = ""; - } - if($Level eq "Source" and $ReportFormat eq "html") - { # do not show library name in HTML report - $DyLib = ""; - } - %{$SymbolChanges{$Symbol}{$Kind}} = %{$CompatProblems{$Level}{$Symbol}{$Kind}}; - foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}})) - { - my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; - if($Severity ne $TargetSeverity) { - delete($SymbolChanges{$Symbol}{$Kind}{$Location}); - } - } - if(not keys(%{$SymbolChanges{$Symbol}{$Kind}})) + my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; + if($Severity eq $TargetSeverity) { - delete($SymbolChanges{$Symbol}{$Kind}); - next; + $SymbolChanges{$Symbol}{$Kind} = $CompatProblems{$Level}{$Symbol}{$Kind}; + $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1; } - $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1; } } - if(not keys(%{$SymbolChanges{$Symbol}})) { - delete($SymbolChanges{$Symbol}); - } } - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) { @@ -16598,24 +7575,24 @@ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}})) { $INTERFACE_PROBLEMS .= " \n"; - my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$ReportMap{$HeaderName}{$DyLib}}); + + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$DyLib}}); foreach my $Symbol (@SortedInterfaces) { $INTERFACE_PROBLEMS .= " \n"; - foreach my $Kind (keys(%{$SymbolChanges{$Symbol}})) + foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}})) { - foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}})) + foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}})) { - my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}}; - $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"}); + my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc}; $INTERFACE_PROBLEMS .= " \n"; my $Change = $CompatRules{$Level}{$Kind}{"Change"}; - $INTERFACE_PROBLEMS .= " $Change\n"; + $INTERFACE_PROBLEMS .= " $Change\n"; my $Effect = $CompatRules{$Level}{$Kind}{"Effect"}; - $INTERFACE_PROBLEMS .= " $Effect\n"; + $INTERFACE_PROBLEMS .= " $Effect\n"; if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) { - $INTERFACE_PROBLEMS .= " $Overcome\n"; + $INTERFACE_PROBLEMS .= " $Overcome\n"; } $INTERFACE_PROBLEMS .= " \n"; } @@ -16637,30 +7614,31 @@ { my (%NameSpaceSymbols, %NewSignature) = (); foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) { - $NameSpaceSymbols{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1; + $NameSpaceSymbols{selectSymbolNs($Symbol, 1)}{$Symbol} = 1; } foreach my $NameSpace (sort keys(%NameSpaceSymbols)) { $INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace); - my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} sort keys(%{$NameSpaceSymbols{$NameSpace}}); + + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}}); foreach my $Symbol (@SortedInterfaces) { - my $Signature = get_Signature($Symbol, 1); my $SYMBOL_REPORT = ""; my $ProblemNum = 1; foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}})) { - foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}})) + foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}})) { - my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}}; - $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"}); - if($Problem{"New_Signature"}) { - $NewSignature{$Symbol} = $Problem{"New_Signature"}; + my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc}; + + if(my $NSign = $ProblemAttr->{"New_Signature"}) { + $NewSignature{$Symbol} = $NSign; } - if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem)) + + if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr)) { - my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem); - $SYMBOL_REPORT .= "\n$ProblemNum\n".$Change."\n".$Effect."\n\n"; + my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr); + $SYMBOL_REPORT .= "\n$ProblemNum\n".$Change."\n".$Effect."\n\n"; $ProblemNum += 1; $ProblemsNum += 1; } @@ -16669,27 +7647,39 @@ $ProblemNum -= 1; if($SYMBOL_REPORT) { - $INTERFACE_PROBLEMS .= $ContentSpanStart."[+] "; - if($Signature) { - $INTERFACE_PROBLEMS .= highLight_Signature_Italic_Color($Signature); + my $ShowSymbol = highLight_ItalicColor($Symbol, 1); + + if($NameSpace) + { + $SYMBOL_REPORT = cutNs($SYMBOL_REPORT, $NameSpace); + $ShowSymbol = cutNs($ShowSymbol, $NameSpace); + } + + $INTERFACE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowSymbol; + if($In::Opt{"OldStyle"}) { + $INTERFACE_PROBLEMS .= " ($ProblemNum)"; } else { - $INTERFACE_PROBLEMS .= $Symbol; + $INTERFACE_PROBLEMS .= "  $ProblemNum 
"; } - $INTERFACE_PROBLEMS .= " ($ProblemNum)".$ContentSpanEnd."
\n"; + $INTERFACE_PROBLEMS .= $ContentSpanEnd."
\n"; $INTERFACE_PROBLEMS .= $ContentDivStart."\n"; - if($NewSignature{$Symbol}) + + if(my $NSign = $NewSignature{$Symbol}) { # argument list changed to - $INTERFACE_PROBLEMS .= "\nchanged to:\n
\n".highLight_Signature_Italic_Color($NewSignature{$Symbol})."
\n"; + $NSign = highLight_ItalicColor($NSign, 2); + if($NameSpace) { + $NSign = cutNs($NSign, $NameSpace); + } + $INTERFACE_PROBLEMS .= "\n\n
\n".$NSign."
\n"; } + if($Symbol=~/\A(_Z|\?)/) { - $INTERFACE_PROBLEMS .= "    [symbol: $Symbol]
\n"; + $INTERFACE_PROBLEMS .= "$Symbol
\n"; } - $INTERFACE_PROBLEMS .= "\n\n\n\n\n\n$SYMBOL_REPORT
ChangeEffect
\n
\n"; + + $INTERFACE_PROBLEMS .= "\n\n\n\n\n\n$SYMBOL_REPORT
ChangeEffect
\n
\n"; $INTERFACE_PROBLEMS .= $ContentDivEnd; - if($NameSpace) { - $INTERFACE_PROBLEMS=~s/\b\Q$NameSpace\E::\b//g; - } } } $INTERFACE_PROBLEMS .= "
\n"; @@ -16705,110 +7695,90 @@ { # Safe Changes $Title = "Other Changes in Symbols"; } - $INTERFACE_PROBLEMS = "\n

$Title ($ProblemsNum)


\n".$INTERFACE_PROBLEMS.$TOP_REF."
\n"; + if($In::Opt{"OldStyle"}) { + $INTERFACE_PROBLEMS = "

$Title ($ProblemsNum)


\n".$INTERFACE_PROBLEMS; + } + else { + $INTERFACE_PROBLEMS = "

$Title  $ProblemsNum 


\n".$INTERFACE_PROBLEMS; + } + $INTERFACE_PROBLEMS = "\n".$INTERFACE_PROBLEMS.$TOP_REF."
\n"; } } return $INTERFACE_PROBLEMS; } -sub get_Report_TypeProblems($$) +sub cutNs($$) +{ + my ($N, $Ns) = @_; + $N=~s/\b\Q$Ns\E:://g; + return $N; +} + +sub getReportTypeProblems($$) { my ($TargetSeverity, $Level) = @_; my $TYPE_PROBLEMS = ""; - my (%ReportMap, %TypeChanges) = (); - foreach my $Interface (sort keys(%{$CompatProblems{$Level}})) - { - foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}})) - { - if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types") - { - foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}})) - { - my $TypeName = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"}; - my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"}; - my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; - - if($Severity eq "Safe" - and $TargetSeverity ne "Safe") { - next; - } - - if(my $MaxSeverity = $Type_MaxSeverity{$Level}{$TypeName}{$Kind}{$Target}) - { - if($Severity_Val{$MaxSeverity}>$Severity_Val{$Severity}) - { # select a problem with the highest priority - next; - } - } - - $TypeChanges{$TypeName}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}; - } - } - } - } + my %ReportMap = (); + my %TypeChanges_Sev = (); - my %Kinds_Locations = (); - foreach my $TypeName (keys(%TypeChanges)) + foreach my $TypeName (keys(%{$TypeChanges{$Level}})) { - my %Kind_Target = (); - foreach my $Kind (sort keys(%{$TypeChanges{$TypeName}})) + my $Tid = $TName_Tid{1}{$TypeName}; + my $HeaderName = $TypeInfo{1}{$Tid}{"Header"}; + if(not $HeaderName) { + $HeaderName = $TypeInfo{1}{$Tid}{"Source"}; + } + + foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}})) { - foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}})) + if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) { + next; + } + + foreach my $Loc (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}})) { - my $Severity = $CompatRules{$Level}{$Kind}{"Severity"}; - if($Severity ne $TargetSeverity) - { # other priority - delete($TypeChanges{$TypeName}{$Kind}{$Location}); - next; - } - $Kinds_Locations{$TypeName}{$Kind}{$Location} = 1; - my $Target = $TypeChanges{$TypeName}{$Kind}{$Location}{"Target"}; - if($Kind_Target{$Kind}{$Target}) - { # duplicate target - delete($TypeChanges{$TypeName}{$Kind}{$Location}); - next; - } - $Kind_Target{$Kind}{$Target} = 1; - my $HeaderName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Header"}; $ReportMap{$HeaderName}{$TypeName} = 1; - } - if(not keys(%{$TypeChanges{$TypeName}{$Kind}})) { - delete($TypeChanges{$TypeName}{$Kind}); + $TypeChanges_Sev{$TypeName}{$Kind}{$Loc} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Loc}; } } - if(not keys(%{$TypeChanges{$TypeName}})) { - delete($TypeChanges{$TypeName}); - } } - my @Symbols = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$CompatProblems{$Level}}); - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap)) { $TYPE_PROBLEMS .= "
\n"; foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) { + my (%Kinds_Locations, %Kinds_Target) = (); $TYPE_PROBLEMS .= " \n"; - foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}})) + foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}})) { - foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}})) + foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}})) { - my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}}; + $Kinds_Locations{$Kind}{$Loc} = 1; + + my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"}; + if($Kinds_Target{$Kind}{$Target}) { + next; + } + $Kinds_Target{$Kind}{$Target} = 1; + + my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}; $TYPE_PROBLEMS .= " \n"; my $Change = $CompatRules{$Level}{$Kind}{"Change"}; - $TYPE_PROBLEMS .= " $Change\n"; + $TYPE_PROBLEMS .= " $Change\n"; my $Effect = $CompatRules{$Level}{$Kind}{"Effect"}; - $TYPE_PROBLEMS .= " $Effect\n"; + $TYPE_PROBLEMS .= " $Effect\n"; if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) { - $TYPE_PROBLEMS .= " $Overcome\n"; + $TYPE_PROBLEMS .= " $Overcome\n"; } $TYPE_PROBLEMS .= " \n"; } } - $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, $Kinds_Locations{$TypeName}, \@Symbols); - if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) { + $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, \%Kinds_Locations); + if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) { $TYPE_PROBLEMS .= showVTables($TypeName); } $TYPE_PROBLEMS .= " \n"; @@ -16824,26 +7794,35 @@ { my (%NameSpace_Type) = (); foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) { - $NameSpace_Type{select_Type_NS($TypeName, 1)}{$TypeName} = 1; + $NameSpace_Type{selectTypeNs($TypeName, 1)}{$TypeName} = 1; } foreach my $NameSpace (sort keys(%NameSpace_Type)) { $TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace); - my @SortedTypes = sort {lc(show_Type($a, 0, 1)) cmp lc(show_Type($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}}); + my @SortedTypes = sort {lc(showType($a, 0, 1)) cmp lc(showType($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}}); foreach my $TypeName (@SortedTypes) { my $ProblemNum = 1; my $TYPE_REPORT = ""; + my (%Kinds_Locations, %Kinds_Target) = (); - foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}})) + foreach my $Kind (sort {(index($b, "Size")!=-1) cmp (index($a, "Size")!=-1)} sort keys(%{$TypeChanges_Sev{$TypeName}})) { - foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}})) + foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}})) { - my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}}; - if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem)) + $Kinds_Locations{$Kind}{$Loc} = 1; + + my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"}; + if($Kinds_Target{$Kind}{$Target}) { + next; + } + $Kinds_Target{$Kind}{$Target} = 1; + + my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}; + if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr)) { - my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem); - $TYPE_REPORT .= "\n$ProblemNum\n".$Change."\n$Effect\n\n"; + my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr); + $TYPE_REPORT .= "\n$ProblemNum\n".$Change."\n$Effect\n\n"; $ProblemNum += 1; $ProblemsNum += 1; } @@ -16852,20 +7831,34 @@ $ProblemNum -= 1; if($TYPE_REPORT) { - my $Affected = getAffectedSymbols($Level, $TypeName, $Kinds_Locations{$TypeName}, \@Symbols); + my $Affected = getAffectedSymbols($Level, $TypeName, \%Kinds_Locations); my $ShowVTables = ""; - if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) { + if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) { $ShowVTables = showVTables($TypeName); } - $TYPE_PROBLEMS .= $ContentSpanStart."[+] ".show_Type($TypeName, 1, 1)." ($ProblemNum)".$ContentSpanEnd; + my $ShowType = showType($TypeName, 1, 1); + + if($NameSpace) + { + $TYPE_REPORT = cutNs($TYPE_REPORT, $NameSpace); + $ShowType = cutNs($ShowType, $NameSpace); + $Affected = cutNs($Affected, $NameSpace); + $ShowVTables = cutNs($ShowVTables, $NameSpace); + } + + $TYPE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowType; + if($In::Opt{"OldStyle"}) { + $TYPE_PROBLEMS .= " ($ProblemNum)"; + } + else { + $TYPE_PROBLEMS .= "  $ProblemNum "; + } + $TYPE_PROBLEMS .= $ContentSpanEnd; $TYPE_PROBLEMS .= "
\n".$ContentDivStart."\n"; - $TYPE_PROBLEMS .= "\n"; + $TYPE_PROBLEMS .= "\n\n"; $TYPE_PROBLEMS .= "".$TYPE_REPORT."
ChangeChangeEffect
\n"; $TYPE_PROBLEMS .= $ShowVTables.$Affected."

".$ContentDivEnd."\n"; - if($NameSpace) { - $TYPE_PROBLEMS=~s/\b\Q$NameSpace\E::(\w|\~)/$1/g; - } } } $TYPE_PROBLEMS .= "
\n"; @@ -16880,22 +7873,28 @@ { # Safe Changes $Title = "Other Changes in Data Types"; } - $TYPE_PROBLEMS = "\n

$Title ($ProblemsNum)


\n".$TYPE_PROBLEMS.$TOP_REF."
\n"; + if($In::Opt{"OldStyle"}) { + $TYPE_PROBLEMS = "

$Title ($ProblemsNum)


\n".$TYPE_PROBLEMS; + } + else { + $TYPE_PROBLEMS = "

$Title  $ProblemsNum 


\n".$TYPE_PROBLEMS; + } + $TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS.$TOP_REF."
\n"; } } return $TYPE_PROBLEMS; } -sub show_Type($$$) +sub showType($$$) { - my ($Name, $Html, $LibVersion) = @_; - my $TType = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$Name}}{"Type"}; + my ($Name, $Html, $LVer) = @_; + my $TType = $TypeInfo{$LVer}{$TName_Tid{$LVer}{$Name}}{"Type"}; $TType = lc($TType); if($TType=~/struct|union|enum/) { $Name=~s/\A\Q$TType\E //g; } if($Html) { - $Name = "".$TType." ".htmlSpecChars($Name); + $Name = "".$TType." ".specChars($Name); } else { $Name = $TType." ".$Name; @@ -16903,10 +7902,10 @@ return $Name; } -sub get_Anchor($$$) +sub getAnchor($$$) { my ($Kind, $Level, $Severity) = @_; - if($JoinReport) + if($In::Opt{"JoinReport"}) { if($Severity eq "Safe") { return "Other_".$Level."_Changes_In_".$Kind."s"; @@ -16930,27 +7929,27 @@ { my $TypeName = $_[0]; my $TypeId1 = $TName_Tid{1}{$TypeName}; - my %Type1 = get_Type($TypeId1, 1); + my %Type1 = getType($TypeId1, 1); if(defined $Type1{"VTable"} and keys(%{$Type1{"VTable"}})) { my $TypeId2 = $TName_Tid{2}{$TypeName}; - my %Type2 = get_Type($TypeId2, 2); + my %Type2 = getType($TypeId2, 2); if(defined $Type2{"VTable"} and keys(%{$Type2{"VTable"}})) { my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}})); my %Entries = (); - foreach my $Index (sort {int($a)<=>int($b)} (keys(%Indexes))) + foreach my $Index (sort {$a<=>$b} (keys(%Indexes))) { $Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index}); $Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index}); } my $VTABLES = ""; - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML $VTABLES .= " \n"; - foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries))) + foreach my $Index (sort {$a<=>$b} (keys(%Entries))) { $VTABLES .= " \n"; $VTABLES .= " ".xmlSpecChars($Entries{$Index}{"E1"})."\n"; @@ -16965,10 +7964,16 @@ $VTABLES .= "Offset"; $VTABLES .= "Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries"; $VTABLES .= "Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries"; - foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries))) + foreach my $Index (sort {$a<=>$b} (keys(%Entries))) { my ($Color1, $Color2) = ("", ""); - if($Entries{$Index}{"E1"} ne $Entries{$Index}{"E2"}) + + my $E1 = $Entries{$Index}{"E1"}; + my $E2 = $Entries{$Index}{"E2"}; + + if($E1 ne $E2 + and $E1!~/ 0x/ + and $E2!~/ 0x/) { if($Entries{$Index}{"E1"}) { @@ -16980,8 +7985,8 @@ } } $VTABLES .= "".$Index."\n"; - $VTABLES .= "".htmlSpecChars($Entries{$Index}{"E1"})."\n"; - $VTABLES .= "".htmlSpecChars($Entries{$Index}{"E2"})."\n"; + $VTABLES .= "".specChars($Entries{$Index}{"E1"})."\n"; + $VTABLES .= "".specChars($Entries{$Index}{"E2"})."\n"; } $VTABLES .= "
\n"; $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd; @@ -17018,11 +8023,11 @@ sub adjustParamPos($$$) { - my ($Pos, $Symbol, $LibVersion) = @_; - if(defined $CompleteSignature{$LibVersion}{$Symbol}) + my ($Pos, $Symbol, $LVer) = @_; + if(defined $CompSign{$LVer}{$Symbol}) { - if(not $CompleteSignature{$LibVersion}{$Symbol}{"Static"} - and $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) + if(not $CompSign{$LVer}{$Symbol}{"Static"} + and $CompSign{$LVer}{$Symbol}{"Class"}) { return $Pos-1; } @@ -17035,12 +8040,12 @@ sub getParamPos($$$) { - my ($Name, $Symbol, $LibVersion) = @_; + my ($Name, $Symbol, $LVer) = @_; - if(defined $CompleteSignature{$LibVersion}{$Symbol} - and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}) + if(defined $CompSign{$LVer}{$Symbol} + and defined $CompSign{$LVer}{$Symbol}{"Param"}) { - my $Info = $CompleteSignature{$LibVersion}{$Symbol}; + my $Info = $CompSign{$LVer}{$Symbol}; foreach (keys(%{$Info->{"Param"}})) { if($Info->{"Param"}{$_}{"name"} eq $Name) @@ -17060,94 +8065,76 @@ return $Loc; } -sub getAffectedSymbols($$$$) +sub getAffectedSymbols($$$) { - my ($Level, $Target_TypeName, $Kinds_Locations, $Syms) = @_; - my $LIMIT = 10; + my ($Level, $Target_TypeName, $Kinds_Locations) = @_; - if(defined $AffectLimit) - { - $LIMIT = $AffectLimit; + my $LIMIT = 10; + if(defined $In::Opt{"AffectLimit"}) { + $LIMIT = $In::Opt{"AffectLimit"}; } my %SymSel = (); - my %SymLocKind = (); - foreach my $Symbol (@{$Syms}) + foreach my $Kind (sort keys(%{$Kinds_Locations})) { - if(index($Symbol, "_Z")==0 - and $Symbol=~/(C2|D2|D0)[EI]/) - { # duplicated problems for C2 constructors, D2 and D0 destructors - next; - } + my @Locs = sort {(index($a, "retval")!=-1) cmp (index($b, "retval")!=-1)} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}}); - foreach my $Kind (sort keys(%{$Kinds_Locations})) + foreach my $Loc (@Locs) { - if(not defined $CompatProblems{$Level}{$Symbol} - or not defined $CompatProblems{$Level}{$Symbol}{$Kind}) { - next; - } - - foreach my $Loc (sort keys(%{$Kinds_Locations->{$Kind}})) + foreach my $Symbol (keys(%{$TypeProblemsIndex{$Level}{$Target_TypeName}{$Kind}{$Loc}})) { - if(not defined $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}) { + if(index($Symbol, "_Z")==0 + and $Symbol=~/(C4|C2|D4|D2|D0)[EI]/) + { # duplicated problems for C2/C4 constructors, D2/D4 and D0 destructors next; } - my ($SN, $SS, $SV) = separate_symbol($Symbol); - if($Level eq "Source") - { # remove symbol version - $Symbol = $SN; - } - - if($SV and defined $CompatProblems{$Level}{$SN} - and defined $CompatProblems{$Level}{$SN}{$Kind}{$Loc}) - { # duplicated problems for versioned symbols - next; + if(index($Symbol, "\@")!=-1 + or index($Symbol, "\$")!=-1) + { + my ($SN, $SS, $SV) = symbolParts($Symbol); + + if($Level eq "Source") + { # remove symbol version + $Symbol = $SN; + } + + if($SV and defined $CompatProblems{$Level}{$SN}) + { # duplicated problems for versioned symbols + next; + } } - my $Type_Name = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}{"Type_Name"}; - if($Type_Name ne $Target_TypeName) { - next; + if(not defined $SymSel{$Symbol}) + { + $SymSel{$Symbol}{"Kind"} = $Kind; + $SymSel{$Symbol}{"Loc"} = $Loc; } - - $SymLocKind{$Symbol}{$Loc}{$Kind} = 1; - } - } - } - - foreach my $Symbol (sort keys(%SymLocKind)) - { - LOOP: foreach my $Loc (sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$SymLocKind{$Symbol}})) - { - foreach my $Kind (keys(%{$SymLocKind{$Symbol}{$Loc}})) - { - $SymSel{$Symbol}{"Loc"} = $Loc; - $SymSel{$Symbol}{"Kind"} = $Kind; - - last LOOP; } } } my $Affected = ""; - my $Num = 0; + my $SNum = 0; - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML $Affected .= " \n"; foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel)) { + my $Kind = $SymSel{$Symbol}{"Kind"}; my $Loc = $SymSel{$Symbol}{"Loc"}; + my $PName = getParamName($Loc); - my $Desc = getAffectDesc($Level, $Symbol, $SymSel{$Symbol}{"Kind"}, $Loc); + my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc); my $Target = ""; if($PName) { $Target .= " param=\"$PName\""; - $Desc=~s/parameter $PName /parameter \@param /; + $Des=~s/parameter $PName /parameter \@param /; } elsif($Loc=~/\Aretval(\-|\Z)/i) { $Target .= " affected=\"retval\""; @@ -17156,19 +8143,17 @@ $Target .= " affected=\"this\""; } - if($Desc=~s/\AField ([^\s]+) /Field \@field /) { + if($Des=~s/\AField ([^\s]+) /Field \@field /) { $Target .= " field=\"$1\""; } $Affected .= " \n"; - $Affected .= " ".xmlSpecChars($Desc)."\n"; + $Affected .= " ".xmlSpecChars($Des)."\n"; $Affected .= " \n"; - - if($Num>$LIMIT) { - last LOOP; + + if(++$SNum>=$LIMIT) { + last; } - - $Num += 1; } $Affected .= " \n"; } @@ -17176,30 +8161,34 @@ { # HTML foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel)) { - my $Desc = getAffectDesc($Level, $Symbol, $SymSel{$Symbol}{"Kind"}, $SymSel{$Symbol}{"Loc"}); - my $S = get_Signature($Symbol, 1); - my $PName = getParamName($SymSel{$Symbol}{"Loc"}); + my $Kind = $SymSel{$Symbol}{"Kind"}; + my $Loc = $SymSel{$Symbol}{"Loc"}; + + my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc); + my $PName = getParamName($Loc); my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1); - $Affected .= "".highLight_Signature_PPos_Italic($S, $Pos, 1, 0, 0)."
\n"; - $Affected .= "
".htmlSpecChars($Desc)."
\n"; + $Affected .= "".getSignature($Symbol, 1, "Class|Name|Param|HTML|Italic|Target=".$Pos)."
\n"; + $Affected .= "
".specChars($Des)."
\n"; - if($Num>$LIMIT) { + if(++$SNum>=$LIMIT) { last; } - - $Num += 1; } - if(keys(%SymSel)>$LIMIT) { - $Affected .= " ...\n
\n"; # and others ... + my $Total = keys(%SymSel); + + if($Total>$LIMIT) { + $Affected .= " ...\n
\n"; # and others ... } $Affected = "
".$Affected."
\n"; if($Affected) { + + my $Per = showNum($Total*100/keys(%{$CheckedSymbols{$Level}})); $Affected = $ContentDivStart.$Affected.$ContentDivEnd; - $Affected = $ContentSpanStart_Affected."[+] affected symbols (".keys(%SymSel).")".$ContentSpanEnd.$Affected; + $Affected = $ContentSpanStart_Affected."[+] affected symbols: $Total ($Per\%)".$ContentSpanEnd.$Affected; } } @@ -17209,13 +8198,13 @@ sub cmpLocations($$) { my ($L1, $L2) = @_; - if($L2=~/\A(retval|this)\b/ - and $L1!~/\A(retval|this)\b/) + if((index($L2, "retval")==0 or index($L2, "this")==0) + and (index($L1, "retval")!=0 and index($L1, "this")!=0)) { - if($L1!~/\-\>/) { + if(index($L1, "->")==-1) { return 1; } - elsif($L2=~/\-\>/) { + elsif(index($L2, "->")!=-1) { return 1; } } @@ -17224,43 +8213,53 @@ sub getAffectDesc($$$$) { - my ($Level, $Symbol, $Kind, $Location) = @_; + my ($Level, $Symbol, $Kind, $Loc) = @_; - my %Problem = %{$CompatProblems{$Level}{$Symbol}{$Kind}{$Location}}; + if($ExtendedSymbols{$Symbol}) { + return "This is a symbol from an external library that may use subject library and change the ABI after recompiling."; + } + + my $PAttr = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}; - my $Location_I = $Location; - $Location=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field + $Loc=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field my @Sentence = (); if($Kind eq "Overridden_Virtual_Method" or $Kind eq "Overridden_Virtual_Method_B") { - push(@Sentence, "The method '".$Problem{"New_Value"}."' will be called instead of this method."); + push(@Sentence, "The method '".$PAttr->{"New_Value"}."' will be called instead of this method."); } elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types") { - my %SymInfo = %{$CompleteSignature{1}{$Symbol}}; + my %SymInfo = %{$CompSign{1}{$Symbol}}; - if($Location eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/) + if($Loc eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/) { - my $METHOD_TYPE = $SymInfo{"Constructor"}?"constructor":"method"; + my $MType = "method"; + if($SymInfo{"Constructor"}) { + $MType = "constructor"; + } + elsif($SymInfo{"Destructor"}) { + $MType = "destructor"; + } + my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"}; - if($ClassName eq $Problem{"Type_Name"}) { - push(@Sentence, "This $METHOD_TYPE is from \'".$Problem{"Type_Name"}."\' class."); + if($ClassName eq $PAttr->{"Type_Name"}) { + push(@Sentence, "This $MType is from \'".$PAttr->{"Type_Name"}."\' class."); } else { - push(@Sentence, "This $METHOD_TYPE is from derived class \'".$ClassName."\'."); + push(@Sentence, "This $MType is from derived class \'".$ClassName."\'."); } } else { my $TypeID = undef; - if($Location=~/retval/) + if($Loc=~/retval/) { # return value - if(index($Location, "->")!=-1) { - push(@Sentence, "Field \'".$Location."\' in return value"); + if(index($Loc, "->")!=-1) { + push(@Sentence, "Field \'".$Loc."\' in the return value"); } else { push(@Sentence, "Return value"); @@ -17268,10 +8267,10 @@ $TypeID = $SymInfo{"Return"}; } - elsif($Location=~/this/) + elsif($Loc=~/this/) { # "this" pointer - if(index($Location, "->")!=-1) { - push(@Sentence, "Field \'".$Location."\' in the object of this method"); + if(index($Loc, "->")!=-1) { + push(@Sentence, "Field \'".$Loc."\' in the object of this method"); } else { push(@Sentence, "\'this\' pointer"); @@ -17281,12 +8280,11 @@ } else { # parameters - - my $PName = getParamName($Location); + my $PName = getParamName($Loc); my $PPos = getParamPos($PName, $Symbol, 1); - if(index($Location, "->")!=-1) { - push(@Sentence, "Field \'".$Location."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter"); + if(index($Loc, "->")!=-1) { + push(@Sentence, "Field \'".$Loc."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter"); } else { push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter"); @@ -17298,9 +8296,9 @@ $TypeID = $SymInfo{"Param"}{$PPos}{"type"}; } - if($Location!~/this/) + if($Loc!~/this/) { - if(my %PureType = get_PureType($TypeID, $TypeInfo{1})) + if(my %PureType = getPureType($TypeID, 1)) { if($PureType{"Type"} eq "Pointer") { push(@Sentence, "(pointer)"); @@ -17311,38 +8309,34 @@ } } - if($Location eq "this") { - push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'."); + if($Loc eq "this") { + push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'."); } else { - my $Location_T = $Location; - $Location_T=~s/\A\w+(\->|\Z)//; # location in type + my $Loc_T = $Loc; + $Loc_T=~s/\A\w+(\->|\Z)//; # location in type my $TypeID_Problem = $TypeID; - if($Location_T) { - $TypeID_Problem = getFieldType($Location_T, $TypeID, 1); + if($Loc_T) { + $TypeID_Problem = getFieldType($Loc_T, $TypeID, 1); } - if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Problem{"Type_Name"}) { - push(@Sentence, "has type \'".$Problem{"Type_Name"}."\'."); + if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $PAttr->{"Type_Name"}) { + push(@Sentence, "is of type \'".$PAttr->{"Type_Name"}."\'."); } else { - push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'."); + push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'."); } } } } - if($ExtendedSymbols{$Symbol}) { - push(@Sentence, " This is a symbol from an external library that may use the \'$TargetLibraryName\' library and change the ABI after recompiling."); - } my $Sent = join(" ", @Sentence); $Sent=~s/->/./g; - if($ReportFormat eq "xml") - { + if($In::Opt{"ReportFormat"} eq "xml") { $Sent=~s/'//g; } @@ -17351,13 +8345,13 @@ sub getFieldType($$$) { - my ($Location, $TypeId, $LibVersion) = @_; + my ($Loc, $TypeId, $LVer) = @_; - my @Fields = split(/\->/, $Location); + my @Fields = split(/\->/, $Loc); foreach my $Name (@Fields) { - my %Info = get_BaseType($TypeId, $LibVersion); + my %Info = getBaseType($TypeId, $LVer); foreach my $Pos (keys(%{$Info{"Memb"}})) { @@ -17372,105 +8366,20 @@ return $TypeId; } -sub get_XmlSign($$) -{ - my ($Symbol, $LibVersion) = @_; - my $Info = $CompleteSignature{$LibVersion}{$Symbol}; - my $Report = ""; - foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Info->{"Param"}})) - { - my $Name = $Info->{"Param"}{$Pos}{"name"}; - my $Type = $Info->{"Param"}{$Pos}{"type"}; - my $TypeName = $TypeInfo{$LibVersion}{$Type}{"Name"}; - foreach my $Typedef (keys(%ChangedTypedef)) - { - if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) { - $TypeName=~s/\b\Q$Typedef\E\b/$Base/g; - } - } - $Report .= " \n"; - $Report .= " ".$Name."\n"; - $Report .= " ".xmlSpecChars($TypeName)."\n"; - $Report .= " \n"; - } - if(my $Return = $Info->{"Return"}) - { - my $RTName = $TypeInfo{$LibVersion}{$Return}{"Name"}; - $Report .= " \n"; - $Report .= " ".xmlSpecChars($RTName)."\n"; - $Report .= " \n"; - } - return $Report; -} - -sub get_Report_SymbolsInfo($) -{ - my $Level = $_[0]; - my $Report = "\n"; - foreach my $Symbol (sort keys(%{$CompatProblems{$Level}})) - { - my ($SN, $SS, $SV) = separate_symbol($Symbol); - if($SV and defined $CompatProblems{$Level}{$SN}) { - next; - } - $Report .= " \n"; - my ($S1, $P1, $S2, $P2) = (); - if(not $AddedInt{$Level}{$Symbol}) - { - if(defined $CompleteSignature{1}{$Symbol} - and defined $CompleteSignature{1}{$Symbol}{"Header"}) - { - $P1 = get_XmlSign($Symbol, 1); - $S1 = get_Signature($Symbol, 1); - } - elsif($Symbol=~/\A(_Z|\?)/) { - $S1 = $tr_name{$Symbol}; - } - } - if(not $RemovedInt{$Level}{$Symbol}) - { - if(defined $CompleteSignature{2}{$Symbol} - and defined $CompleteSignature{2}{$Symbol}{"Header"}) - { - $P2 = get_XmlSign($Symbol, 2); - $S2 = get_Signature($Symbol, 2); - } - elsif($Symbol=~/\A(_Z|\?)/) { - $S2 = $tr_name{$Symbol}; - } - } - if($S1) - { - $Report .= " \n"; - $Report .= $P1; - $Report .= " \n"; - } - if($S2 and $S2 ne $S1) - { - $Report .= " \n"; - $Report .= $P2; - $Report .= " \n"; - } - $Report .= " \n"; - } - $Report .= "\n"; - return $Report; -} - sub writeReport($$) { my ($Level, $Report) = @_; - if($ReportFormat eq "xml") { + if($In::Opt{"ReportFormat"} eq "xml") { $Report = "\n".$Report; } - if($StdOut) + if($In::Opt{"StdOut"}) { # --stdout option print STDOUT $Report; } else { my $RPath = getReportPath($Level); - mkpath(get_dirname($RPath)); + mkpath(getDirname($RPath)); open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n"); print REPORT $Report; @@ -17481,7 +8390,7 @@ sub getReport($) { my $Level = $_[0]; - if($ReportFormat eq "xml") + if($In::Opt{"ReportFormat"} eq "xml") { # XML if($Level eq "Join") { @@ -17494,14 +8403,9 @@ else { my $Report = "\n\n"; - my ($Summary, $MetaData) = get_Summary($Level); + my ($Summary, $MetaData, $AnyChanged) = getSummary($Level); $Report .= $Summary."\n"; - $Report .= get_Report_Added($Level).get_Report_Removed($Level); - $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level); - - # additional symbols info (if needed) - # $Report .= get_Report_SymbolsInfo($Level); - + $Report .= getReportProblems_All($Level); $Report .= "\n"; return $Report; } @@ -17514,42 +8418,40 @@ { $CssStyles .= "\n".readModule("Styles", "Tabs.css"); $JScripts .= "\n".readModule("Scripts", "Tabs.js"); - my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." compatibility report"; - my $Keywords = $TargetTitle.", compatibility, API, ABI, report"; - my $Description = "API/ABI compatibility report for the $TargetTitle $TargetComponent between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions"; - my ($BSummary, $BMetaData) = get_Summary("Binary"); - my ($SSummary, $SMetaData) = get_Summary("Source"); - my $Report = "\n\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts).""; - $Report .= get_Report_Title("Join")." + my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." compatibility report"; + my $Keywords = $In::Opt{"TargetTitle"}.", compatibility, API, ABI, report"; + my $Des = "API/ABI compatibility report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}." between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions"; + my ($BSummary, $BMetaData, $BAnyChanged) = getSummary("Binary"); + my ($SSummary, $SMetaData, $SAnyChanged) = getSummary("Source"); + my $Report = "\n\n".composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, ($BAnyChanged or $SAnyChanged)).""; + $Report .= getReportTitle("Join")."
"; - $Report .= "
\n$BSummary\n".get_Report_Added("Binary").get_Report_Removed("Binary").get_Report_Problems("High", "Binary").get_Report_Problems("Medium", "Binary").get_Report_Problems("Low", "Binary").get_Report_Problems("Safe", "Binary").get_SourceInfo()."


"; - $Report .= "
\n$SSummary\n".get_Report_Added("Source").get_Report_Removed("Source").get_Report_Problems("High", "Source").get_Report_Problems("Medium", "Source").get_Report_Problems("Low", "Source").get_Report_Problems("Safe", "Source").get_SourceInfo()."


"; + $Report .= "
\n$BSummary\n".getReportProblems_All("Binary").getSourceInfo()."


"; + $Report .= "
\n$SSummary\n".getReportProblems_All("Source").getSourceInfo()."


"; $Report .= getReportFooter(); $Report .= "\n\n"; return $Report; } else { - my ($Summary, $MetaData) = get_Summary($Level); - my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." ".lc($Level)." compatibility report"; - my $Keywords = $TargetTitle.", ".lc($Level)." compatibility, API, report"; - my $Description = "$Level compatibility report for the ".$TargetTitle." ".$TargetComponent." between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions"; + my ($Summary, $MetaData, $AnyChanged) = getSummary($Level); + my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." ".lc($Level)." compatibility report"; + my $Keywords = $In::Opt{"TargetTitle"}.", ".lc($Level)." compatibility, API, report"; + my $Des = "$Level compatibility report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}." between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions"; if($Level eq "Binary") { - if(getArch(1) eq getArch(2) - and getArch(1) ne "unknown") { - $Description .= " on ".showArch(getArch(1)); + if($In::ABI{1}{"Arch"} eq $In::ABI{2}{"Arch"}) { + $Des .= " on ".showArch($In::ABI{1}{"Arch"}); } } - my $Report = "\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."\n\n
\n"; - $Report .= get_Report_Title($Level)."\n".$Summary."\n"; - $Report .= get_Report_Added($Level).get_Report_Removed($Level); - $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level); - $Report .= get_SourceInfo(); + my $Report = "\n".composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, $AnyChanged)."\n\n
\n"; + $Report .= getReportTitle($Level)."\n".$Summary."\n"; + $Report .= getReportProblems_All($Level); + $Report .= getSourceInfo(); $Report .= "
\n


\n"; $Report .= getReportFooter(); $Report .= "\n\n"; @@ -17558,36 +8460,39 @@ } } -sub getLegend() +sub getReportProblems_All($) { - return "
- - - - - - - - -
addedcompatible
warningincompatible
\n"; + my $Level = $_[0]; + + my $Report = getReportAdded($Level).getReportRemoved($Level); + $Report .= getReportProblems("High", $Level); + $Report .= getReportProblems("Medium", $Level); + $Report .= getReportProblems("Low", $Level); + $Report .= getReportProblems("Safe", $Level); + + # clean memory + delete($TypeProblemsIndex{$Level}); + delete($TypeChanges{$Level}); + delete($CompatProblems{$Level}); + + return $Report; } sub createReport() { - if($JoinReport) - { # --stdout + if($In::Opt{"JoinReport"}) { writeReport("Join", getReport("Join")); } - elsif($DoubleReport) + elsif($In::Opt{"DoubleReport"}) { # default writeReport("Binary", getReport("Binary")); writeReport("Source", getReport("Source")); } - elsif($BinaryOnly) + elsif($In::Opt{"BinOnly"}) { # --binary writeReport("Binary", getReport("Binary")); } - elsif($SourceOnly) + elsif($In::Opt{"SrcOnly"}) { # --source writeReport("Source", getReport("Source")); } @@ -17597,33 +8502,33 @@ { my $Footer = ""; - $Footer .= "
"; - $Footer .= ""; + $Footer .= "
\n"; + $Footer .= "\n"; $Footer .= "
\n"; return $Footer; } -sub get_Report_Problems($$) +sub getReportProblems($$) { my ($Severity, $Level) = @_; - my $Report = get_Report_TypeProblems($Severity, $Level); - if(my $SProblems = get_Report_SymbolProblems($Severity, $Level)) { + my $Report = getReportTypeProblems($Severity, $Level); + if(my $SProblems = getReportSymbolProblems($Severity, $Level)) { $Report .= $SProblems; } if($Severity eq "Low" or $Severity eq "Safe") { - $Report .= get_Report_ChangedConstants($Severity, $Level); + $Report .= getReportChangedConstants($Severity, $Level); } - if($ReportFormat eq "html") + if($In::Opt{"ReportFormat"} eq "html") { if($Report) { # add anchor - if($JoinReport) + if($In::Opt{"JoinReport"}) { if($Severity eq "Safe") { $Report = "".$Report; @@ -17646,27 +8551,28 @@ return $Report; } -sub composeHTML_Head($$$$$) +sub composeHTML_Head($$$$$$) { - my ($Title, $Keywords, $Description, $Styles, $Scripts) = @_; - return " - - - - - - - $Title - - - - "; + my ($Title, $Keywords, $Des, $Styles, $Scripts, $AnyChanged) = @_; + + my $Head = "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + + if(not $AnyChanged) { + $Head .= "\n"; + } + + $Head .= "$Title\n"; + $Head .= "\n"; + $Head .= "\n"; + $Head .= "\n"; + + return $Head; } sub insertIDs($) @@ -17684,132 +8590,25 @@ return $Text; } -sub checkPreprocessedUnit($) -{ - my $Path = $_[0]; - my ($CurHeader, $CurHeaderName) = ("", ""); - my $CurClass = ""; # extra info - open(PREPROC, $Path) || die ("can't open file \'$Path\': $!\n"); - - while(my $Line = ) - { # detecting public and private constants - if(substr($Line, 0, 1) eq "#") - { - chomp($Line); - if($Line=~/\A\#\s+\d+\s+\"(.+)\"/) - { - $CurHeader = path_format($1, $OSgroup); - $CurHeaderName = get_filename($CurHeader); - $CurClass = ""; - - if(index($CurHeader, $TMP_DIR)==0) { - next; - } - - if(substr($CurHeaderName, 0, 1) eq "<") - { # , , etc. - $CurHeaderName = ""; - $CurHeader = ""; - } - - if($ExtraInfo) - { - if($CurHeaderName) { - $PreprocessedHeaders{$Version}{$CurHeader} = 1; - } - } - } - if(not $ExtraDump) - { - if($CurHeaderName) - { - if(not $Include_Neighbors{$Version}{$CurHeaderName} - and not $Registered_Headers{$Version}{$CurHeader}) - { # not a target - next; - } - if(not is_target_header($CurHeaderName, 1) - and not is_target_header($CurHeaderName, 2)) - { # user-defined header - next; - } - } - } - - if($Line=~/\A\#\s*define\s+(\w+)\s+(.+)\s*\Z/) - { - my ($Name, $Value) = ($1, $2); - if(not $Constants{$Version}{$Name}{"Access"}) - { - $Constants{$Version}{$Name}{"Access"} = "public"; - $Constants{$Version}{$Name}{"Value"} = $Value; - if($CurHeaderName) { - $Constants{$Version}{$Name}{"Header"} = $CurHeaderName; - } - } - } - elsif($Line=~/\A\#[ \t]*undef[ \t]+([_A-Z]+)[ \t]*/) { - $Constants{$Version}{$1}{"Access"} = "private"; - } - } - else - { - if(defined $ExtraDump) - { - if($Line=~/(\w+)\s*\(/) - { # functions - $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader; - } - #elsif($Line=~/(\w+)\s*;/) - #{ # data - # $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader; - #} - elsif($Line=~/(\A|\s)class\s+(\w+)/) { - $CurClass = $2; - } - } - } - } - close(PREPROC); - foreach my $Constant (keys(%{$Constants{$Version}})) - { - if($Constants{$Version}{$Constant}{"Access"} eq "private") - { - delete($Constants{$Version}{$Constant}); - next; - } - if(not $ExtraDump and ($Constant=~/_h\Z/i - or isBuiltIn($Constants{$Version}{$Constant}{"Header"}))) - { # skip - delete($Constants{$Version}{$Constant}); - } - else { - delete($Constants{$Version}{$Constant}{"Access"}); - } - } - if($Debug) - { - mkpath($DEBUG_PATH{$Version}); - copy($Path, $DEBUG_PATH{$Version}."/preprocessor.txt"); - } -} - sub uncoverConstant($$) { - my ($LibVersion, $Constant) = @_; - return "" if(not $LibVersion or not $Constant); - return $Constant if(isCyclical(\@RecurConstant, $Constant)); - if(defined $Cache{"uncoverConstant"}{$LibVersion}{$Constant}) { - return $Cache{"uncoverConstant"}{$LibVersion}{$Constant}; + my ($LVer, $Constant) = @_; + + if(isCyclical(\@RecurConstant, $Constant)) { + return $Constant; + } + + if(defined $Cache{"uncoverConstant"}{$LVer}{$Constant}) { + return $Cache{"uncoverConstant"}{$LVer}{$Constant}; } - if(defined $Constants{$LibVersion}{$Constant}) + if(defined $Constants{$LVer}{$Constant}) { - my $Value = $Constants{$LibVersion}{$Constant}{"Value"}; - if(defined $Constants{$LibVersion}{$Value}) + my $Value = $Constants{$LVer}{$Constant}{"Value"}; + if(defined $Constants{$LVer}{$Value}) { push(@RecurConstant, $Constant); - my $Uncovered = uncoverConstant($LibVersion, $Value); + my $Uncovered = uncoverConstant($LVer, $Value); if($Uncovered ne "") { $Value = $Uncovered; } @@ -17818,14 +8617,14 @@ # FIXME: uncover $Value using all the enum constants # USE CASE: change of define NC_LONG from NC_INT (enum value) to NC_INT (define) - return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = $Value); + return ($Cache{"uncoverConstant"}{$LVer}{$Constant} = $Value); } - return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = ""); + return ($Cache{"uncoverConstant"}{$LVer}{$Constant} = ""); } sub simpleConstant($$) { - my ($LibVersion, $Value) = @_; + my ($LVer, $Value) = @_; if($Value=~/\W/) { my $Value_Copy = $Value; @@ -17834,7 +8633,7 @@ my $Word = $1; if($Value!~/$Word\s*\(/) { - my $Val = uncoverConstant($LibVersion, $Word); + my $Val = uncoverConstant($LVer, $Word); if($Val ne "") { $Value=~s/\b$Word\b/$Val/g; @@ -17860,7 +8659,7 @@ return $Value; } -my %IgnoreConstant = map {$_=>1} ( +my $IgnoreConstant = join("|", "VERSION", "VERSIONCODE", "VERNUM", @@ -17919,21 +8718,16 @@ { # __malloc_ptr_t return 1; } - foreach (keys(%IgnoreConstant)) - { - if($Name=~/(\A|_)$_(_|\Z)/) - { # version - return 1; - } - if(/\A[A-Z].*[a-z]\Z/) - { - if($Name=~/(\A|[a-z])$_([A-Z]|\Z)/) - { # version - return 1; - } - } + if($Name=~/(\A|_)($IgnoreConstant)(_|\Z)/) + { # version + return 1; } - if($Name=~/(\A|_)(lib|open|)$TargetLibraryShortName(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i) + if($Name=~/(\A|[a-z])(Release|Version)([A-Z]|\Z)/) + { # version + return 1; + } + my $LShort = $In::Opt{"TargetLibShort"}; + if($Name=~/(\A|_)(lib|open|)$LShort(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i) { # version return 1; } @@ -17971,18 +8765,24 @@ sub mergeConstants($) { my $Level = $_[0]; - foreach my $Constant (keys(%{$Constants{1}})) + foreach my $Constant (sort keys(%{$Constants{1}})) { - if($SkipConstants{1}{$Constant}) + if($In::Desc{1}{"SkipConstants"}{$Constant}) { # skipped by the user next; } if(my $Header = $Constants{1}{$Constant}{"Header"}) { - if(not is_target_header($Header, 1) - and not is_target_header($Header, 2)) - { # user-defined header + if(not isTargetHeader($Header, 1) + and not isTargetHeader($Header, 2)) { + next; + } + } + elsif(my $Source = $Constants{1}{$Constant}{"Source"}) + { + if(not isTargetSource($Source, 1) + and not isTargetSource($Source, 2)) { next; } } @@ -17999,9 +8799,12 @@ if(not defined $Constants{2}{$Constant}{"Value"}) { # removed - %{$CompatProblems_Constants{$Level}{$Constant}{"Removed_Constant"}} = ( - "Target"=>$Constant, - "Old_Value"=>$Old_Value ); + if(not defined $In::Opt{"SkipRemovedConstants"}) + { + %{$CompatProblems_Constants{$Level}{$Constant}{"Removed_Constant"}} = ( + "Target"=>$Constant, + "Old_Value"=>$Old_Value ); + } next; } @@ -18033,7 +8836,7 @@ { # expressions next; } - if(convert_integer($Old_Value) eq convert_integer($New_Value)) + if(convertInteger($Old_Value) eq convertInteger($New_Value)) { # 0x0001 and 0x1, 0x1 and 1 equal constants next; } @@ -18052,19 +8855,31 @@ } } + if(defined $In::Opt{"SkipAddedConstants"}) { + return; + } + foreach my $Constant (keys(%{$Constants{2}})) { if(not defined $Constants{1}{$Constant}{"Value"}) { - if($SkipConstants{2}{$Constant}) + if($In::Desc{2}{"SkipConstants"}{$Constant}) { # skipped by the user next; } if(my $Header = $Constants{2}{$Constant}{"Header"}) { - if(not is_target_header($Header, 1) - and not is_target_header($Header, 2)) + if(not isTargetHeader($Header, 1) + and not isTargetHeader($Header, 2)) + { # user-defined header + next; + } + } + elsif(my $Source = $Constants{2}{$Constant}{"Source"}) + { + if(not isTargetSource($Source, 1) + and not isTargetSource($Source, 2)) { # user-defined header next; } @@ -18090,7 +8905,7 @@ } } -sub convert_integer($) +sub convertInteger($) { my $Value = $_[0]; if($Value=~/\A0x[a-f0-9]+\Z/) @@ -18110,1703 +8925,626 @@ } } -sub readSymbols($) -{ - my $LibVersion = $_[0]; - my @LibPaths = getSOPaths($LibVersion); - if($#LibPaths==-1 and not $CheckHeadersOnly) - { - if($LibVersion==1) - { - printMsg("WARNING", "checking headers only"); - $CheckHeadersOnly = 1; - } - else { - exitStatus("Error", "$SLIB_TYPE libraries are not found in ".$Descriptor{$LibVersion}{"Version"}); - } - } - - foreach my $LibPath (@LibPaths) { - readSymbols_Lib($LibVersion, $LibPath, 0, "+Weak", 1, 1); - } - - if($CheckUndefined) - { - my %UndefinedLibs = (); - - my @Libs = (keys(%{$Library_Symbol{$LibVersion}}), keys(%{$DepLibrary_Symbol{$LibVersion}})); - - foreach my $LibName (sort @Libs) - { - if(defined $UndefinedSymbols{$LibVersion}{$LibName}) - { - foreach my $Symbol (keys(%{$UndefinedSymbols{$LibVersion}{$LibName}})) - { - if($Symbol_Library{$LibVersion}{$Symbol} - or $DepSymbol_Library{$LibVersion}{$Symbol}) - { # exported by target library - next; - } - if(index($Symbol, '@')!=-1) - { # exported default symbol version (@@) - $Symbol=~s/\@/\@\@/; - if($Symbol_Library{$LibVersion}{$Symbol} - or $DepSymbol_Library{$LibVersion}{$Symbol}) { - next; - } - } - foreach my $Path (find_SymbolLibs($LibVersion, $Symbol)) { - $UndefinedLibs{$Path} = 1; - } - } - } - } - if($ExtraInfo) - { # extra information for other tools - if(my @Paths = sort keys(%UndefinedLibs)) - { - my $LibString = ""; - my %Dirs = (); - foreach (@Paths) - { - $KnownLibs{$_} = 1; - my ($Dir, $Name) = separate_path($_); - - if(not grep {$Dir eq $_} (@{$SystemPaths{"lib"}})) { - $Dirs{esc($Dir)} = 1; - } - - $Name = parse_libname($Name, "name", $OStarget); - $Name=~s/\Alib//; - - $LibString .= " -l$Name"; - } - - foreach my $Dir (sort {$b cmp $a} keys(%Dirs)) - { - $LibString = " -L".esc($Dir).$LibString; - } - - writeFile($ExtraInfo."/libs-string", $LibString); - } - } - } - - if($ExtraInfo) { - writeFile($ExtraInfo."/lib-paths", join("\n", sort keys(%KnownLibs))); - } +sub getSymbolSize($$) +{ # size from the shared library + my ($Symbol, $LVer) = @_; - if(not $CheckHeadersOnly) + if(defined $In::ABI{$LVer}{"SymLib"}{$Symbol} + and my $LibName = $In::ABI{$LVer}{"SymLib"}{$Symbol}) { - if($#LibPaths!=-1) + if(defined $In::ABI{$LVer}{"Symbols"}{$LibName}{$Symbol} + and my $Size = $In::ABI{$LVer}{"Symbols"}{$LibName}{$Symbol}) { - if(not keys(%{$Symbol_Library{$LibVersion}})) - { - printMsg("WARNING", "the set of public symbols in library(ies) is empty ($LibVersion)"); - printMsg("WARNING", "checking headers only"); - $CheckHeadersOnly = 1; + if($Size<0) { + return -$Size; } } } - - # clean memory - %SystemObjects = (); + return 0; } -my %Prefix_Lib_Map=( - # symbols for autodetecting library dependencies (by prefix) - "pthread_" => ["libpthread"], - "g_" => ["libglib-2.0", "libgobject-2.0", "libgio-2.0"], - "cairo_" => ["libcairo"], - "gtk_" => ["libgtk-x11-2.0"], - "atk_" => ["libatk-1.0"], - "gdk_" => ["libgdk-x11-2.0"], - "gl" => ["libGL"], - "glu" => ["libGLU"], - "popt" => ["libpopt"], - "Py" => ["libpython"], - "jpeg_" => ["libjpeg"], - "BZ2_" => ["libbz2"], - "Fc" => ["libfontconfig"], - "Xft" => ["libXft"], - "SSL_" => ["libssl"], - "sem_" => ["libpthread"], - "snd_" => ["libasound"], - "art_" => ["libart_lgpl_2"], - "dbus_g" => ["libdbus-glib-1"], - "GOMP_" => ["libgomp"], - "omp_" => ["libgomp"], - "cms" => ["liblcms"] -); - -my %Pattern_Lib_Map=( - "SL[a-z]" => ["libslang"] -); - -my %Symbol_Lib_Map=( - # symbols for autodetecting library dependencies (by name) - "pow" => "libm", - "fmod" => "libm", - "sin" => "libm", - "floor" => "libm", - "cos" => "libm", - "dlopen" => "libdl", - "deflate" => "libz", - "inflate" => "libz", - "move_panel" => "libpanel", - "XOpenDisplay" => "libX11", - "resize_term" => "libncurses", - "clock_gettime" => "librt", - "crypt" => "libcrypt" -); - -sub find_SymbolLibs($$) +sub createSymbolsList($$$$$) { - my ($LibVersion, $Symbol) = @_; + my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_; - if(index($Symbol, "g_")==0 and $Symbol=~/[A-Z]/) - { # debug symbols - return (); - } + $In::ABI{1} = readABIDump(1, $DPath); + initAliases(1); - my %Paths = (); + prepareSymbols(1); - if(my $LibName = $Symbol_Lib_Map{$Symbol}) + my %SymbolHeaderLib = (); + my $Total = 0; + + # Get List + foreach my $Symbol (sort keys(%{$CompSign{1}})) { - if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) { - $Paths{$Path} = 1; + if(not linkSymbol($Symbol, 1, "-Deps")) + { # skip src only and all external functions + next; } + if(not symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Public", "Binary", 1)) + { # skip other symbols + next; + } + my $HeaderName = $CompSign{1}{$Symbol}{"Header"}; + if(not $HeaderName) + { # skip src only and all external functions + next; + } + my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol}; + if(not $DyLib) + { # skip src only and all external functions + next; + } + $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1; + $Total += 1; } - - if(my $SymbolPrefix = getPrefix($Symbol)) + # Draw List + my $SYMBOLS_LIST = "

Public symbols in $LName ($LVersion)"; + $SYMBOLS_LIST .= " on ".showArch($ArchName)."
Total: $Total


"; + foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib)) { - if(defined $Cache{"find_SymbolLibs"}{$SymbolPrefix}) { - return @{$Cache{"find_SymbolLibs"}{$SymbolPrefix}}; - } - - if(not keys(%Paths)) + foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}})) { - if(defined $Prefix_Lib_Map{$SymbolPrefix}) - { - foreach my $LibName (@{$Prefix_Lib_Map{$SymbolPrefix}}) - { - if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) { - $Paths{$Path} = 1; - } - } + my %NS_Symbol = (); + foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) { + $NS_Symbol{selectSymbolNs($Symbol, 1)}{$Symbol} = 1; } - } - - if(not keys(%Paths)) - { - foreach my $Prefix (sort keys(%Pattern_Lib_Map)) + foreach my $NameSpace (sort keys(%NS_Symbol)) { - if($Symbol=~/\A$Prefix/) + $SYMBOLS_LIST .= getTitle($HeaderName, $DyLib, $NameSpace); + my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NS_Symbol{$NameSpace}}); + foreach my $Symbol (@SortedInterfaces) { - foreach my $LibName (@{$Pattern_Lib_Map{$Prefix}}) - { - if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) { - $Paths{$Path} = 1; - } + my $SubReport = ""; + my $Signature = highLight_ItalicColor($Symbol, 1); + if($NameSpace) { + $Signature = cutNs($Signature, $NameSpace); } - } - } - } - - if(not keys(%Paths)) - { - if($SymbolPrefix) - { # try to find a library by symbol prefix - if($SymbolPrefix eq "inotify" and - index($Symbol, "\@GLIBC")!=-1) - { - if(my $Path = get_LibPath($LibVersion, "libc.$LIB_EXT")) { - $Paths{$Path} = 1; + if($Symbol=~/\A(_Z|\?)/) { + $SubReport = insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol

".$ContentDivEnd."\n"); } - } - else - { - if(my $Path = get_LibPath_Prefix($LibVersion, $SymbolPrefix)) { - $Paths{$Path} = 1; + else { + $SubReport = "".$Signature."
\n"; } + $SYMBOLS_LIST .= $SubReport; } } - } - - if(my @Paths = keys(%Paths)) { - $Cache{"find_SymbolLibs"}{$SymbolPrefix} = \@Paths; + $SYMBOLS_LIST .= "
\n"; } } - return keys(%Paths); -} - -sub get_LibPath_Prefix($$) -{ - my ($LibVersion, $Prefix) = @_; + # clear info + (%CompSign, %ClassMethods, %AllocableClass, %ClassNames, %In::ABI) = (); - $Prefix = lc($Prefix); - $Prefix=~s/[_]+\Z//g; + ($Content_Counter, $ContentID) = (0, 0); - foreach ("-2", "2", "-1", "1", "") - { # libgnome-2.so - # libxml2.so - # libdbus-1.so - if(my $Path = get_LibPath($LibVersion, "lib".$Prefix.$_.".".$LIB_EXT)) { - return $Path; - } - } - return ""; + my $CssStyles = readModule("Styles", "SymbolsList.css"); + my $JScripts = readModule("Scripts", "Sections.js"); + $SYMBOLS_LIST = "".$SYMBOLS_LIST.$TOP_REF."
\n"; + my $Title = "$LName: public symbols"; + my $Keywords = "$LName, API, symbols"; + my $Des = "List of symbols in $LName ($LVersion) on ".showArch($ArchName); + $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, 1)." +
\n$SYMBOLS_LIST
+

\n".getReportFooter()." + "; + writeFile($SaveTo, $SYMBOLS_LIST); } -sub getPrefix($) +sub dumpSorting($) { - my $Str = $_[0]; - if($Str=~/\A([_]*[A-Z][a-z]{1,5})[A-Z]/) - { # XmuValidArea: Xmu - return $1; - } - elsif($Str=~/\A([_]*[a-z]+)[A-Z]/) - { # snfReadFont: snf - return $1; - } - elsif($Str=~/\A([_]*[A-Z]{2,})[A-Z][a-z]+([A-Z][a-z]+|\Z)/) - { # XRRTimes: XRR - return $1; - } - elsif($Str=~/\A([_]*[a-z]{1,2}\d+)[a-z\d]*_[a-z]+/i) - { # H5HF_delete: H5 - return $1; - } - elsif($Str=~/\A([_]*[a-z0-9]{2,}_)[a-z]+/i) - { # alarm_event_add: alarm_ - return $1; - } - elsif($Str=~/\A(([a-z])\2{1,})/i) - { # ffopen - return $1; - } - return ""; -} - -sub getSymbolSize($$) -{ # size from the shared library - my ($Symbol, $LibVersion) = @_; - return 0 if(not $Symbol); - if(defined $Symbol_Library{$LibVersion}{$Symbol} - and my $LibName = $Symbol_Library{$LibVersion}{$Symbol}) - { - if(defined $Library_Symbol{$LibVersion}{$LibName}{$Symbol} - and my $Size = $Library_Symbol{$LibVersion}{$LibName}{$Symbol}) - { - if($Size<0) { - return -$Size; - } - } + my $Hash = $_[0]; + if(not $Hash) { + return []; } - return 0; -} - -sub canonifyName($$) -{ # make TIFFStreamOpen(char const*, std::basic_ostream >*) - # to be TIFFStreamOpen(char const*, std::basic_ostream*) - my ($Name, $Type) = @_; - # single - while($Name=~/([^<>,]+),\s*$DEFAULT_STD_PARMS<([^<>,]+)>\s*/ and $1 eq $3) - { - my $P = $1; - $Name=~s/\Q$P\E,\s*$DEFAULT_STD_PARMS<\Q$P\E>\s*/$P/g; + my @Keys = keys(%{$Hash}); + if($#Keys<0) { + return []; } - # double - if($Name=~/$DEFAULT_STD_PARMS/) - { - if($Type eq "S") - { - my ($ShortName, $FuncParams) = split_Signature($Name); - - foreach my $FParam (separate_Params($FuncParams, 0, 0)) - { - if(index($FParam, "<")!=-1) - { - $FParam=~s/>([^<>]+)\Z/>/; # remove quals - my $FParam_N = canonifyName($FParam, "T"); - if($FParam_N ne $FParam) { - $Name=~s/\Q$FParam\E/$FParam_N/g; - } - } - } - } - elsif($Type eq "T") - { - my ($ShortTmpl, $TmplParams) = template_Base($Name); - - my @TParams = separate_Params($TmplParams, 0, 0); - if($#TParams>=1) - { - my $FParam = $TParams[0]; - foreach my $Pos (1 .. $#TParams) - { - my $TParam = $TParams[$Pos]; - if($TParam=~/\A$DEFAULT_STD_PARMS<\Q$FParam\E\s*>\Z/) { - $Name=~s/\Q$FParam, $TParam\E\s*/$FParam/g; - } - } - } - } + if($Keys[0]=~/\A\d+\Z/) + { # numbers + return [sort {$a<=>$b} @Keys]; } - if($Type eq "S") { - return formatName($Name, "S"); + else + { # strings + return [sort {$a cmp $b} @Keys]; } - return $Name; } -sub translateSymbols(@) -{ - my $LibVersion = pop(@_); - my (@MnglNames1, @MnglNames2, @UnmangledNames) = (); - foreach my $Symbol (sort @_) - { - if(index($Symbol, "_Z")==0) - { - next if($tr_name{$Symbol}); - $Symbol=~s/[\@\$]+(.*)\Z//; - push(@MnglNames1, $Symbol); - } - elsif(index($Symbol, "?")==0) - { - next if($tr_name{$Symbol}); - push(@MnglNames2, $Symbol); - } - else - { # not mangled - $tr_name{$Symbol} = $Symbol; - $mangled_name_gcc{$Symbol} = $Symbol; - $mangled_name{$LibVersion}{$Symbol} = $Symbol; - } +sub exitReport() +{ # the tool has run without any errors + printReport(); + if($In::Opt{"CompileError"}) + { # errors in headers may add false positives/negatives + exit(getErrorCode("Compile_Error")); } - if($#MnglNames1 > -1) - { # GCC names - @UnmangledNames = reverse(unmangleArray(@MnglNames1)); - foreach my $MnglName (@MnglNames1) - { - if(my $Unmangled = pop(@UnmangledNames)) - { - $tr_name{$MnglName} = canonifyName($Unmangled, "S"); - if(not $mangled_name_gcc{$tr_name{$MnglName}}) { - $mangled_name_gcc{$tr_name{$MnglName}} = $MnglName; - } - if(index($MnglName, "_ZTV")==0 - and $tr_name{$MnglName}=~/vtable for (.+)/) - { # bind class name and v-table symbol - my $ClassName = $1; - $ClassVTable{$ClassName} = $MnglName; - $VTableClass{$MnglName} = $ClassName; - } - } - } + if($In::Opt{"BinOnly"} and $RESULT{"Binary"}{"Problems"}) + { # --binary + exit(getErrorCode("Incompatible")); } - if($#MnglNames2 > -1) - { # MSVC names - @UnmangledNames = reverse(unmangleArray(@MnglNames2)); - foreach my $MnglName (@MnglNames2) - { - if(my $Unmangled = pop(@UnmangledNames)) - { - $tr_name{$MnglName} = formatName($Unmangled, "S"); - $mangled_name{$LibVersion}{$tr_name{$MnglName}} = $MnglName; - } - } + elsif($In::Opt{"SrcOnly"} + and $RESULT{"Source"}{"Problems"}) + { # --source + exit(getErrorCode("Incompatible")); } - return \%tr_name; -} - -sub link_symbol($$$) -{ - my ($Symbol, $RunWith, $Deps) = @_; - if(link_symbol_internal($Symbol, $RunWith, \%Symbol_Library)) { - return 1; + elsif($RESULT{"Source"}{"Problems"} + or $RESULT{"Binary"}{"Problems"}) + { # default + exit(getErrorCode("Incompatible")); } - if($Deps eq "+Deps") - { # check the dependencies - if(link_symbol_internal($Symbol, $RunWith, \%DepSymbol_Library)) { - return 1; - } + else { + exit(getErrorCode("Compatible")); } - return 0; } -sub link_symbol_internal($$$) +sub readRules($) { - my ($Symbol, $RunWith, $Where) = @_; - return 0 if(not $Where or not $Symbol); - if($Where->{$RunWith}{$Symbol}) - { # the exact match by symbol name - return 1; - } - if(my $VSym = $SymVer{$RunWith}{$Symbol}) - { # indirect symbol version, i.e. - # foo_old and its symlink foo@v (or foo@@v) - # foo_old may be in symtab table - if($Where->{$RunWith}{$VSym}) { - return 1; - } + my $Kind = $_[0]; + if(not -f $RULES_PATH{$Kind}) { + exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'"); } - my ($Sym, $Spec, $Ver) = separate_symbol($Symbol); - if($Sym and $Ver) - { # search for the symbol with the same version - # or without version - if($Where->{$RunWith}{$Sym}) - { # old: foo@v|foo@@v - # new: foo - return 1; + my $Content = readFile($RULES_PATH{$Kind}); + while(my $Rule = parseTag(\$Content, "rule")) + { + my $RId = parseTag(\$Rule, "id"); + my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind"); + foreach my $Prop (@Properties) { + if(my $Value = parseTag(\$Rule, lc($Prop))) + { + $Value=~s/\n[ ]*//; + $CompatRules{$Kind}{$RId}{$Prop} = $Value; + } } - if($Where->{$RunWith}{$Sym."\@".$Ver}) - { # old: foo|foo@@v - # new: foo@v - return 1; + if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) { + $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols"; } - if($Where->{$RunWith}{$Sym."\@\@".$Ver}) - { # old: foo|foo@v - # new: foo@@v - return 1; + else { + $CompatRules{$Kind}{$RId}{"Kind"} = "Types"; } } - return 0; } -sub readSymbols_App($) +sub getReportPath($) { - my $Path = $_[0]; - return () if(not $Path); - my @Imported = (); - if($OStarget eq "macos") + my $Level = $_[0]; + my $Dir = "compat_reports/".$In::Opt{"TargetLib"}."/".$In::Desc{1}{"Version"}."_to_".$In::Desc{2}{"Version"}; + if($Level eq "Binary") { - my $NM = get_CmdPath("nm"); - if(not $NM) { - exitStatus("Not_Found", "can't find \"nm\""); + if($In::Opt{"BinReportPath"}) + { # --bin-report-path + return $In::Opt{"BinReportPath"}; } - open(APP, "$NM -g \"$Path\" 2>\"$TMP_DIR/null\" |"); - while() - { - if(/ U _([\w\$]+)\s*\Z/) { - push(@Imported, $1); - } + elsif($In::Opt{"OutputReportPath"}) + { # --report-path + return $In::Opt{"OutputReportPath"}; + } + else + { # default + return $Dir."/abi_compat_report.".$In::Opt{"ReportFormat"}; } - close(APP); } - elsif($OStarget eq "windows") + elsif($Level eq "Source") { - my $DumpBinCmd = get_CmdPath("dumpbin"); - if(not $DumpBinCmd) { - exitStatus("Not_Found", "can't find \"dumpbin.exe\""); + if($In::Opt{"SrcReportPath"}) + { # --src-report-path + return $In::Opt{"SrcReportPath"}; } - open(APP, "$DumpBinCmd /IMPORTS \"$Path\" 2>\"$TMP_DIR/null\" |"); - while() - { - if(/\s*\w+\s+\w+\s+\w+\s+([\w\?\@]+)\s*/) { - push(@Imported, $1); - } + elsif($In::Opt{"OutputReportPath"}) + { # --report-path + return $In::Opt{"OutputReportPath"}; + } + else + { # default + return $Dir."/src_compat_report.".$In::Opt{"ReportFormat"}; } - close(APP); } else { - my $ReadelfCmd = get_CmdPath("readelf"); - if(not $ReadelfCmd) { - exitStatus("Not_Found", "can't find \"readelf\""); - } - open(APP, "$ReadelfCmd -Ws \"$Path\" 2>\"$TMP_DIR/null\" |"); - my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output - while() - { - if(defined $symtab) - { # do nothing with symtab - if(index($_, "'.dynsym'")!=-1) - { # dynamic table - $symtab = undef; - } - } - elsif(index($_, "'.symtab'")!=-1) - { # symbol table - $symtab = 1; - } - elsif(my @Info = readline_ELF($_)) - { - my ($Ndx, $Symbol) = ($Info[5], $Info[6]); - if($Ndx eq "UND") - { # only imported symbols - push(@Imported, $Symbol); - } - } - } - close(APP); - } - return @Imported; -} - -my %ELF_BIND = map {$_=>1} ( - "WEAK", - "GLOBAL" -); - -my %ELF_TYPE = map {$_=>1} ( - "FUNC", - "IFUNC", - "OBJECT", - "COMMON" -); - -my %ELF_VIS = map {$_=>1} ( - "DEFAULT", - "PROTECTED" -); - -sub readline_ELF($) -{ # read the line of 'readelf' output corresponding to the symbol - my @Info = split(/\s+/, $_[0]); - # Num: Value Size Type Bind Vis Ndx Name - # 3629: 000b09c0 32 FUNC GLOBAL DEFAULT 13 _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4 - # 135: 00000000 0 FUNC GLOBAL DEFAULT UND av_image_fill_pointers@LIBAVUTIL_52 (3) - shift(@Info); # spaces - shift(@Info); # num - - if($#Info==7) - { # UND SYMBOL (N) - if($Info[7]=~/\(\d+\)/) { - pop(@Info); + if($In::Opt{"OutputReportPath"}) + { # --report-path + return $In::Opt{"OutputReportPath"}; } - } - - if($#Info!=6) - { # other lines - return (); - } - return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UND"); - return () if(not defined $ELF_BIND{$Info[3]}); - return () if(not defined $ELF_VIS{$Info[4]}); - if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/) - { # 1272: 00000000 0 OBJECT GLOBAL DEFAULT ABS CXXABI_1.3 - return (); - } - if($OStarget eq "symbian") - { # _ZN12CCTTokenType4NewLE4TUid3RFs@@ctfinder{000a0000}[102020e5].dll - if(index($Info[6], "_._.absent_export_")!=-1) - { # "_._.absent_export_111"@@libstdcpp{00010001}[10282872].dll - return (); + else + { # default + return $Dir."/compat_report.".$In::Opt{"ReportFormat"}; } - $Info[6]=~s/\@.+//g; # remove version - } - if(index($Info[2], "0x") == 0) - { # size == 0x3d158 - $Info[2] = hex($Info[2]); } - return @Info; } -sub get_LibPath($$) +sub printStatMsg($) { - my ($LibVersion, $Name) = @_; - return "" if(not $LibVersion or not $Name); - if(defined $Cache{"get_LibPath"}{$LibVersion}{$Name}) { - return $Cache{"get_LibPath"}{$LibVersion}{$Name}; - } - return ($Cache{"get_LibPath"}{$LibVersion}{$Name} = get_LibPath_I($LibVersion, $Name)); + my $Level = $_[0]; + printMsg("INFO", "Total ".lc($Level)." compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"}); } -sub get_LibPath_I($$) +sub listAffected($) { - my ($LibVersion, $Name) = @_; - if(is_abs($Name)) + my $Level = $_[0]; + my $List = ""; + foreach (keys(%{$TotalAffected{$Level}})) { - if(-f $Name) - { # absolute path - return $Name; - } - else - { # broken - return ""; - } - } - if(defined $RegisteredObjects{$LibVersion}{$Name}) - { # registered paths - return $RegisteredObjects{$LibVersion}{$Name}; - } - if(defined $RegisteredSONAMEs{$LibVersion}{$Name}) - { # registered paths - return $RegisteredSONAMEs{$LibVersion}{$Name}; - } - if(my $DefaultPath = $DyLib_DefaultPath{$Name}) - { # ldconfig default paths - return $DefaultPath; - } - foreach my $Dir (@DefaultLibPaths, @{$SystemPaths{"lib"}}) - { # search in default linker directories - # and then in all system paths - if(-f $Dir."/".$Name) { - return join_P($Dir,$Name); + if($In::Opt{"StrictCompat"} and $TotalAffected{$Level}{$_} eq "Low") + { # skip "Low"-severity problems + next; } + $List .= "$_\n"; } - if(not defined $Cache{"checkSystemFiles"}) { - checkSystemFiles(); - } - if(my @AllObjects = keys(%{$SystemObjects{$Name}})) { - return $AllObjects[0]; + my $Dir = getDirname(getReportPath($Level)); + if($Level eq "Binary") { + writeFile($Dir."/abi_affected.txt", $List); } - if(my $ShortName = parse_libname($Name, "name+ext", $OStarget)) - { - if($ShortName ne $Name) - { # FIXME: check this case - if(my $Path = get_LibPath($LibVersion, $ShortName)) { - return $Path; - } - } + elsif($Level eq "Source") { + writeFile($Dir."/src_affected.txt", $List); } - # can't find - return ""; } -sub readSymbols_Lib($$$$$$) +sub printReport() { - my ($LibVersion, $Lib_Path, $IsNeededLib, $Weak, $Deps, $Vers) = @_; - return () if(not $LibVersion or not $Lib_Path); - - my $Real_Path = realpath($Lib_Path); - - if(not $Real_Path) - { # broken link - return (); - } + printMsg("INFO", "Creating compatibility report ..."); - my $Lib_Name = get_filename($Real_Path); - - if($ExtraInfo) - { - $KnownLibs{$Real_Path} = 1; - $KnownLibs{$Lib_Path} = 1; # links - } + createReport(); - if($IsNeededLib) + if($In::Opt{"JoinReport"} or $In::Opt{"DoubleReport"}) { - if($CheckedDyLib{$LibVersion}{$Lib_Name}) { - return (); - } - } - return () if(isCyclical(\@RecurLib, $Lib_Name) or $#RecurLib>=1); - $CheckedDyLib{$LibVersion}{$Lib_Name} = 1; - - push(@RecurLib, $Lib_Name); - my (%Value_Interface, %Interface_Value, %NeededLib) = (); - my $Lib_ShortName = parse_libname($Lib_Name, "name+ext", $OStarget); - - if(not $IsNeededLib) - { # special cases: libstdc++ and libc - if(my $ShortName = parse_libname($Lib_Name, "short", $OStarget)) - { - if($ShortName eq "libstdc++") - { # libstdc++.so.6 - $STDCXX_TESTING = 1; - } - elsif($ShortName eq "libc") - { # libc-2.11.3.so - $GLIBC_TESTING = 1; - } - } - } - my $DebugPath = ""; - if($Debug and not $DumpSystem) - { # debug mode - $DebugPath = $DEBUG_PATH{$LibVersion}."/libs/".get_filename($Lib_Path).".txt"; - mkpath(get_dirname($DebugPath)); - } - if($OStarget eq "macos") - { # Mac OS X: *.dylib, *.a - my $NM = get_CmdPath("nm"); - if(not $NM) { - exitStatus("Not_Found", "can't find \"nm\""); - } - $NM .= " -g \"$Lib_Path\" 2>\"$TMP_DIR/null\""; - if($DebugPath) - { # debug mode - # write to file - system($NM." >\"$DebugPath\""); - open(LIB, $DebugPath); - } - else - { # write to pipe - open(LIB, $NM." |"); - } - while() - { - if($CheckUndefined) - { - if(not $IsNeededLib) - { - if(/ U _([\w\$]+)\s*\Z/) - { - $UndefinedSymbols{$LibVersion}{$Lib_Name}{$1} = 0; - next; - } - } - } - - if(/ [STD] _([\w\$]+)\s*\Z/) - { - my $Symbol = $1; - if($IsNeededLib) - { - if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName}) - { - $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name; - $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1; - } - } - else - { - $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name; - $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1; - if($COMMON_LANGUAGE{$LibVersion} ne "C++") - { - if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) { - setLanguage($LibVersion, "C++"); - } - } - } - } - } - close(LIB); - - if($Deps) - { - if($LIB_TYPE eq "dynamic") - { # dependencies - - my $OtoolCmd = get_CmdPath("otool"); - if(not $OtoolCmd) { - exitStatus("Not_Found", "can't find \"otool\""); - } - - open(LIB, "$OtoolCmd -L \"$Lib_Path\" 2>\"$TMP_DIR/null\" |"); - while() - { - if(/\s*([\/\\].+\.$LIB_EXT)\s*/ - and $1 ne $Lib_Path) { - $NeededLib{$1} = 1; - } - } - close(LIB); - } - } - } - elsif($OStarget eq "windows") - { # Windows *.dll, *.lib - my $DumpBinCmd = get_CmdPath("dumpbin"); - if(not $DumpBinCmd) { - exitStatus("Not_Found", "can't find \"dumpbin\""); - } - $DumpBinCmd .= " /EXPORTS \"".$Lib_Path."\" 2>$TMP_DIR/null"; - if($DebugPath) - { # debug mode - # write to file - system($DumpBinCmd." >\"$DebugPath\""); - open(LIB, $DebugPath); - } - else - { # write to pipe - open(LIB, $DumpBinCmd." |"); - } - while() - { # 1197 4AC 0000A620 SetThreadStackGuarantee - # 1198 4AD SetThreadToken (forwarded to ...) - # 3368 _o2i_ECPublicKey - # 1 0 00005B30 ??0?N = ... (with pdb) - if(/\A\s*\d+\s+[a-f\d]+\s+[a-f\d]+\s+([\w\?\@]+)\s*(?:=.+)?\Z/i - or /\A\s*\d+\s+[a-f\d]+\s+([\w\?\@]+)\s*\(\s*forwarded\s+/ - or /\A\s*\d+\s+_([\w\?\@]+)\s*(?:=.+)?\Z/) - { # dynamic, static and forwarded symbols - my $realname = $1; - if($IsNeededLib) - { - if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName}) - { - $DepSymbol_Library{$LibVersion}{$realname} = $Lib_Name; - $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1; - } - } - else - { - $Symbol_Library{$LibVersion}{$realname} = $Lib_Name; - $Library_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1; - if($COMMON_LANGUAGE{$LibVersion} ne "C++") - { - if(index($realname, "_Z")==0 or index($realname, "?")==0) { - setLanguage($LibVersion, "C++"); - } - } - } - } - } - close(LIB); - - if($Deps) + if($RESULT{"Binary"}{"Problems"} + or $RESULT{"Source"}{"Problems"}) { - if($LIB_TYPE eq "dynamic") - { # dependencies - open(LIB, "$DumpBinCmd /DEPENDENTS \"$Lib_Path\" 2>\"$TMP_DIR/null\" |"); - while() - { - if(/\s*([^\s]+?\.$LIB_EXT)\s*/i - and $1 ne $Lib_Path) { - $NeededLib{path_format($1, $OSgroup)} = 1; - } - } - close(LIB); - } - } - } - else - { # Unix; *.so, *.a - # Symbian: *.dso, *.lib - my $ReadelfCmd = get_CmdPath("readelf"); - if(not $ReadelfCmd) { - exitStatus("Not_Found", "can't find \"readelf\""); - } - my $Cmd = $ReadelfCmd." -Ws \"$Lib_Path\" 2>\"$TMP_DIR/null\""; - if($DebugPath) - { # debug mode - # write to file - system($Cmd." >\"$DebugPath\""); - open(LIB, $DebugPath); + printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%"); + printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%"); } else - { # write to pipe - open(LIB, $Cmd." |"); - } - my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output - while() { - if($LIB_TYPE eq "dynamic") - { # dynamic library specifics - if(defined $symtab) - { - if(index($_, "'.dynsym'")!=-1) - { # dynamic table - $symtab = undef; - } - # do nothing with symtab - next; - } - elsif(index($_, "'.symtab'")!=-1) - { # symbol table - $symtab = 1; - next; - } - } - if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_)) - { # read ELF entry - if($Ndx eq "UND") - { # ignore interfaces that are imported from somewhere else - if($CheckUndefined) - { - if(not $IsNeededLib) { - $UndefinedSymbols{$LibVersion}{$Lib_Name}{$Symbol} = 0; - } - } - next; - } - if($Bind eq "WEAK") - { - $WeakSymbols{$LibVersion}{$Symbol} = 1; - if($Weak eq "-Weak") - { # skip WEAK symbols - next; - } - } - my $Short = $Symbol; - $Short=~s/\@.+//g; - if($Type eq "OBJECT") - { # global data - $GlobalDataObject{$LibVersion}{$Symbol} = $Size; - $GlobalDataObject{$LibVersion}{$Short} = $Size; - } - if($IsNeededLib) - { - if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName}) - { - $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name; - $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1; - } - } - else - { - $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name; - $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1; - if($Vers) - { - if($LIB_EXT eq "so") - { # value - $Interface_Value{$LibVersion}{$Symbol} = $Value; - $Value_Interface{$LibVersion}{$Value}{$Symbol} = 1; - } - } - if($COMMON_LANGUAGE{$LibVersion} ne "C++") - { - if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) { - setLanguage($LibVersion, "C++"); - } - } - } - } + printMsg("INFO", "Binary compatibility: 100\%"); + printMsg("INFO", "Source compatibility: 100\%"); } - close(LIB); + printStatMsg("Binary"); + printStatMsg("Source"); - if($Deps and $LIB_TYPE eq "dynamic") - { # dynamic library specifics - $Cmd = $ReadelfCmd." -Wd \"$Lib_Path\" 2>\"$TMP_DIR/null\""; - open(LIB, $Cmd." |"); - - while() - { - if(/NEEDED.+\[([^\[\]]+)\]/) - { # dependencies: - # 0x00000001 (NEEDED) Shared library: [libc.so.6] - $NeededLib{$1} = 1; - } - } - - close(LIB); + if($In::Opt{"ListAffected"}) + { # --list-affected + listAffected("Binary"); + listAffected("Source"); } } - if($Vers) + elsif($In::Opt{"BinOnly"}) { - if(not $IsNeededLib and $LIB_EXT eq "so") - { # get symbol versions - my %Found = (); - - # by value - foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}})) - { - next if(index($Symbol,"\@")==-1); - if(my $Value = $Interface_Value{$LibVersion}{$Symbol}) - { - foreach my $Symbol_SameValue (keys(%{$Value_Interface{$LibVersion}{$Value}})) - { - if($Symbol_SameValue ne $Symbol - and index($Symbol_SameValue,"\@")==-1) - { - $SymVer{$LibVersion}{$Symbol_SameValue} = $Symbol; - $Found{$Symbol} = 1; - last; - } - } - } - } - - # default - foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}})) - { - next if(defined $Found{$Symbol}); - next if(index($Symbol,"\@\@")==-1); - - if($Symbol=~/\A([^\@]*)\@\@/ - and not $SymVer{$LibVersion}{$1}) - { - $SymVer{$LibVersion}{$1} = $Symbol; - $Found{$Symbol} = 1; - } - } - - # non-default - foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}})) - { - next if(defined $Found{$Symbol}); - next if(index($Symbol,"\@")==-1); - - if($Symbol=~/\A([^\@]*)\@([^\@]*)/ - and not $SymVer{$LibVersion}{$1}) - { - $SymVer{$LibVersion}{$1} = $Symbol; - $Found{$Symbol} = 1; - } - } + if($RESULT{"Binary"}{"Problems"}) { + printMsg("INFO", "Binary compatibility: ".(100-$RESULT{"Binary"}{"Affected"})."\%"); } - } - if($Deps) - { - foreach my $DyLib (sort keys(%NeededLib)) - { - $Library_Needed{$LibVersion}{$Lib_Name}{get_filename($DyLib)} = 1; - - if(my $DepPath = get_LibPath($LibVersion, $DyLib)) - { - if(not $CheckedDyLib{$LibVersion}{get_filename($DepPath)}) { - readSymbols_Lib($LibVersion, $DepPath, 1, "+Weak", $Deps, $Vers); - } - } + else { + printMsg("INFO", "Binary compatibility: 100\%"); } - } - pop(@RecurLib); - return $Library_Symbol{$LibVersion}; -} - -sub get_prefixes($) -{ - my %Prefixes = (); - get_prefixes_I([$_[0]], \%Prefixes); - return keys(%Prefixes); -} - -sub get_prefixes_I($$) -{ - foreach my $P (@{$_[0]}) - { - my @Parts = reverse(split(/[\/\\]+/, $P)); - my $Name = $Parts[0]; - foreach (1 .. $#Parts) - { - $_[1]->{$Name}{$P} = 1; - last if($_>4 or $Parts[$_] eq "include"); - $Name = $Parts[$_].$SLASH.$Name; + printStatMsg("Binary"); + + if($In::Opt{"ListAffected"}) + { # --list-affected + listAffected("Binary"); } } -} - -sub checkSystemFiles() -{ - $Cache{"checkSystemFiles"} = 1; - - my @SysHeaders = (); - - foreach my $DevelPath (@{$SystemPaths{"lib"}}) + elsif($In::Opt{"SrcOnly"}) { - next if(not -d $DevelPath); - - my @Files = cmd_find($DevelPath,"f"); - foreach my $Link (cmd_find($DevelPath,"l")) - { # add symbolic links - if(-f $Link) { - push(@Files, $Link); - } + if($RESULT{"Source"}{"Problems"}) { + printMsg("INFO", "Source compatibility: ".(100-$RESULT{"Source"}{"Affected"})."\%"); } + else { + printMsg("INFO", "Source compatibility: 100\%"); + } + printStatMsg("Source"); - # search for headers in /usr/lib - my @Headers = grep { /\.h(pp|xx)?\Z|\/include\// } @Files; - @Headers = grep { not /\/(gcc|jvm|syslinux|kbd|parrot|xemacs|perl|llvm)/ } @Headers; - push(@SysHeaders, @Headers); - - # search for libraries in /usr/lib (including symbolic links) - my @Libs = grep { /\.$LIB_EXT[0-9.]*\Z/ } @Files; - foreach my $Path (@Libs) - { - my $N = get_filename($Path); - $SystemObjects{$N}{$Path} = 1; - $SystemObjects{parse_libname($N, "name+ext", $OStarget)}{$Path} = 1; + if($In::Opt{"ListAffected"}) + { # --list-affected + listAffected("Source"); } } - foreach my $DevelPath (@{$SystemPaths{"include"}}) - { - next if(not -d $DevelPath); - # search for all header files in the /usr/include - # with or without extension (ncurses.h, QtCore, ...) - push(@SysHeaders, cmd_find($DevelPath,"f")); - foreach my $Link (cmd_find($DevelPath,"l")) - { # add symbolic links - if(-f $Link) { - push(@SysHeaders, $Link); - } - } - } - get_prefixes_I(\@SysHeaders, \%SystemHeaders); -} - -sub getSOPaths($) -{ - my $LibVersion = $_[0]; - my @Paths = (); - foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Libs"})) + if($In::Opt{"StdOut"}) { - if(not -e $Dest) { - exitStatus("Access_Error", "can't access \'$Dest\'"); + if($In::Opt{"JoinReport"} or not $In::Opt{"DoubleReport"}) + { # --binary or --source + printMsg("INFO", "Compatibility report has been generated to stdout"); } - $Dest = get_abs_path($Dest); - my @SoPaths_Dest = getSOPaths_Dest($Dest, $LibVersion); - foreach (@SoPaths_Dest) { - push(@Paths, $_); + else + { # default + printMsg("INFO", "Compatibility reports have been generated to stdout"); } } - return sort @Paths; -} - -sub skipLib($$) -{ - my ($Path, $LibVersion) = @_; - return 1 if(not $Path or not $LibVersion); - my $Name = get_filename($Path); - if($SkipLibs{$LibVersion}{"Name"}{$Name}) { - return 1; - } - my $ShortName = parse_libname($Name, "name+ext", $OStarget); - if($SkipLibs{$LibVersion}{"Name"}{$ShortName}) { - return 1; - } - foreach my $Dir (keys(%{$SkipLibs{$LibVersion}{"Path"}})) + else { - if($Path=~/\Q$Dir\E([\/\\]|\Z)/) { - return 1; + if($In::Opt{"JoinReport"}) + { # default + printMsg("INFO", "Report: ".pathFmt(getReportPath("Join"))); } - } - foreach my $P (keys(%{$SkipLibs{$LibVersion}{"Pattern"}})) - { - if($Name=~/$P/) { - return 1; + elsif($In::Opt{"DoubleReport"}) + { + printMsg("INFO", "Report (BC): ".pathFmt(getReportPath("Binary"))); + printMsg("INFO", "Report (SC): ".pathFmt(getReportPath("Source"))); } - if($P=~/[\/\\]/ and $Path=~/$P/) { - return 1; + elsif($In::Opt{"BinOnly"}) + { # --binary + printMsg("INFO", "Report: ".pathFmt(getReportPath("Binary"))); + } + elsif($In::Opt{"SrcOnly"}) + { # --source + printMsg("INFO", "Report: ".pathFmt(getReportPath("Source"))); } } - return 0; } -sub specificHeader($$) +sub unpackDump($) { - my ($Header, $Spec) = @_; - my $Name = get_filename($Header); + my $Path = $_[0]; - if($Spec eq "windows") - {# MS Windows - return 1 if($Name=~/(\A|[._-])(win|wince|wnt)(\d\d|[._-]|\Z)/i); - return 1 if($Name=~/([._-]w|win)(32|64)/i); - return 1 if($Name=~/\A(Win|Windows)[A-Z]/); - return 1 if($Name=~/\A(w|win|windows)(32|64|\.)/i); - my @Dirs = ( - "win32", - "win64", - "win", - "windows", - "msvcrt" - ); # /gsf-win32/ - if(my $DIRs = join("|", @Dirs)) { - return 1 if($Header=~/[\/\\](|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i); - } - } - elsif($Spec eq "macos") - { # Mac OS - return 1 if($Name=~/(\A|[_-])mac[._-]/i); - } + $Path = getAbsPath($Path); + my ($Dir, $FileName) = sepPath($Path); - return 0; -} - -sub skipAlienHeader($) -{ - my $Path = $_[0]; - my $Name = get_filename($Path); - my $Dir = get_dirname($Path); + my $TmpDir = $In::Opt{"Tmp"}; + my $UnpackDir = $TmpDir."/unpack"; + rmtree($UnpackDir); + mkpath($UnpackDir); - if($Tolerance=~/2/) - { # 2 - skip internal headers - my @Terms = ( - "p", - "priv", - "int", - "impl", - "implementation", - "internal", - "private", - "old", - "compat", - "debug", - "test", - "gen" - ); - - my @Dirs = ( - "private", - "priv", - "port", - "impl", - "internal", - "detail", - "details", - "old", - "compat", - "debug", - "config", - "compiler", - "platform", - "test" - ); - - if(my $TERMs = join("|", @Terms)) { - return 1 if($Name=~/(\A|[._-])($TERMs)([._-]|\Z)/i); + if($FileName=~s/\Q.zip\E\Z//g) + { # *.zip + my $UnzipCmd = getCmdPath("unzip"); + if(not $UnzipCmd) { + exitStatus("Not_Found", "can't find \"unzip\" command"); } - if(my $DIRs = join("|", @Dirs)) { - return 1 if($Dir=~/(\A|[\/\\])(|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i); + chdir($UnpackDir); + system("$UnzipCmd \"$Path\" >\"$TmpDir/null\""); + if($?) { + exitStatus("Error", "can't extract \'$Path\' ($?): $!"); } - - return 1 if($Name=~/[a-z](Imp|Impl|I|P)(\.|\Z)/); + chdir($In::Opt{"OrigDir"}); + my @Contents = cmdFind($UnpackDir, "f"); + if(not @Contents) { + exitStatus("Error", "can't extract \'$Path\'"); + } + return $Contents[0]; } - - if($Tolerance=~/1/) - { # 1 - skip non-Linux headers - if($OSgroup ne "windows") - { - if(specificHeader($Path, "windows")) { - return 1; + elsif($FileName=~s/\Q.tar.gz\E(\.\w+|)\Z//g) + { # *.tar.gz + # *.tar.gz.amd64 (dh & cdbs) + if($In::Opt{"OS"} eq "windows") + { # -xvzf option is not implemented in tar.exe (2003) + # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\" command"); + } + my $GzipCmd = getCmdPath("gzip"); + if(not $GzipCmd) { + exitStatus("Not_Found", "can't find \"gzip\" command"); + } + chdir($UnpackDir); + system("$GzipCmd -k -d -f \"$Path\""); # keep input files (-k) + if($?) { + exitStatus("Error", "can't extract \'$Path\'"); } + system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >\"$TmpDir/null\""); + if($?) { + exitStatus("Error", "can't extract \'$Path\' ($?): $!"); + } + chdir($In::Opt{"OrigDir"}); + unlink($Dir."/".$FileName.".tar"); + my @Contents = cmdFind($UnpackDir, "f"); + if(not @Contents) { + exitStatus("Error", "can't extract \'$Path\'"); + } + return $Contents[0]; } - if($OSgroup ne "macos") - { - if(specificHeader($Path, "macos")) { - return 1; + else + { # Unix, Mac + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\" command"); + } + chdir($UnpackDir); + system("$TarCmd -xvzf \"$Path\" >\"$TmpDir/null\""); + if($?) { + exitStatus("Error", "can't extract \'$Path\' ($?): $!"); + } + chdir($In::Opt{"OrigDir"}); + my @Contents = cmdFind($UnpackDir, "f"); + if(not @Contents) { + exitStatus("Error", "can't extract \'$Path\'"); } + return $Contents[0]; } } - - # valid - return 0; } -sub skipHeader($$) +sub createArchive($$) { - my ($Path, $LibVersion) = @_; - return 1 if(not $Path or not $LibVersion); - if(defined $Cache{"skipHeader"}{$Path}) { - return $Cache{"skipHeader"}{$Path}; + my ($Path, $To) = @_; + if(not $To) { + $To = "."; } - if(defined $Tolerance and $Tolerance=~/1|2/) - { # --tolerant - if(skipAlienHeader($Path)) { - return ($Cache{"skipHeader"}{$Path} = 1); + + my ($From, $Name) = sepPath($Path); + if($In::Opt{"OS"} eq "windows") + { # *.zip + my $ZipCmd = getCmdPath("zip"); + if(not $ZipCmd) { + exitStatus("Not_Found", "can't find \"zip\""); } - } - if(not keys(%{$SkipHeaders{$LibVersion}})) { - return 0; - } - return ($Cache{"skipHeader"}{$Path} = skipHeader_I(@_)); -} - -sub skipHeader_I($$) -{ # returns: - # 1 - if header should NOT be included and checked - # 2 - if header should NOT be included, but should be checked - my ($Path, $LibVersion) = @_; - my $Name = get_filename($Path); - if(my $Kind = $SkipHeaders{$LibVersion}{"Name"}{$Name}) { - return $Kind; - } - foreach my $D (sort {$SkipHeaders{$LibVersion}{"Path"}{$a} cmp $SkipHeaders{$LibVersion}{"Path"}{$b}} - keys(%{$SkipHeaders{$LibVersion}{"Path"}})) - { - if(index($Path, $D)!=-1) - { - if($Path=~/\Q$D\E([\/\\]|\Z)/) { - return $SkipHeaders{$LibVersion}{"Path"}{$D}; - } + my $Pkg = $To."/".$Name.".zip"; + unlink($Pkg); + chdir($To); + system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"".$In::Opt{"Tmp"}."/null\""); + if($?) + { # cannot allocate memory (or other problems with "zip") + chdir($In::Opt{"OrigDir"}); + exitStatus("Error", "can't pack the ABI dump: ".$!); } + chdir($In::Opt{"OrigDir"}); + unlink($Path); + return $Pkg; } - foreach my $P (sort {$SkipHeaders{$LibVersion}{"Pattern"}{$a} cmp $SkipHeaders{$LibVersion}{"Pattern"}{$b}} - keys(%{$SkipHeaders{$LibVersion}{"Pattern"}})) - { - if(my $Kind = $SkipHeaders{$LibVersion}{"Pattern"}{$P}) - { - if($Name=~/$P/) { - return $Kind; - } - if($P=~/[\/\\]/ and $Path=~/$P/) { - return $Kind; - } + else + { # *.tar.gz + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\""); } + my $GzipCmd = getCmdPath("gzip"); + if(not $GzipCmd) { + exitStatus("Not_Found", "can't find \"gzip\""); + } + my $Pkg = abs_path($To)."/".$Name.".tar.gz"; + if(-e $Pkg) { + unlink($Pkg); + } + system($TarCmd, "-C", $From, "-czf", $Pkg, $Name); + if($?) + { # cannot allocate memory (or other problems with "tar") + exitStatus("Error", "can't pack the ABI dump: ".$!); + } + unlink($Path); + return $To."/".$Name.".tar.gz"; } - - return 0; } -sub registerObject_Dir($$) +sub defaultDumpPath($$) { - my ($Dir, $LibVersion) = @_; - if(grep {$_ eq $Dir} @{$SystemPaths{"lib"}}) - { # system directory - return; - } - if($RegisteredObject_Dirs{$LibVersion}{$Dir}) - { # already registered - return; - } - foreach my $Path (find_libs($Dir,"",1)) - { - next if(ignore_path($Path)); - next if(skipLib($Path, $LibVersion)); - registerObject($Path, $LibVersion); - } - $RegisteredObject_Dirs{$LibVersion}{$Dir} = 1; + my ($N, $V) = @_; + return "abi_dumps/".$N."/".$V."/ABI.dump"; } -sub registerObject($$) +sub createABIFile($$) { - my ($Path, $LibVersion) = @_; - - my $Name = get_filename($Path); - $RegisteredObjects{$LibVersion}{$Name} = $Path; - if($OStarget=~/linux|bsd/i) - { - if(my $SONAME = getSONAME($Path)) { - $RegisteredSONAMEs{$LibVersion}{$SONAME} = $Path; - } - } - if(my $Short = parse_libname($Name, "name+ext", $OStarget)) { - $RegisteredObjects_Short{$LibVersion}{$Short} = $Path; - } + my ($LVer, $DescPath) = @_; - if(not $CheckedArch{$LibVersion} and -f $Path) - { - if(my $ObjArch = getArch_Object($Path)) - { - if($ObjArch ne getArch_GCC($LibVersion)) - { # translation unit dump generated by the GCC compiler should correspond to the input objects - $CheckedArch{$LibVersion} = 1; - printMsg("WARNING", "the architectures of input objects and the used GCC compiler are not equal, please change the compiler by --gcc-path=PATH option."); - } - } + if(not -e $DescPath) { + exitStatus("Access_Error", "can't access \'$DescPath\'"); } -} - -sub getArch_Object($) -{ - my $Path = $_[0]; - my %MachineType = ( - "14C" => "x86", - "8664" => "x86_64", - "1C0" => "arm", - "200" => "ia64" - ); + detectDefaultPaths(undef, undef, "bin", undef); - my %ArchName = ( - "s390:31-bit" => "s390", - "s390:64-bit" => "s390x", - "powerpc:common" => "ppc32", - "powerpc:common64" => "ppc64", - "i386:x86-64" => "x86_64", - "mips:3000" => "mips", - "sparc:v8plus" => "sparcv9" - ); - - if($OStarget eq "windows") + if(isDump($DescPath)) { - my $DumpbinCmd = get_CmdPath("dumpbin"); - if(not $DumpbinCmd) { - exitStatus("Not_Found", "can't find \"dumpbin\""); - } - - my $Cmd = $DumpbinCmd." /headers \"$Path\""; - my $Out = `$Cmd`; + $In::ABI{$LVer} = readABIDump($LVer, $DescPath); + initAliases($LVer); - if($Out=~/(\w+)\smachine/) - { - if(my $Type = $MachineType{uc($1)}) - { - return $Type; - } + if(my $V = $In::Desc{$LVer}{"TargetVersion"}) { + $In::Desc{$LVer}{"Version"} = $V; + } + else { + $In::Desc{$LVer}{"Version"} = $In::ABI{$LVer}{"LibraryVersion"}; } } - elsif($OStarget=~/linux|bsd/) + else { - my $ObjdumpCmd = get_CmdPath("objdump"); - if(not $ObjdumpCmd) { - exitStatus("Not_Found", "can't find \"objdump\""); - } + loadModule("ABIDump"); + readDesc(createDesc($DescPath, $LVer), $LVer); - my $Cmd = $ObjdumpCmd." -f \"$Path\""; + initLogging($LVer); - if($OSgroup eq "windows") { - $Cmd = "set LANG=$LOCALE & ".$Cmd; - } - else { - $Cmd = "LANG=$LOCALE ".$Cmd; - } - my $Out = `$Cmd`; - - if($Out=~/architecture:\s+([\w\-\:]+)/) + if($In::Opt{"Debug"}) { - my $Arch = $1; - if($Arch=~s/\:(.+)//) - { - my $Suffix = $1; - - if(my $Name = $ArchName{$Arch.":".$Suffix}) - { - $Arch = $Name; - } - } - - if($Arch=~/i[3-6]86/) { - $Arch = "x86"; - } - - if($Arch eq "x86-64") { - $Arch = "x86_64"; - } - - if($Arch eq "ia64-elf64") { - $Arch = "ia64"; + if(not $In::Opt{"ExtraInfo"}) { + $In::Opt{"ExtraInfo"} = getExtraDir($LVer); } - - return $Arch; } - } - else - { # macos, etc. - # TODO + + detectDefaultPaths("inc", "lib", undef, "gcc"); + createABIDump($LVer); } - return undef; -} - -sub getSONAME($) -{ - my $Path = $_[0]; - return if(not $Path); - if(defined $Cache{"getSONAME"}{$Path}) { - return $Cache{"getSONAME"}{$Path}; - } - my $ObjdumpCmd = get_CmdPath("objdump"); - if(not $ObjdumpCmd) { - exitStatus("Not_Found", "can't find \"objdump\""); - } - my $SonameCmd = "$ObjdumpCmd -x \"$Path\" 2>$TMP_DIR/null"; - if($OSgroup eq "windows") { - $SonameCmd .= " | find \"SONAME\""; - } - else { - $SonameCmd .= " | grep SONAME"; - } - if(my $SonameInfo = `$SonameCmd`) - { - if($SonameInfo=~/SONAME\s+([^\s]+)/) { - return ($Cache{"getSONAME"}{$Path} = $1); - } - } - return ($Cache{"getSONAME"}{$Path}=""); -} - -sub getSOPaths_Dest($$) -{ - my ($Dest, $LibVersion) = @_; - if(skipLib($Dest, $LibVersion)) { - return (); - } - if(-f $Dest) - { - if(not parse_libname($Dest, "name", $OStarget)) { - exitStatus("Error", "incorrect format of library (should be *.$LIB_EXT): \'$Dest\'"); - } - registerObject($Dest, $LibVersion); - registerObject_Dir(get_dirname($Dest), $LibVersion); - return ($Dest); - } - elsif(-d $Dest) - { - $Dest=~s/[\/\\]+\Z//g; - my %Libs = (); - if(grep { $Dest eq $_ } @{$SystemPaths{"lib"}}) - { # you have specified /usr/lib as the search directory () in the XML descriptor - # and the real name of the library by -l option (bz2, stdc++, Xaw, ...) - foreach my $Path (cmd_find($Dest,"","*".esc($TargetLibraryName)."*.$LIB_EXT*",2)) - { # all files and symlinks that match the name of a library - if(get_filename($Path)=~/\A(|lib)\Q$TargetLibraryName\E[\d\-]*\.$LIB_EXT[\d\.]*\Z/i) - { - registerObject($Path, $LibVersion); - $Libs{realpath($Path)}=1; - } - } - } - else - { # search for all files and symlinks - foreach my $Path (find_libs($Dest,"","")) - { - next if(ignore_path($Path)); - next if(skipLib($Path, $LibVersion)); - registerObject($Path, $LibVersion); - $Libs{realpath($Path)}=1; - } - if($OSgroup eq "macos") - { # shared libraries on MacOS X may have no extension - foreach my $Path (cmd_find($Dest,"f")) - { - next if(ignore_path($Path)); - next if(skipLib($Path, $LibVersion)); - if(get_filename($Path)!~/\./ - and cmd_file($Path)=~/(shared|dynamic)\s+library/i) - { - registerObject($Path, $LibVersion); - $Libs{realpath($Path)}=1; - } - } - } - } - return keys(%Libs); - } - else { - return (); - } -} - -sub isCyclical($$) -{ - my ($Stack, $Value) = @_; - return (grep {$_ eq $Value} @{$Stack}); -} - -sub getGCC_Opts($) -{ # to use in module - my $LibVersion = $_[0]; + clearSysFilesCache($LVer); - my @Opts = (); + printMsg("INFO", "Creating library ABI dump ..."); - if($CompilerOptions{$LibVersion}) - { # user-defined options - push(@Opts, $CompilerOptions{$LibVersion}); - } - if($GccOptions) - { # additional - push(@Opts, $GccOptions); - } + $In::ABI{$LVer}{"ABI_DUMP_VERSION"} = $ABI_DUMP_VERSION; + $In::ABI{$LVer}{"ABI_COMPLIANCE_CHECKER_VERSION"} = $TOOL_VERSION; - if(@Opts) { - return join(" ", @Opts); + if($In::Opt{"UseXML"}) { + $In::ABI{$LVer}{"XML_ABI_DUMP_VERSION"} = $XML_ABI_DUMP_VERSION; } - return undef; -} - -sub getArch_GCC($) -{ - my $LibVersion = $_[0]; + $In::ABI{$LVer}{"TargetHeaders"} = $In::Desc{$LVer}{"TargetHeader"}; - if(defined $Cache{"getArch_GCC"}{$LibVersion}) { - return $Cache{"getArch_GCC"}{$LibVersion}; + foreach ("SymLib", "DepSymLib", "TName_Tid", "TypeTypedef", + "TypedefBase", "Class_SubClasses", "ClassVTable") { + delete($In::ABI{$LVer}{$_}); } - my $Arch = undef; - - if($GCC_PATH) - { - writeFile("$TMP_DIR/test.c", "int main(){return 0;}\n"); - - my $Cmd = $GCC_PATH." test.c -o test"; - if(my $Opts = getGCC_Opts($LibVersion)) - { # user-defined options - $Cmd .= " ".$Opts; - } - - chdir($TMP_DIR); - system($Cmd); - chdir($ORIG_DIR); - - $Arch = getArch_Object("$TMP_DIR/test"); - - unlink("$TMP_DIR/test.c"); - unlink("$TMP_DIR/test"); - } - - if(not $Arch) { - exitStatus("Error", "can't check ARCH type"); + my $DumpPath = defaultDumpPath($In::Opt{"TargetLib"}, $In::Desc{1}{"Version"}); + if($In::Opt{"OutputDumpPath"}) + { # user defined path + $DumpPath = $In::Opt{"OutputDumpPath"}; } - return ($Cache{"getArch_GCC"}{$LibVersion} = $Arch); -} - -sub detectWordSize($) -{ - my $LibVersion = $_[0]; - - my $Size = undef; + my $ArExt = $In::Opt{"Ar"}; + my $Archive = ($DumpPath=~s/\Q.$ArExt\E\Z//g); - # speed up detection - if(my $Arch = getArch($LibVersion)) - { - if($Arch=~/\A(x86_64|s390x|ppc64|ia64|alpha)\Z/) { - $Size = "8"; + if($Archive and not $In::Opt{"StdOut"}) + { # check archive utilities + if($In::Opt{"OS"} eq "windows") + { # using zip + my $ZipCmd = getCmdPath("zip"); + if(not $ZipCmd) { + exitStatus("Not_Found", "can't find \"zip\""); + } } - elsif($Arch=~/\A(x86|s390|ppc32)\Z/) { - $Size = "4"; + else + { # using tar and gzip + my $TarCmd = getCmdPath("tar"); + if(not $TarCmd) { + exitStatus("Not_Found", "can't find \"tar\""); + } + my $GzipCmd = getCmdPath("gzip"); + if(not $GzipCmd) { + exitStatus("Not_Found", "can't find \"gzip\""); + } } } - if($GCC_PATH) + my $ABI_DUMP = ""; + if($In::Opt{"UseXML"}) { - writeFile("$TMP_DIR/empty.h", ""); - - my $Cmd = $GCC_PATH." -E -dD empty.h"; - if(my $Opts = getGCC_Opts($LibVersion)) - { # user-defined options - $Cmd .= " ".$Opts; + loadModule("XmlDump"); + $ABI_DUMP = createXmlDump($LVer); + } + else + { # default + $ABI_DUMP = Dumper($In::ABI{$LVer}); + } + if($In::Opt{"StdOut"}) + { + print STDOUT $ABI_DUMP; + printMsg("INFO", "ABI dump has been generated to stdout"); + return; + } + else + { # to file + my ($DDir, $DName) = sepPath($DumpPath); + my $DPath = $In::Opt{"Tmp"}."/".$DName; + if(not $Archive) { + $DPath = $DumpPath; } - chdir($TMP_DIR); - my $Defines = `$Cmd`; - chdir($ORIG_DIR); + mkpath($DDir); - unlink("$TMP_DIR/empty.h"); + open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n"); + print DUMP $ABI_DUMP; + close(DUMP); - if($Defines=~/ __SIZEOF_POINTER__\s+(\d+)/) - { # GCC 4 - $Size = $1; + if(not -s $DPath) { + exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module"); } - elsif($Defines=~/ __PTRDIFF_TYPE__\s+(\w+)/) - { # GCC 3 - my $PTRDIFF = $1; - if($PTRDIFF=~/long/) { - $Size = "8"; - } - else { - $Size = "4"; - } + if($Archive) { + $DumpPath = createArchive($DPath, $DDir); } + + printMsg("INFO", "Dump path: ".pathFmt($DumpPath)); } - - if(not $Size) { - exitStatus("Error", "can't check WORD size"); - } - - return $Size; -} - -sub getWordSize($) -{ # to use in module - return $WORD_SIZE{$_[0]}; -} - -sub majorVersion($) -{ - my $V = $_[0]; - return 0 if(not $V); - my @VParts = split(/\./, $V); - return $VParts[0]; -} - -sub cmpVersions($$) -{ # compare two versions in dotted-numeric format - my ($V1, $V2) = @_; - return 0 if($V1 eq $V2); - my @V1Parts = split(/\./, $V1); - my @V2Parts = split(/\./, $V2); - for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) - { - return -1 if(int($V1Parts[$i]) < int($V2Parts[$i])); - return 1 if(int($V1Parts[$i]) > int($V2Parts[$i])); - } - return -1 if($#V1Parts < $#V2Parts); - return 1 if($#V1Parts > $#V2Parts); - return 0; } -sub read_ABI_Dump($$) +sub readABIDump($$) { - my ($LibVersion, $Path) = @_; - return if(not $LibVersion or not -e $Path); + my ($LVer, $Path) = @_; + my $FilePath = ""; if(isDump_U($Path)) { # input *.abi @@ -19820,13 +9558,13 @@ } } - my $ABI = {}; + my $ABIRef = {}; my $Line = readLineNum($FilePath, 0); if($Line=~/xml/) { # XML format loadModule("XmlDump"); - $ABI = readXmlDump($FilePath); + $ABIRef = readXmlDump($FilePath); } else { # Perl Data::Dumper format (default) @@ -19835,2433 +9573,496 @@ my $Content = ; close(DUMP); - if(get_dirname($FilePath) eq $TMP_DIR."/unpack") + if(getDirname($FilePath) eq $In::Opt{"Tmp"}."/unpack") { # remove temp file unlink($FilePath); } if($Content!~/};\s*\Z/) { exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it"); } - $ABI = eval($Content); - if(not $ABI) { + $ABIRef = eval($Content); + if(not $ABIRef) { exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again"); } } - # new dumps (>=1.22) have a personal versioning - my $DVersion = $ABI->{"ABI_DUMP_VERSION"}; - my $ToolVersion = $ABI->{"ABI_COMPLIANCE_CHECKER_VERSION"}; - if(not $DVersion) - { # old dumps (<=1.21.6) have been marked by the tool version - $DVersion = $ToolVersion; - } - $UsedDump{$LibVersion}{"V"} = $DVersion; - $UsedDump{$LibVersion}{"M"} = $ABI->{"LibraryName"}; - if($ABI->{"PublicABI"}) { - $UsedDump{$LibVersion}{"Public"} = 1; - } + my $ABIVer = $ABIRef->{"ABI_DUMP_VERSION"}; - if($ABI->{"ABI_DUMP_VERSION"}) + if($ABIVer) { - if(cmpVersions($DVersion, $ABI_DUMP_VERSION)>0) - { # Don't know how to parse future dump formats - exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $ABI_DUMP_VERSION)"); - } - } - else - { # support for old ABI dumps - if(cmpVersions($DVersion, $TOOL_VERSION)>0) - { # Don't know how to parse future dump formats - exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $TOOL_VERSION)"); + if(cmpVersions($ABIVer, $ABI_DUMP_VERSION)>0) + { # future formats + exitStatus("Dump_Version", "the versions of the ABI dump is newer than version of the tool"); } } - if(majorVersion($DVersion)<2) - { - exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (allowed only 2.0<=V<=$ABI_DUMP_VERSION)"); + if(cmpVersions($ABIVer, $ABI_DUMP_VERSION_MIN)<0) { + exitStatus("Dump_Version", "the version of the ABI dump is too old and unsupported anymore, please regenerate it"); } - if(defined $ABI->{"ABI_DUMPER_VERSION"}) + if(defined $ABIRef->{"ABI_DUMPER_VERSION"}) { # DWARF ABI Dump - $UseConv_Real{$LibVersion}{"P"} = 1; - $UseConv_Real{$LibVersion}{"R"} = 0; # not implemented yet + $UseConv_Real{$LVer}{"P"} = 1; + $UseConv_Real{$LVer}{"R"} = 0; # not implemented yet - $UsedDump{$LibVersion}{"DWARF"} = 1; + $UsedDump{$LVer}{"DWARF"} = 1; - if(not $TargetComponent_Opt) - { - if($ABI->{"LibraryName"}=~/\.ko[\.\d]*\Z/) { - $TargetComponent = "module"; - } - else { - $TargetComponent = "object"; - } + if($ABIRef->{"LibraryName"}=~/\.ko(\.|\Z)/) { + $In::Opt{"TargetComponent"} = "module"; + } + else { + $In::Opt{"TargetComponent"} = "object"; } } - if(not checkDump($LibVersion, "2.11")) - { # old ABI dumps - $UsedDump{$LibVersion}{"BinOnly"} = 1; + if(index($ABIRef->{"LibraryName"}, "libstdc++")==0 + or index($ABIRef->{"LibraryName"}, "libc++")==0) { + $In::Opt{"StdcxxTesting"} = 1; } - elsif($ABI->{"BinOnly"}) + + if($ABIRef->{"BinOnly"}) { # ABI dump created with --binary option - $UsedDump{$LibVersion}{"BinOnly"} = 1; + $UsedDump{$LVer}{"BinOnly"} = 1; } else { # default - $UsedDump{$LibVersion}{"SrcBin"} = 1; + $UsedDump{$LVer}{"SrcBin"} = 1; } - if(defined $ABI->{"Mode"} - and $ABI->{"Mode"} eq "Extended") + if(defined $ABIRef->{"Mode"} + and $ABIRef->{"Mode"} eq "Extended") { # --ext option - $ExtendedCheck = 1; - } - if($ABI->{"Extra"}) { - $ExtraDump = 1; + $In::Opt{"ExtendedCheck"} = 1; } - if(my $Lang = $ABI->{"Language"}) - { - $UsedDump{$LibVersion}{"L"} = $Lang; - setLanguage($LibVersion, $Lang); - } - if(checkDump($LibVersion, "2.15")) { - $TypeInfo{$LibVersion} = $ABI->{"TypeInfo"}; - } - else - { # support for old ABI dumps - my $TInfo = $ABI->{"TypeInfo"}; - if(not $TInfo) - { # support for older ABI dumps - $TInfo = $ABI->{"TypeDescr"}; - } - my %Tid_TDid = (); - foreach my $TDid (keys(%{$TInfo})) - { - foreach my $Tid (keys(%{$TInfo->{$TDid}})) - { - $MAX_ID = $Tid if($Tid>$MAX_ID); - $MAX_ID = $TDid if($TDid and $TDid>$MAX_ID); - $Tid_TDid{$Tid}{$TDid} = 1; - } - } - my %NewID = (); - foreach my $Tid (keys(%Tid_TDid)) - { - my @TDids = keys(%{$Tid_TDid{$Tid}}); - if($#TDids>=1) - { - foreach my $TDid (@TDids) - { - if($TDid) { - %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}}; - } - else - { - my $ID = ++$MAX_ID; - - $NewID{$TDid}{$Tid} = $ID; - %{$TypeInfo{$LibVersion}{$ID}} = %{$TInfo->{$TDid}{$Tid}}; - $TypeInfo{$LibVersion}{$ID}{"Tid"} = $ID; - } - } - } - else - { - my $TDid = $TDids[0]; - %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}}; - } - } - foreach my $Tid (keys(%{$TypeInfo{$LibVersion}})) - { - my %Info = %{$TypeInfo{$LibVersion}{$Tid}}; - if(defined $Info{"BaseType"}) - { - my $Bid = $Info{"BaseType"}{"Tid"}; - my $BDid = $Info{"BaseType"}{"TDid"}; - $BDid="" if(not defined $BDid); - delete($TypeInfo{$LibVersion}{$Tid}{"BaseType"}{"TDid"}); - if(defined $NewID{$BDid} and my $ID = $NewID{$BDid}{$Bid}) { - $TypeInfo{$LibVersion}{$Tid}{"BaseType"} = $ID; - } - } - delete($TypeInfo{$LibVersion}{$Tid}{"TDid"}); - } - } - read_Machine_DumpInfo($ABI, $LibVersion); - $SymbolInfo{$LibVersion} = $ABI->{"SymbolInfo"}; - if(not $SymbolInfo{$LibVersion}) - { # support for old dumps - $SymbolInfo{$LibVersion} = $ABI->{"FuncDescr"}; + if($ABIRef->{"Extra"}) { + $In::Opt{"ExtraDump"} = 1; } - if(not keys(%{$SymbolInfo{$LibVersion}})) + + if(not keys(%{$ABIRef->{"SymbolInfo"}})) { # validation of old-version dumps - if(not $ExtendedCheck) { - exitStatus("Invalid_Dump", "the input dump d$LibVersion is invalid"); - } - } - if(checkDump($LibVersion, "2.15")) { - $DepLibrary_Symbol{$LibVersion} = $ABI->{"DepSymbols"}; - } - else - { # support for old ABI dumps - my $DepSymbols = $ABI->{"DepSymbols"}; - if(not $DepSymbols) { - $DepSymbols = $ABI->{"DepInterfaces"}; - } - if(not $DepSymbols) - { # Cannot reconstruct DepSymbols. This may result in false - # positives if the old dump is for library 2. Not a problem if - # old dumps are only from old libraries. - $DepSymbols = {}; - } - foreach my $Symbol (keys(%{$DepSymbols})) { - $DepSymbol_Library{$LibVersion}{$Symbol} = 1; + if(not $In::Opt{"ExtendedCheck"}) { + exitStatus("Invalid_Dump", "no symbols info in the ABI dump"); } } - $SymVer{$LibVersion} = $ABI->{"SymbolVersion"}; - if(my $V = $TargetVersion{$LibVersion}) { - $Descriptor{$LibVersion}{"Version"} = $V; - } - else { - $Descriptor{$LibVersion}{"Version"} = $ABI->{"LibraryVersion"}; - } - - if(not $SkipTypes{$LibVersion}) - { # if not defined by -skip-types option - if(defined $ABI->{"SkipTypes"}) - { - foreach my $TName (keys(%{$ABI->{"SkipTypes"}})) - { - $SkipTypes{$LibVersion}{$TName} = 1; - } - } - if(defined $ABI->{"OpaqueTypes"}) - { # support for old dumps - foreach my $TName (keys(%{$ABI->{"OpaqueTypes"}})) - { - $SkipTypes{$LibVersion}{$TName} = 1; - } + if(defined $ABIRef->{"GccConstants"}) + { # support for 3.0 + foreach my $Name (keys(%{$ABIRef->{"GccConstants"}})) { + $ABIRef->{"Constants"}{$Name}{"Value"} = $ABIRef->{"GccConstants"}{$Name}; } } - - if(not $SkipSymbols{$LibVersion}) - { # if not defined by -skip-symbols option - $SkipSymbols{$LibVersion} = $ABI->{"SkipSymbols"}; - if(not $SkipSymbols{$LibVersion}) - { # support for old dumps - $SkipSymbols{$LibVersion} = $ABI->{"SkipInterfaces"}; - } - if(not $SkipSymbols{$LibVersion}) - { # support for old dumps - $SkipSymbols{$LibVersion} = $ABI->{"InternalInterfaces"}; + elsif(defined $ABIRef->{"CompilerConstants"}) + { + foreach my $Name (keys(%{$ABIRef->{"CompilerConstants"}})) { + $ABIRef->{"Constants"}{$Name}{"Value"} = $ABIRef->{"CompilerConstants"}{$Name}; } } - $SkipNameSpaces{$LibVersion} = $ABI->{"SkipNameSpaces"}; - - if(not $TargetHeaders{$LibVersion}) - { # if not defined by -headers-list option - $TargetHeaders{$LibVersion} = $ABI->{"TargetHeaders"}; - } - foreach my $Path (keys(%{$ABI->{"SkipHeaders"}})) - { - $SkipHeadersList{$LibVersion}{$Path} = $ABI->{"SkipHeaders"}{$Path}; - my ($CPath, $Type) = classifyPath($Path); - $SkipHeaders{$LibVersion}{$Type}{$CPath} = $ABI->{"SkipHeaders"}{$Path}; + if(not $ABIRef->{"SymbolVersion"}) { + $ABIRef->{"SymbolVersion"} = $ABIRef->{"SymVer"}; } - read_Source_DumpInfo($ABI, $LibVersion); - read_Libs_DumpInfo($ABI, $LibVersion); - - if(not checkDump($LibVersion, "2.10.1") - or not $TargetHeaders{$LibVersion}) - { # support for old ABI dumps: added target headers - foreach (keys(%{$Registered_Headers{$LibVersion}})) { - $TargetHeaders{$LibVersion}{get_filename($_)} = 1; - } - - if(not $ABI->{"PublicABI"}) - { - foreach (keys(%{$Registered_Sources{$LibVersion}})) { - $TargetHeaders{$LibVersion}{get_filename($_)} = 1; - } - } - } - $Constants{$LibVersion} = $ABI->{"Constants"}; - if(defined $ABI->{"GccConstants"}) - { # 3.0 - foreach my $Name (keys(%{$ABI->{"GccConstants"}})) { - $Constants{$LibVersion}{$Name}{"Value"} = $ABI->{"GccConstants"}{$Name}; - } + if(defined $ABIRef->{"TargetHeaders"}) { + $In::Desc{$LVer}{"TargetHeader"} = $ABIRef->{"TargetHeaders"}; } - $NestedNameSpaces{$LibVersion} = $ABI->{"NameSpaces"}; - if(not $NestedNameSpaces{$LibVersion}) - { # support for old dumps - # Cannot reconstruct NameSpaces. This may affect design - # of the compatibility report. - $NestedNameSpaces{$LibVersion} = {}; - } - # target system type - # needed to adopt HTML report - if(not $DumpSystem) - { # to use in createSymbolsList(...) - $OStarget = $ABI->{"Target"}; - } - # recreate environment - foreach my $Lib_Name (keys(%{$Library_Symbol{$LibVersion}})) + foreach my $LName (keys(%{$ABIRef->{"Symbols"}})) { - foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}})) - { - $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name; - if($Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol}<=-1) - { # data marked as -size in the dump - $GlobalDataObject{$LibVersion}{$Symbol} = -$Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol}; - } - if($COMMON_LANGUAGE{$LibVersion} ne "C++") - { - if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) { - setLanguage($LibVersion, "C++"); - } - } - } - } - foreach my $Lib_Name (keys(%{$DepLibrary_Symbol{$LibVersion}})) - { - foreach my $Symbol (keys(%{$DepLibrary_Symbol{$LibVersion}{$Lib_Name}})) { - $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name; + foreach my $Symbol (keys(%{$ABIRef->{"Symbols"}{$LName}})) { + $ABIRef->{"SymLib"}{$Symbol} = $LName; } } - my @VFunc = (); - foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}})) + foreach my $LName (keys(%{$ABIRef->{"DepSymbols"}})) { - if(my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"}) - { - if(not $Symbol_Library{$LibVersion}{$MnglName} - and not $DepSymbol_Library{$LibVersion}{$MnglName}) { - push(@VFunc, $MnglName); - } + foreach my $Symbol (keys(%{$ABIRef->{"DepSymbols"}{$LName}})) { + $ABIRef->{"DepSymLib"}{$Symbol} = $LName; } } - translateSymbols(@VFunc, $LibVersion); - translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion); - translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion); - if(not checkDump($LibVersion, "3.0")) - { # support for old ABI dumps - foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { - if(my $BaseType = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"}) - { - if(ref($BaseType) eq "HASH") { - $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"}{"Tid"}; - } - } - } - } + $In::Opt{"Target"} = $ABIRef->{"Target"}; + $In::Desc{$LVer}{"Dump"} = 1; - if(not checkDump($LibVersion, "3.2")) - { # support for old ABI dumps - foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { - if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"}) - { - foreach my $Offset (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"VTable"}})) { - $TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset} = simplifyVTable($TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset}); - } - } - } - - # repair target headers list - delete($TargetHeaders{$LibVersion}); - foreach (keys(%{$Registered_Headers{$LibVersion}})) { - $TargetHeaders{$LibVersion}{get_filename($_)} = 1; - } - foreach (keys(%{$Registered_Sources{$LibVersion}})) { - $TargetHeaders{$LibVersion}{get_filename($_)} = 1; - } - - # non-target constants from anon enums - foreach my $Name (keys(%{$Constants{$LibVersion}})) - { - if(not $ExtraDump - and not is_target_header($Constants{$LibVersion}{$Name}{"Header"}, $LibVersion)) - { - delete($Constants{$LibVersion}{$Name}); - } - } - } + return $ABIRef; +} + +sub prepareCompare($) +{ + my $LVer = $_[0]; - if(not checkDump($LibVersion, "2.20")) - { # support for old ABI dumps - foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) + foreach my $Lib_Name (keys(%{$In::ABI{$LVer}{"Symbols"}})) + { + foreach my $Symbol (keys(%{$In::ABI{$LVer}{"Symbols"}{$Lib_Name}})) { - my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"}; - - if($TType=~/Struct|Union|Enum|Typedef/) - { # repair complex types first - next; - } - - if(my $BaseId = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"}) - { - my $BType = lc($TypeInfo{$LibVersion}{$BaseId}{"Type"}); - if($BType=~/Struct|Union|Enum/i) - { - my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"}; - $TypeInfo{$LibVersion}{$TypeId}{"Name"}=~s/\A\Q$BName\E\b/$BType $BName/g; + if($In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol}<0) + { # data marked as -size in the dump + $GlobalDataObject{$LVer}{$Symbol} = -$In::ABI{$LVer}{"Symbols"}{$Lib_Name}{$Symbol}; + + if($Symbol=~/\A(.+?)\@.+/) { + $GlobalDataObject{$LVer}{$1} = $GlobalDataObject{$LVer}{$Symbol}; } } } - foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { - my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"}; - my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"}; - if($TType=~/Struct|Union|Enum/) { - $TypeInfo{$LibVersion}{$TypeId}{"Name"} = lc($TType)." ".$TName; - } - } } - foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}})) - { # order is important - if(defined $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}) - { # support for old ABI dumps < 2.0 (ACC 1.22) - foreach my $BId (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}})) - { - if(my $Access = $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}{$BId}) - { - if($Access ne "public") { - $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId}{"access"} = $Access; - } - } - $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId} = {}; - } - delete($TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}); - } - if(my $Header = $TypeInfo{$LibVersion}{$TypeId}{"Header"}) - { # support for old ABI dumps - $TypeInfo{$LibVersion}{$TypeId}{"Header"} = path_format($Header, $OSgroup); - } - elsif(my $Source = $TypeInfo{$LibVersion}{$TypeId}{"Source"}) - { # DWARF ABI Dumps - $TypeInfo{$LibVersion}{$TypeId}{"Header"} = $Source; - } - if(not defined $TypeInfo{$LibVersion}{$TypeId}{"Tid"}) { - $TypeInfo{$LibVersion}{$TypeId}{"Tid"} = $TypeId; - } - my %TInfo = %{$TypeInfo{$LibVersion}{$TypeId}}; - if(defined $TInfo{"Base"}) - { - foreach (keys(%{$TInfo{"Base"}})) { - $Class_SubClasses{$LibVersion}{$_}{$TypeId}=1; - } + foreach my $TypeId (sort {$a<=>$b} keys(%{$TypeInfo{$LVer}})) + { # NOTE: order is important + if(not defined $TypeInfo{$LVer}{$TypeId}{"Tid"}) { + $TypeInfo{$LVer}{$TypeId}{"Tid"} = $TypeId; } - if($TInfo{"Type"} eq "MethodPtr") + + my $TInfo = $TypeInfo{$LVer}{$TypeId}; + if(defined $TInfo->{"Base"}) { - if(defined $TInfo{"Param"}) - { # support for old ABI dumps <= 1.17 - if(not defined $TInfo{"Param"}{"0"}) - { - my $Max = keys(%{$TInfo{"Param"}}); - foreach my $Pos (1 .. $Max) { - $TInfo{"Param"}{$Pos-1} = $TInfo{"Param"}{$Pos}; - } - delete($TInfo{"Param"}{$Max}); - %{$TypeInfo{$LibVersion}{$TypeId}} = %TInfo; + foreach my $SubId (keys(%{$TInfo->{"Base"}})) + { + if($SubId eq $TypeId) + { # Fix erroneus ABI dump + delete($TypeInfo{$LVer}{$TypeId}{"Base"}{$SubId}); + next; } + + $In::ABI{$LVer}{"Class_SubClasses"}{$SubId}{$TypeId} = 1; } } - if($TInfo{"BaseType"} eq $TypeId) + + if($TInfo->{"BaseType"} eq $TypeId) { # fix ABI dump - delete($TypeInfo{$LibVersion}{$TypeId}{"BaseType"}); + delete($TypeInfo{$LVer}{$TypeId}{"BaseType"}); } - if($TInfo{"Type"} eq "Typedef" and not $TInfo{"Artificial"}) + + if($TInfo->{"Type"} eq "Typedef" and not $TInfo->{"Artificial"}) { - if(my $BTid = $TInfo{"BaseType"}) + if(my $BTid = $TInfo->{"BaseType"}) { - my $BName = $TypeInfo{$LibVersion}{$BTid}{"Name"}; + my $BName = $TypeInfo{$LVer}{$BTid}{"Name"}; if(not $BName) { # broken type next; } - if($TInfo{"Name"} eq $BName) + if($TInfo->{"Name"} eq $BName) { # typedef to "class Class" # should not be registered in TName_Tid next; } - if(not $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}}) { - $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}} = $BName; + if(not $In::ABI{$LVer}{"TypedefBase"}{$TInfo->{"Name"}}) { + $In::ABI{$LVer}{"TypedefBase"}{$TInfo->{"Name"}} = $BName; } } } - if(not $TName_Tid{$LibVersion}{$TInfo{"Name"}}) + if(not $TName_Tid{$LVer}{$TInfo->{"Name"}}) { # classes: class (id1), typedef (artificial, id2 > id1) - $TName_Tid{$LibVersion}{formatName($TInfo{"Name"}, "T")} = $TypeId; - } - } - - if(not checkDump($LibVersion, "2.15")) - { # support for old ABI dumps - my %Dups = (); - foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}})) - { - if(my $ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"}) - { - if(not defined $TypeInfo{$LibVersion}{$ClassId}) - { # remove template decls - delete($SymbolInfo{$LibVersion}{$InfoId}); - next; - } - } - my $MName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"}; - if(not $MName and $SymbolInfo{$LibVersion}{$InfoId}{"Class"}) - { # templates - delete($SymbolInfo{$LibVersion}{$InfoId}); - } + $TName_Tid{$LVer}{$TInfo->{"Name"}} = $TypeId; } } - - foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}})) - { - if(my $Class = $SymbolInfo{$LibVersion}{$InfoId}{"Class"} - and not $SymbolInfo{$LibVersion}{$InfoId}{"Static"} - and not $SymbolInfo{$LibVersion}{$InfoId}{"Data"}) - { # support for old ABI dumps (< 3.1) - if(not defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"} - or $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this") - { # add "this" first parameter - my $ThisTid = getTypeIdByName($TypeInfo{$LibVersion}{$Class}{"Name"}."*const", $LibVersion); - my %PInfo = ("name"=>"this", "type"=>"$ThisTid"); - - if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}) - { - my @Pos = sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}}); - foreach my $Pos (reverse(0 .. $#Pos)) { - %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos+1}} = %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos}}; - } - } - $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{"0"} = \%PInfo; - } - } - - if(not $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"}) - { # ABI dumps have no mangled names for C-functions - $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"}; - } - if(my $Header = $SymbolInfo{$LibVersion}{$InfoId}{"Header"}) - { # support for old ABI dumps - $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = path_format($Header, $OSgroup); - } - elsif(my $Source = $SymbolInfo{$LibVersion}{$InfoId}{"Source"}) - { # DWARF ABI Dumps - $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = $Source; - } - } - - $Descriptor{$LibVersion}{"Dump"} = 1; } -sub read_Machine_DumpInfo($$) +sub compareABIDumps($$) { - my ($ABI, $LibVersion) = @_; - if($ABI->{"Arch"}) { - $CPU_ARCH{$LibVersion} = $ABI->{"Arch"}; - } - if($ABI->{"WordSize"}) { - $WORD_SIZE{$LibVersion} = $ABI->{"WordSize"}; - } - else - { # support for old dumps - $WORD_SIZE{$LibVersion} = $ABI->{"SizeOfPointer"}; - } - if(not $WORD_SIZE{$LibVersion}) - { # support for old dumps (<1.23) - if(my $Tid = getTypeIdByName("char*", $LibVersion)) - { # size of char* - $WORD_SIZE{$LibVersion} = $TypeInfo{$LibVersion}{$Tid}{"Size"}; - } - else - { - my $PSize = 0; - foreach my $Tid (keys(%{$TypeInfo{$LibVersion}})) - { - if($TypeInfo{$LibVersion}{$Tid}{"Type"} eq "Pointer") - { # any "pointer"-type - $PSize = $TypeInfo{$LibVersion}{$Tid}{"Size"}; - last; - } - } - if($PSize) - { # a pointer type size - $WORD_SIZE{$LibVersion} = $PSize; - } - else { - printMsg("WARNING", "cannot identify a WORD size in the ABI dump (too old format)"); - } - } - } - if($ABI->{"GccVersion"}) { - $GCC_VERSION{$LibVersion} = $ABI->{"GccVersion"}; - } -} - -sub read_Libs_DumpInfo($$) -{ - my ($ABI, $LibVersion) = @_; - $Library_Symbol{$LibVersion} = $ABI->{"Symbols"}; - if(not $Library_Symbol{$LibVersion}) - { # support for old dumps - $Library_Symbol{$LibVersion} = $ABI->{"Interfaces"}; - } - if(keys(%{$Library_Symbol{$LibVersion}}) - and not $DumpAPI) { - $Descriptor{$LibVersion}{"Libs"} = "OK"; - } -} - -sub read_Source_DumpInfo($$) -{ - my ($ABI, $LibVersion) = @_; + my ($V1, $V2) = @_; + my $DumpPath1 = defaultDumpPath($In::Opt{"TargetLib"}, $V1); + my $DumpPath2 = defaultDumpPath($In::Opt{"TargetLib"}, $V2); - if(keys(%{$ABI->{"Headers"}}) - and not $DumpAPI) { - $Descriptor{$LibVersion}{"Headers"} = "OK"; - } - foreach my $Identity (sort {$ABI->{"Headers"}{$a}<=>$ABI->{"Headers"}{$b}} keys(%{$ABI->{"Headers"}})) - { - $Registered_Headers{$LibVersion}{$Identity}{"Identity"} = $Identity; - $Registered_Headers{$LibVersion}{$Identity}{"Pos"} = $ABI->{"Headers"}{$Identity}; - } + unlink($DumpPath1); + unlink($DumpPath2); - if(keys(%{$ABI->{"Sources"}}) - and not $DumpAPI) { - $Descriptor{$LibVersion}{"Sources"} = "OK"; - } - foreach my $Name (sort {$ABI->{"Sources"}{$a}<=>$ABI->{"Sources"}{$b}} keys(%{$ABI->{"Sources"}})) - { - $Registered_Sources{$LibVersion}{$Name}{"Identity"} = $Name; - $Registered_Sources{$LibVersion}{$Name}{"Pos"} = $ABI->{"Headers"}{$Name}; - } -} - -sub find_libs($$$) -{ - my ($Path, $Type, $MaxDepth) = @_; - # FIXME: correct the search pattern - return cmd_find($Path, $Type, '\.'.$LIB_EXT.'[0-9.]*\Z', $MaxDepth, 1); -} - -sub createDescriptor($$) -{ - my ($LibVersion, $Path) = @_; - if(not $LibVersion or not $Path - or not -e $Path) { - return ""; - } - if(-d $Path) - { # directory with headers files and shared objects - return " - - ".$TargetVersion{$LibVersion}." - - - - $Path - - - - $Path - "; - } - else - { # files - if($Path=~/\.(xml|desc)\Z/i) - { # standard XML-descriptor - return readFile($Path); - } - elsif(is_header($Path, 2, $LibVersion)) - { # header file - $CheckHeadersOnly = 1; - - if($LibVersion==1) { - $TargetVersion{$LibVersion} = "X"; - } - - if($LibVersion==2) { - $TargetVersion{$LibVersion} = "Y"; - } - - return " - - ".$TargetVersion{$LibVersion}." - - - - $Path - - - - none - "; - } - else - { # standard XML-descriptor - return readFile($Path); + my $Pid = fork(); + if($Pid) + { # dump on two CPU cores + my @PARAMS = ("-dump", $In::Desc{1}{"Path"}, "-l", $In::Opt{"TargetLib"}); + @PARAMS = (@PARAMS, "-vnum", $V1); + + if($In::Desc{1}{"RelativeDirectory"}) { + @PARAMS = (@PARAMS, "-relpath", $In::Desc{1}{"RelativeDirectory"}); } - } -} - -sub detect_lib_default_paths() -{ - my %LPaths = (); - if($OSgroup eq "bsd") - { - if(my $LdConfig = get_CmdPath("ldconfig")) - { - foreach my $Line (split(/\n/, `$LdConfig -r 2>\"$TMP_DIR/null\"`)) - { - if($Line=~/\A[ \t]*\d+:\-l(.+) \=\> (.+)\Z/) - { - my $Name = "lib".$1; - if(not defined $LPaths{$Name}) { - $LPaths{$Name} = $2; - } - } - } + if($In::Desc{1}{"OutputLogPath"}) { + @PARAMS = (@PARAMS, "-log-path", $In::Desc{1}{"OutputLogPath"}); } - else { - printMsg("WARNING", "can't find ldconfig"); + if($In::Opt{"CrossGcc"}) { + @PARAMS = (@PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"}); } - } - else - { - if(my $LdConfig = get_CmdPath("ldconfig")) + if($In::Opt{"Quiet"}) { - if($SystemRoot and $OSgroup eq "linux") - { # use host (x86) ldconfig with the target (arm) ld.so.conf - if(-e $SystemRoot."/etc/ld.so.conf") { - $LdConfig .= " -f ".$SystemRoot."/etc/ld.so.conf"; - } - } - foreach my $Line (split(/\n/, `$LdConfig -p 2>\"$TMP_DIR/null\"`)) - { - if($Line=~/\A[ \t]*([^ \t]+) .* \=\> (.+)\Z/) - { - my ($Name, $Path) = ($1, $2); - $Path=~s/[\/]{2,}/\//; - if(not defined $LPaths{$Name}) - { # get first element from the list of available paths - - # libstdc++.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 - # libstdc++.so.6 (libc6) => /usr/lib/i386-linux-gnu/libstdc++.so.6 - # libstdc++.so.6 (libc6) => /usr/lib32/libstdc++.so.6 - - $LPaths{$Name} = $Path; - } - } - } + @PARAMS = (@PARAMS, "-quiet"); + @PARAMS = (@PARAMS, "-logging-mode", "a"); } - elsif($OSgroup eq "linux") { - printMsg("WARNING", "can't find ldconfig"); + elsif($In::Opt{"LogMode"} and $In::Opt{"LogMode"} ne "w") + { # "w" is default + @PARAMS = (@PARAMS, "-logging-mode", $In::Opt{"LogMode"}); } - } - return \%LPaths; -} - -sub detect_bin_default_paths() -{ - my $EnvPaths = $ENV{"PATH"}; - if($OSgroup eq "beos") { - $EnvPaths.=":".$ENV{"BETOOLS"}; - } - my $Sep = ($OSgroup eq "windows")?";":":|;"; - foreach my $Path (split(/$Sep/, $EnvPaths)) - { - $Path = path_format($Path, $OSgroup); - next if(not $Path); - if($SystemRoot - and $Path=~/\A\Q$SystemRoot\E\//) - { # do NOT use binaries from target system - next; + if($In::Opt{"ExtendedCheck"}) { + @PARAMS = (@PARAMS, "-extended"); } - push_U(\@DefaultBinPaths, $Path); - } -} - -sub detect_inc_default_paths() -{ - my %DPaths = ("Cpp"=>[],"Gcc"=>[],"Inc"=>[]); - writeFile("$TMP_DIR/empty.h", ""); - foreach my $Line (split(/\n/, `$GCC_PATH -v -x c++ -E \"$TMP_DIR/empty.h\" 2>&1`)) - { # detecting GCC default include paths - next if(index($Line, "/cc1plus ")!=-1); - - if($Line=~/\A[ \t]*((\/|\w+:\\).+)[ \t]*\Z/) - { - my $Path = realpath($1); - $Path = path_format($Path, $OSgroup); - if(index($Path, "c++")!=-1 - or index($Path, "/g++/")!=-1) - { - push_U($DPaths{"Cpp"}, $Path); - if(not defined $MAIN_CPP_DIR - or get_depth($MAIN_CPP_DIR)>get_depth($Path)) { - $MAIN_CPP_DIR = $Path; - } - } - elsif(index($Path, "gcc")!=-1) { - push_U($DPaths{"Gcc"}, $Path); - } - else - { - if($Path=~/local[\/\\]+include/) - { # local paths - next; - } - if($SystemRoot - and $Path!~/\A\Q$SystemRoot\E(\/|\Z)/) - { # The GCC include path for user headers is not a part of the system root - # The reason: you are not specified the --cross-gcc option or selected a wrong compiler - # or it is the internal cross-GCC path like arm-linux-gnueabi/include - next; - } - push_U($DPaths{"Inc"}, $Path); - } + if($In::Opt{"UserLang"}) { + @PARAMS = (@PARAMS, "-lang", $In::Opt{"UserLang"}); } - } - unlink("$TMP_DIR/empty.h"); - return %DPaths; -} - -sub detect_default_paths($) -{ - my ($HSearch, $LSearch, $BSearch, $GSearch) = (1, 1, 1, 1); - my $Search = $_[0]; - if($Search!~/inc/) { - $HSearch = 0; - } - if($Search!~/lib/) { - $LSearch = 0; - } - if($Search!~/bin/) { - $BSearch = 0; - } - if($Search!~/gcc/) { - $GSearch = 0; - } - if(@{$SystemPaths{"include"}}) - { # section of the XML descriptor - # do NOT search for systems headers - $HSearch = 0; - } - if(@{$SystemPaths{"lib"}}) - { # section of the XML descriptor - # do NOT search for systems libraries - $LSearch = 0; - } - foreach my $Type (keys(%{$OS_AddPath{$OSgroup}})) - { # additional search paths - next if($Type eq "include" and not $HSearch); - next if($Type eq "lib" and not $LSearch); - next if($Type eq "bin" and not $BSearch); - push_U($SystemPaths{$Type}, grep { -d $_ } @{$OS_AddPath{$OSgroup}{$Type}}); - } - if($OSgroup ne "windows") - { # unix-like - foreach my $Type ("include", "lib", "bin") - { # automatic detection of system "devel" directories - next if($Type eq "include" and not $HSearch); - next if($Type eq "lib" and not $LSearch); - next if($Type eq "bin" and not $BSearch); - my ($UsrDir, $RootDir) = ("/usr", "/"); - if($SystemRoot and $Type ne "bin") - { # 1. search for target headers and libraries - # 2. use host commands: ldconfig, readelf, etc. - ($UsrDir, $RootDir) = ("$SystemRoot/usr", $SystemRoot); - } - push_U($SystemPaths{$Type}, cmd_find($RootDir,"d","*$Type*",1)); - if(-d $RootDir."/".$Type) - { # if "/lib" is symbolic link - if($RootDir eq "/") { - push_U($SystemPaths{$Type}, "/".$Type); - } - else { - push_U($SystemPaths{$Type}, $RootDir."/".$Type); - } - } - if(-d $UsrDir) - { - push_U($SystemPaths{$Type}, cmd_find($UsrDir,"d","*$Type*",1)); - if(-d $UsrDir."/".$Type) - { # if "/usr/lib" is symbolic link - push_U($SystemPaths{$Type}, $UsrDir."/".$Type); - } - } - } - } - if($BSearch) - { - detect_bin_default_paths(); - push_U($SystemPaths{"bin"}, @DefaultBinPaths); - } - # check environment variables - if($OSgroup eq "beos") - { - foreach (my @Paths = @{$SystemPaths{"bin"}}) - { - if($_ eq ".") { - next; - } - # search for /boot/develop/abi/x86/gcc4/tools/gcc-4.4.4-haiku-101111/bin/ - if(my @Dirs = sort cmd_find($_, "d", "bin")) { - push_U($SystemPaths{"bin"}, sort {get_depth($a)<=>get_depth($b)} @Dirs); - } - } - if($HSearch) - { - push_U(\@DefaultIncPaths, grep { is_abs($_) } ( - split(/:|;/, $ENV{"BEINCLUDES"}) - )); - } - if($LSearch) - { - push_U(\@DefaultLibPaths, grep { is_abs($_) } ( - split(/:|;/, $ENV{"BELIBRARIES"}), - split(/:|;/, $ENV{"LIBRARY_PATH"}) - )); + if($In::Opt{"BinOnly"}) { + @PARAMS = (@PARAMS, "-binary"); } - } - if($LSearch) - { # using linker to get system paths - if(my $LPaths = detect_lib_default_paths()) - { # unix-like - my %Dirs = (); - foreach my $Name (keys(%{$LPaths})) - { - if($SystemRoot - and $LPaths->{$Name}!~/\A\Q$SystemRoot\E\//) - { # wrong ldconfig configuration - # check your /etc/ld.so.conf - next; - } - $DyLib_DefaultPath{$Name} = $LPaths->{$Name}; - if(my $Dir = get_dirname($LPaths->{$Name})) { - $Dirs{$Dir} = 1; - } - } - push_U(\@DefaultLibPaths, sort {get_depth($a)<=>get_depth($b)} sort keys(%Dirs)); + if($In::Opt{"SrcOnly"}) { + @PARAMS = (@PARAMS, "-source"); } - push_U($SystemPaths{"lib"}, @DefaultLibPaths); - } - if($BSearch) - { - if($CrossGcc) - { # --cross-gcc=arm-linux-gcc - if(-e $CrossGcc) - { # absolute or relative path - $GCC_PATH = get_abs_path($CrossGcc); - } - elsif($CrossGcc!~/\// and get_CmdPath($CrossGcc)) - { # command name - $GCC_PATH = $CrossGcc; - } - else { - exitStatus("Access_Error", "can't access \'$CrossGcc\'"); - } - if($GCC_PATH=~/\s/) { - $GCC_PATH = "\"".$GCC_PATH."\""; - } + if($In::Opt{"SortDump"}) { + @PARAMS = (@PARAMS, "-sort"); } - } - if($GSearch) - { # GCC path and default include dirs - if(not $CrossGcc) - { # try default gcc - $GCC_PATH = get_CmdPath("gcc"); + if($In::Opt{"DumpFormat"} and $In::Opt{"DumpFormat"} ne "perl") { + @PARAMS = (@PARAMS, "-dump-format", $In::Opt{"DumpFormat"}); } - if(not $GCC_PATH) - { # try to find gcc-X.Y - foreach my $Path (@{$SystemPaths{"bin"}}) - { - if(my @GCCs = cmd_find($Path, "", '/gcc-[0-9.]*\Z', 1, 1)) - { # select the latest version - @GCCs = sort {$b cmp $a} @GCCs; - if(check_gcc($GCCs[0], "3")) - { - $GCC_PATH = $GCCs[0]; - last; - } - } - } + if($In::Opt{"CheckHeadersOnly"}) { + @PARAMS = (@PARAMS, "-headers-only"); } - if(not $GCC_PATH) { - exitStatus("Not_Found", "can't find GCC>=3.0 in PATH"); + if($In::Opt{"CxxIncompat"}) { + @PARAMS = (@PARAMS, "-cxx-incompatible"); } - - if(my $GCC_Ver = get_dumpversion($GCC_PATH)) + if($In::Opt{"Debug"}) { - my $GccTarget = get_dumpmachine($GCC_PATH); - - if($GccTarget=~/linux/) - { - $OStarget = "linux"; - $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget}; - } - elsif($GccTarget=~/symbian/) - { - $OStarget = "symbian"; - $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget}; - } - - printMsg("INFO", "Using GCC $GCC_Ver ($GccTarget, target: ".getArch_GCC(1).")"); - - # check GCC version - if($GCC_Ver=~/\A4\.8(|\.[012])\Z/) - { # bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57850 - # introduced in 4.8 - # fixed in 4.8.3 - printMsg("WARNING", "Not working properly with GCC $GCC_Ver. Please update or downgrade GCC or use a local installation by --gcc-path=PATH option."); - $EMERGENCY_MODE_48 = 1; - } + @PARAMS = (@PARAMS, "-debug"); + printMsg("INFO", "Executing perl $0 @PARAMS"); } - else { - exitStatus("Error", "something is going wrong with the GCC compiler"); + system("perl", $0, @PARAMS); + if(not -f $DumpPath1) { + exit(1); } } - if($HSearch) - { - # GCC standard paths - if($GCC_PATH and not $NoStdInc) - { - my %DPaths = detect_inc_default_paths(); - @DefaultCppPaths = @{$DPaths{"Cpp"}}; - @DefaultGccPaths = @{$DPaths{"Gcc"}}; - @DefaultIncPaths = @{$DPaths{"Inc"}}; - push_U($SystemPaths{"include"}, @DefaultIncPaths); - } + else + { # child + my @PARAMS = ("-dump", $In::Desc{2}{"Path"}, "-l", $In::Opt{"TargetLib"}); + @PARAMS = (@PARAMS, "-vnum", $V2); - # users include paths - my $IncPath = "/usr/include"; - if($SystemRoot) { - $IncPath = $SystemRoot.$IncPath; - } - if(-d $IncPath) { - push_U(\@UsersIncPath, $IncPath); + if($In::Desc{2}{"RelativeDirectory"}) { + @PARAMS = (@PARAMS, "-relpath", $In::Desc{2}{"RelativeDirectory"}); } - } - - if($ExtraInfo) - { - writeFile($ExtraInfo."/default-libs", join("\n", @DefaultLibPaths)); - writeFile($ExtraInfo."/default-includes", join("\n", (@DefaultCppPaths, @DefaultGccPaths, @DefaultIncPaths))); - } -} - -sub getLIB_EXT($) -{ - my $Target = $_[0]; - if(my $Ext = $OS_LibExt{$LIB_TYPE}{$Target}) { - return $Ext; - } - return $OS_LibExt{$LIB_TYPE}{"default"}; -} - -sub getAR_EXT($) -{ - my $Target = $_[0]; - if(my $Ext = $OS_Archive{$Target}) { - return $Ext; - } - return $OS_Archive{"default"}; -} - -sub get_dumpversion($) -{ - my $Cmd = $_[0]; - return "" if(not $Cmd); - if($Cache{"get_dumpversion"}{$Cmd}) { - return $Cache{"get_dumpversion"}{$Cmd}; - } - my $V = `$Cmd -dumpversion 2>\"$TMP_DIR/null\"`; - chomp($V); - return ($Cache{"get_dumpversion"}{$Cmd} = $V); -} - -sub get_dumpmachine($) -{ - my $Cmd = $_[0]; - return "" if(not $Cmd); - if($Cache{"get_dumpmachine"}{$Cmd}) { - return $Cache{"get_dumpmachine"}{$Cmd}; - } - my $Machine = `$Cmd -dumpmachine 2>\"$TMP_DIR/null\"`; - chomp($Machine); - return ($Cache{"get_dumpmachine"}{$Cmd} = $Machine); -} - -sub checkCmd($) -{ - my $Cmd = $_[0]; - return "" if(not $Cmd); - my @Options = ( - "--version", - "-help" - ); - foreach my $Opt (@Options) - { - my $Info = `$Cmd $Opt 2>\"$TMP_DIR/null\"`; - if($Info) { - return 1; + if($In::Desc{2}{"OutputLogPath"}) { + @PARAMS = (@PARAMS, "-log-path", $In::Desc{2}{"OutputLogPath"}); } - } - return 0; -} - -sub check_gcc($$) -{ - my ($Cmd, $ReqVer) = @_; - return 0 if(not $Cmd or not $ReqVer); - if(defined $Cache{"check_gcc"}{$Cmd}{$ReqVer}) { - return $Cache{"check_gcc"}{$Cmd}{$ReqVer}; - } - if(my $GccVer = get_dumpversion($Cmd)) - { - $GccVer=~s/(-|_)[a-z_]+.*\Z//; # remove suffix (like "-haiku-100818") - if(cmpVersions($GccVer, $ReqVer)>=0) { - return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = $Cmd); + if($In::Opt{"CrossGcc"}) { + @PARAMS = (@PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"}); } - } - return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = ""); -} - -sub get_depth($) -{ - if(defined $Cache{"get_depth"}{$_[0]}) { - return $Cache{"get_depth"}{$_[0]}; - } - return ($Cache{"get_depth"}{$_[0]} = ($_[0]=~tr![\/\\]|\:\:!!)); -} - -sub registerGccHeaders() -{ - return if($Cache{"registerGccHeaders"}); # this function should be called once - - foreach my $Path (@DefaultGccPaths) - { - my @Headers = cmd_find($Path,"f"); - @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers; - foreach my $HPath (@Headers) + if($In::Opt{"Quiet"}) { - my $FileName = get_filename($HPath); - if(not defined $DefaultGccHeader{$FileName}) - { # skip duplicated - $DefaultGccHeader{$FileName} = $HPath; - } + @PARAMS = (@PARAMS, "-quiet"); + @PARAMS = (@PARAMS, "-logging-mode", "a"); } - } - $Cache{"registerGccHeaders"} = 1; -} - -sub registerCppHeaders() -{ - return if($Cache{"registerCppHeaders"}); # this function should be called once - - foreach my $CppDir (@DefaultCppPaths) - { - my @Headers = cmd_find($CppDir,"f"); - @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers; - foreach my $Path (@Headers) - { - my $FileName = get_filename($Path); - if(not defined $DefaultCppHeader{$FileName}) - { # skip duplicated - $DefaultCppHeader{$FileName} = $Path; - } + elsif($In::Opt{"LogMode"} and $In::Opt{"LogMode"} ne "w") + { # "w" is default + @PARAMS = (@PARAMS, "-logging-mode", $In::Opt{"LogMode"}); } - } - $Cache{"registerCppHeaders"} = 1; -} - -sub parse_libname($$$) -{ - return "" if(not $_[0]); - if(defined $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]}) { - return $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]}; - } - return ($Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]} = parse_libname_I(@_)); -} - -sub parse_libname_I($$$) -{ - my ($Name, $Type, $Target) = @_; - - if($Target eq "symbian") { - return parse_libname_symbian($Name, $Type); - } - elsif($Target eq "windows") { - return parse_libname_windows($Name, $Type); - } - - # unix - my $Ext = getLIB_EXT($Target); - if($Name=~/((((lib|).+?)([\-\_][\d\-\.\_]+.*?|))\.$Ext)(\.(.+)|)\Z/) - { # libSDL-1.2.so.0.7.1 - # libwbxml2.so.0.0.18 - # libopcodes-2.21.53-system.20110810.so - if($Type eq "name") - { # libSDL-1.2 - # libwbxml2 - return $2; + if($In::Opt{"ExtendedCheck"}) { + @PARAMS = (@PARAMS, "-extended"); } - elsif($Type eq "name+ext") - { # libSDL-1.2.so - # libwbxml2.so - return $1; + if($In::Opt{"UserLang"}) { + @PARAMS = (@PARAMS, "-lang", $In::Opt{"UserLang"}); } - elsif($Type eq "version") - { - if(defined $7 - and $7 ne "") - { # 0.7.1 - return $7; - } - else - { # libc-2.5.so (=>2.5 version) - my $MV = $5; - $MV=~s/\A[\-\_]+//g; - return $MV; - } - } - elsif($Type eq "short") - { # libSDL - # libwbxml2 - return $3; - } - elsif($Type eq "shortest") - { # SDL - # wbxml - return shortest_name($3); - } - } - return "";# error -} - -sub parse_libname_symbian($$) -{ - my ($Name, $Type) = @_; - my $Ext = getLIB_EXT("symbian"); - if($Name=~/(((.+?)(\{.+\}|))\.$Ext)\Z/) - { # libpthread{00010001}.dso - if($Type eq "name") - { # libpthread{00010001} - return $2; - } - elsif($Type eq "name+ext") - { # libpthread{00010001}.dso - return $1; - } - elsif($Type eq "version") - { # 00010001 - my $V = $4; - $V=~s/\{(.+)\}/$1/; - return $V; - } - elsif($Type eq "short") - { # libpthread - return $3; + if($In::Opt{"BinOnly"}) { + @PARAMS = (@PARAMS, "-binary"); } - elsif($Type eq "shortest") - { # pthread - return shortest_name($3); + if($In::Opt{"SrcOnly"}) { + @PARAMS = (@PARAMS, "-source"); } - } - return "";# error -} - -sub parse_libname_windows($$) -{ - my ($Name, $Type) = @_; - my $Ext = getLIB_EXT("windows"); - if($Name=~/((.+?)\.$Ext)\Z/) - { # netapi32.dll - if($Type eq "name") - { # netapi32 - return $2; - } - elsif($Type eq "name+ext") - { # netapi32.dll - return $1; - } - elsif($Type eq "version") - { # DLL version embedded - # at binary-level - return ""; - } - elsif($Type eq "short") - { # netapi32 - return $2; - } - elsif($Type eq "shortest") - { # netapi - return shortest_name($2); - } - } - return "";# error -} - -sub shortest_name($) -{ - my $Name = $_[0]; - # remove prefix - $Name=~s/\A(lib|open)//; - # remove suffix - $Name=~s/[\W\d_]+\Z//i; - $Name=~s/([a-z]{2,})(lib)\Z/$1/i; - return $Name; -} - -sub createSymbolsList($$$$$) -{ - my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_; - - read_ABI_Dump(1, $DPath); - prepareSymbols(1); - - my %SymbolHeaderLib = (); - my $Total = 0; - - # Get List - foreach my $Symbol (sort keys(%{$CompleteSignature{1}})) - { - if(not link_symbol($Symbol, 1, "-Deps")) - { # skip src only and all external functions - next; + if($In::Opt{"SortDump"}) { + @PARAMS = (@PARAMS, "-sort"); } - if(not symbolFilter($Symbol, 1, "Public", "Binary")) - { # skip other symbols - next; + if($In::Opt{"DumpFormat"} and $In::Opt{"DumpFormat"} ne "perl") { + @PARAMS = (@PARAMS, "-dump-format", $In::Opt{"DumpFormat"}); } - my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"}; - if(not $HeaderName) - { # skip src only and all external functions - next; + if($In::Opt{"CheckHeadersOnly"}) { + @PARAMS = (@PARAMS, "-headers-only"); } - my $DyLib = $Symbol_Library{1}{$Symbol}; - if(not $DyLib) - { # skip src only and all external functions - next; + if($In::Opt{"CxxIncompat"}) { + @PARAMS = (@PARAMS, "-cxx-incompatible"); } - $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1; - $Total+=1; - } - # Draw List - my $SYMBOLS_LIST = "

Public symbols in $LName ($LVersion)"; - $SYMBOLS_LIST .= " on ".showArch($ArchName)."
Total: $Total


"; - foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib)) - { - foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}})) + if($In::Opt{"Debug"}) { - my %NS_Symbol = (); - foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) { - $NS_Symbol{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1; - } - foreach my $NameSpace (sort keys(%NS_Symbol)) - { - $SYMBOLS_LIST .= getTitle($HeaderName, $DyLib, $NameSpace); - my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NS_Symbol{$NameSpace}}); - foreach my $Symbol (@SortedInterfaces) - { - my $SubReport = ""; - my $Signature = get_Signature($Symbol, 1); - if($NameSpace) { - $Signature=~s/\b\Q$NameSpace\E::\b//g; - } - if($Symbol=~/\A(_Z|\?)/) - { - if($Signature) { - $SubReport = insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."
\n".$ContentDivStart."[symbol: $Symbol]

".$ContentDivEnd."\n"); - } - else { - $SubReport = "".$Symbol."
\n"; - } - } - else - { - if($Signature) { - $SubReport = "".highLight_Signature_Italic_Color($Signature)."
\n"; - } - else { - $SubReport = "".$Symbol."
\n"; - } - } - $SYMBOLS_LIST .= $SubReport; - } - } - $SYMBOLS_LIST .= "
\n"; - } - } - # clear info - (%TypeInfo, %SymbolInfo, %Library_Symbol, %DepSymbol_Library, - %DepLibrary_Symbol, %SymVer, %SkipTypes, %SkipSymbols, - %NestedNameSpaces, %ClassMethods, %AllocableClass, %ClassNames, - %CompleteSignature, %SkipNameSpaces, %Symbol_Library, %Library_Symbol) = (); - ($Content_Counter, $ContentID) = (0, 0); - # print report - my $CssStyles = readModule("Styles", "SymbolsList.css"); - my $JScripts = readModule("Scripts", "Sections.js"); - $SYMBOLS_LIST = "".$SYMBOLS_LIST.$TOP_REF."
\n"; - my $Title = "$LName: public symbols"; - my $Keywords = "$LName, API, symbols"; - my $Description = "List of symbols in $LName ($LVersion) on ".showArch($ArchName); - $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)." -
\n$SYMBOLS_LIST
-

\n".getReportFooter()." - "; - writeFile($SaveTo, $SYMBOLS_LIST); -} - -sub add_target_libs($) -{ - foreach (@{$_[0]}) { - $TargetLibs{$_} = 1; - } -} - -sub is_target_lib($) -{ - my $LName = $_[0]; - if(not $LName) { - return 0; - } - if($TargetLibraryName - and $LName!~/\Q$TargetLibraryName\E/) { - return 0; - } - if(keys(%TargetLibs) - and not $TargetLibs{$LName} - and not $TargetLibs{parse_libname($LName, "name+ext", $OStarget)}) { - return 0; - } - return 1; -} - -sub is_target_header($$) -{ # --header, --headers-list - my ($H, $V) = @_; - if(keys(%{$TargetHeaders{$V}})) - { - if($TargetHeaders{$V}{$H}) { - return 1; + @PARAMS = (@PARAMS, "-debug"); + printMsg("INFO", "Executing perl $0 @PARAMS"); } - } - return 0; -} - -sub readLibs($) -{ - my $LibVersion = $_[0]; - if($OStarget eq "windows") - { # dumpbin.exe will crash - # without VS Environment - check_win32_env(); - } - readSymbols($LibVersion); - translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion); - translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion); -} - -sub dump_sorting($) -{ - my $Hash = $_[0]; - return [] if(not $Hash); - my @Keys = keys(%{$Hash}); - return [] if($#Keys<0); - if($Keys[0]=~/\A\d+\Z/) - { # numbers - return [sort {int($a)<=>int($b)} @Keys]; - } - else - { # strings - return [sort {$a cmp $b} @Keys]; - } -} - -sub printMsg($$) -{ - my ($Type, $Msg) = @_; - if($Type!~/\AINFO/) { - $Msg = $Type.": ".$Msg; - } - if($Type!~/_C\Z/) { - $Msg .= "\n"; - } - if($Quiet) - { # --quiet option - appendFile($COMMON_LOG_PATH, $Msg); - } - else - { - if($Type eq "ERROR") { - print STDERR $Msg; + system("perl", $0, @PARAMS); + if(not -f $DumpPath2) { + exit(1); } else { - print $Msg; + exit(0); } } -} - -sub exitStatus($$) -{ - my ($Code, $Msg) = @_; - printMsg("ERROR", $Msg); - exit($ERROR_CODE{$Code}); -} - -sub exitReport() -{ # the tool has run without any errors - printReport(); - if($COMPILE_ERRORS) - { # errors in headers may add false positives/negatives - exit($ERROR_CODE{"Compile_Error"}); + waitpid($Pid, 0); + + my @CMP_PARAMS = ("-l", $In::Opt{"TargetLib"}); + @CMP_PARAMS = (@CMP_PARAMS, "-d1", $DumpPath1); + @CMP_PARAMS = (@CMP_PARAMS, "-d2", $DumpPath2); + if($In::Opt{"TargetTitle"} ne $In::Opt{"TargetLib"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-title", $In::Opt{"TargetTitle"}); } - if($BinaryOnly and $RESULT{"Binary"}{"Problems"}) - { # --binary - exit($ERROR_CODE{"Incompatible"}); + if($In::Opt{"ShowRetVal"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-show-retval"); } - elsif($SourceOnly and $RESULT{"Source"}{"Problems"}) - { # --source - exit($ERROR_CODE{"Incompatible"}); + if($In::Opt{"CrossGcc"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $In::Opt{"CrossGcc"}); } - elsif($RESULT{"Source"}{"Problems"} - or $RESULT{"Binary"}{"Problems"}) - { # default - exit($ERROR_CODE{"Incompatible"}); + @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a"); + if($In::Opt{"Quiet"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-quiet"); } - else { - exit($ERROR_CODE{"Compatible"}); + if($In::Opt{"ReportFormat"} + and $In::Opt{"ReportFormat"} ne "html") + { # HTML is default format + @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $In::Opt{"ReportFormat"}); } -} - -sub readRules($) -{ - my $Kind = $_[0]; - if(not -f $RULES_PATH{$Kind}) { - exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'"); + if($In::Opt{"OutputReportPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-report-path", $In::Opt{"OutputReportPath"}); } - my $Content = readFile($RULES_PATH{$Kind}); - while(my $Rule = parseTag(\$Content, "rule")) - { - my $RId = parseTag(\$Rule, "id"); - my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind"); - foreach my $Prop (@Properties) { - if(my $Value = parseTag(\$Rule, lc($Prop))) - { - $Value=~s/\n[ ]*//; - $CompatRules{$Kind}{$RId}{$Prop} = $Value; - } - } - if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) { - $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols"; - } - else { - $CompatRules{$Kind}{$RId}{"Kind"} = "Types"; - } + if($In::Opt{"BinReportPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-bin-report-path", $In::Opt{"BinReportPath"}); } -} - -sub getReportPath($) -{ - my $Level = $_[0]; - my $Dir = "compat_reports/$TargetLibraryName/".$Descriptor{1}{"Version"}."_to_".$Descriptor{2}{"Version"}; - if($Level eq "Binary") - { - if($BinaryReportPath) - { # --bin-report-path - return $BinaryReportPath; - } - elsif($OutputReportPath) - { # --report-path - return $OutputReportPath; - } - else - { # default - return $Dir."/abi_compat_report.$ReportFormat"; - } + if($In::Opt{"SrcReportPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-src-report-path", $In::Opt{"SrcReportPath"}); } - elsif($Level eq "Source") - { - if($SourceReportPath) - { # --src-report-path - return $SourceReportPath; - } - elsif($OutputReportPath) - { # --report-path - return $OutputReportPath; - } - else - { # default - return $Dir."/src_compat_report.$ReportFormat"; - } + if($In::Opt{"LoggingPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-log-path", $In::Opt{"LoggingPath"}); } - else - { - if($OutputReportPath) - { # --report-path - return $OutputReportPath; - } - else - { # default - return $Dir."/compat_report.$ReportFormat"; - } + if($In::Opt{"CheckHeadersOnly"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-headers-only"); } -} - -sub printStatMsg($) -{ - my $Level = $_[0]; - printMsg("INFO", "total \"$Level\" compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"}); -} - -sub listAffected($) -{ - my $Level = $_[0]; - my $List = ""; - foreach (keys(%{$TotalAffected{$Level}})) - { - if($StrictCompat and $TotalAffected{$Level}{$_} eq "Low") - { # skip "Low"-severity problems - next; - } - $List .= "$_\n"; - } - my $Dir = get_dirname(getReportPath($Level)); - if($Level eq "Binary") { - writeFile($Dir."/abi_affected.txt", $List); - } - elsif($Level eq "Source") { - writeFile($Dir."/src_affected.txt", $List); + if($In::Opt{"BinOnly"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-binary"); } -} - -sub printReport() -{ - printMsg("INFO", "creating compatibility report ..."); - createReport(); - if($JoinReport or $DoubleReport) - { - if($RESULT{"Binary"}{"Problems"} - or $RESULT{"Source"}{"Problems"}) { - printMsg("INFO", "result: INCOMPATIBLE (Binary: ".$RESULT{"Binary"}{"Affected"}."\%, Source: ".$RESULT{"Source"}{"Affected"}."\%)"); - } - else { - printMsg("INFO", "result: COMPATIBLE"); - } - printStatMsg("Binary"); - printStatMsg("Source"); - if($ListAffected) - { # --list-affected - listAffected("Binary"); - listAffected("Source"); - } + if($In::Opt{"SrcOnly"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-source"); } - elsif($BinaryOnly) - { - if($RESULT{"Binary"}{"Problems"}) { - printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Binary"}{"Affected"}."\%)"); - } - else { - printMsg("INFO", "result: COMPATIBLE"); - } - printStatMsg("Binary"); - if($ListAffected) - { # --list-affected - listAffected("Binary"); - } + if($In::Opt{"FilterPath"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-filter", $In::Opt{"FilterPath"}); } - elsif($SourceOnly) - { - if($RESULT{"Source"}{"Problems"}) { - printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Source"}{"Affected"}."\%)"); - } - else { - printMsg("INFO", "result: COMPATIBLE"); - } - printStatMsg("Source"); - if($ListAffected) - { # --list-affected - listAffected("Source"); - } + if($In::Opt{"SkipInternalSymbols"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-skip-internal-symbols", $In::Opt{"SkipInternalSymbols"}); } - if($StdOut) - { - if($JoinReport or not $DoubleReport) - { # --binary or --source - printMsg("INFO", "compatibility report has been generated to stdout"); - } - else - { # default - printMsg("INFO", "compatibility reports have been generated to stdout"); - } + if($In::Opt{"SkipInternalTypes"}) { + @CMP_PARAMS = (@CMP_PARAMS, "-skip-internal-types", $In::Opt{"SkipInternalTypes"}); } - else + if($In::Opt{"Debug"}) { - if($JoinReport) - { - printMsg("INFO", "see detailed report:\n ".getReportPath("Join")); - } - elsif($DoubleReport) - { # default - printMsg("INFO", "see detailed reports:\n ".getReportPath("Binary")."\n ".getReportPath("Source")); - } - elsif($BinaryOnly) - { # --binary - printMsg("INFO", "see detailed report:\n ".getReportPath("Binary")); - } - elsif($SourceOnly) - { # --source - printMsg("INFO", "see detailed report:\n ".getReportPath("Source")); - } - } -} - -sub check_win32_env() -{ - if(not $ENV{"DevEnvDir"} - or not $ENV{"LIB"}) { - exitStatus("Error", "can't start without VS environment (vsvars32.bat)"); + @CMP_PARAMS = (@CMP_PARAMS, "-debug"); + printMsg("INFO", "Executing perl $0 @CMP_PARAMS"); } + system("perl", $0, @CMP_PARAMS); + exit($?>>8); } -sub diffSets($$) +sub compareInit() { - my ($S1, $S2) = @_; - my @SK1 = keys(%{$S1}); - my @SK2 = keys(%{$S2}); - if($#SK1!=$#SK2) { - return 1; - } - foreach my $K1 (@SK1) - { - if(not defined $S2->{$K1}) { - return 1; - } + # read input XML descriptors or ABI dumps + if(not $In::Desc{1}{"Path"}) { + exitStatus("Error", "-old option is not specified"); } - return 0; -} - -sub defaultDumpPath($$) -{ - my ($N, $V) = @_; - return "abi_dumps/".$N."/".$N."_".$V.".abi.".$AR_EXT; # gzipped by default -} - -sub create_ABI_Dump() -{ - if(not -e $DumpAPI) { - exitStatus("Access_Error", "can't access \'$DumpAPI\'"); + if(not -e $In::Desc{1}{"Path"}) { + exitStatus("Access_Error", "can't access \'".$In::Desc{1}{"Path"}."\'"); } - if(isDump($DumpAPI)) { - read_ABI_Dump(1, $DumpAPI); - } - else { - readDescriptor(1, createDescriptor(1, $DumpAPI)); + if(not $In::Desc{2}{"Path"}) { + exitStatus("Error", "-new option is not specified"); } - - if(not $Descriptor{1}{"Version"}) - { # set to default: N - $Descriptor{1}{"Version"} = "N"; + if(not -e $In::Desc{2}{"Path"}) { + exitStatus("Access_Error", "can't access \'".$In::Desc{2}{"Path"}."\'"); } - initLogging(1); - detect_default_paths("inc|lib|bin|gcc"); # complete analysis + detectDefaultPaths(undef, undef, "bin", undef); # to extract dumps - my $DumpPath = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"}); - if($OutputDumpPath) - { # user defined path - $DumpPath = $OutputDumpPath; - } - my $Archive = ($DumpPath=~s/\Q.$AR_EXT\E\Z//g); + printMsg("INFO", "Preparing, please wait ..."); - if(not $Archive and not $StdOut) - { # check archive utilities - if($OSgroup eq "windows") - { # using zip - my $ZipCmd = get_CmdPath("zip"); - if(not $ZipCmd) { - exitStatus("Not_Found", "can't find \"zip\""); - } - } - else - { # using tar and gzip - my $TarCmd = get_CmdPath("tar"); - if(not $TarCmd) { - exitStatus("Not_Found", "can't find \"tar\""); - } - my $GzipCmd = get_CmdPath("gzip"); - if(not $GzipCmd) { - exitStatus("Not_Found", "can't find \"gzip\""); - } + if($In::Opt{"UseDumps"}) + { # --use-dumps + # parallel processing + if(isDump($In::Desc{1}{"Path"}) + or isDump($In::Desc{2}{"Path"})) { + exitStatus("Error", "please specify input XML descriptors instead of ABI dumps to use with -use-dumps option."); } + + readDesc(createDesc($In::Desc{1}{"Path"}, 1), 1); + readDesc(createDesc($In::Desc{2}{"Path"}, 2), 2); + + compareABIDumps($In::Desc{1}{"Version"}, $In::Desc{2}{"Version"}); } - if(not $Descriptor{1}{"Dump"}) + if(isDump($In::Desc{1}{"Path"})) { - if(not $CheckHeadersOnly) { - readLibs(1); - } - if($CheckHeadersOnly) { - setLanguage(1, "C++"); - } - searchForHeaders(1); - $WORD_SIZE{1} = detectWordSize(1); - } - if(not $Descriptor{1}{"Dump"}) - { - if($Descriptor{1}{"Headers"}) { - readHeaders(1); - } - } - cleanDump(1); - if(not keys(%{$SymbolInfo{1}})) - { # check if created dump is valid - if(not $ExtendedCheck) - { - if($CheckHeadersOnly) { - exitStatus("Empty_Set", "the set of public symbols is empty"); - } - else { - exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection"); - } + $In::ABI{1} = readABIDump(1, $In::Desc{1}{"Path"}); + initAliases(1); + + if(my $V = $In::Desc{1}{"TargetVersion"}) { + $In::Desc{1}{"Version"} = $V; } - } - my %HeadersInfo = (); - foreach my $HPath (keys(%{$Registered_Headers{1}})) { - $HeadersInfo{$Registered_Headers{1}{$HPath}{"Identity"}} = $Registered_Headers{1}{$HPath}{"Pos"}; - } - if($ExtraDump) - { # add unmangled names to the ABI dump - my @Names = (); - foreach my $InfoId (keys(%{$SymbolInfo{1}})) - { - if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"}) { - push(@Names, $MnglName); - } + else { + $In::Desc{1}{"Version"} = $In::ABI{1}{"LibraryVersion"}; } - translateSymbols(@Names, 1); - foreach my $InfoId (keys(%{$SymbolInfo{1}})) - { - if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"}) - { - if(my $Unmangled = $tr_name{$MnglName}) - { - if($MnglName ne $Unmangled) { - $SymbolInfo{1}{$InfoId}{"Unmangled"} = $Unmangled; - } - } - } + + if(not defined $In::Desc{1}{"Version"}) { + $In::Desc{1}{"Version"} = "X"; } } - - my %GccConstants = (); # built-in GCC constants - foreach my $Name (keys(%{$Constants{1}})) + else { - if(not defined $Constants{1}{$Name}{"Header"}) - { - $GccConstants{$Name} = $Constants{1}{$Name}{"Value"}; - delete($Constants{1}{$Name}); - } - } - - printMsg("INFO", "creating library ABI dump ..."); - my %ABI = ( - "TypeInfo" => $TypeInfo{1}, - "SymbolInfo" => $SymbolInfo{1}, - "Symbols" => $Library_Symbol{1}, - "DepSymbols" => $DepLibrary_Symbol{1}, - "SymbolVersion" => $SymVer{1}, - "LibraryVersion" => $Descriptor{1}{"Version"}, - "LibraryName" => $TargetLibraryName, - "Language" => $COMMON_LANGUAGE{1}, - "SkipTypes" => $SkipTypes{1}, - "SkipSymbols" => $SkipSymbols{1}, - "SkipNameSpaces" => $SkipNameSpaces{1}, - "SkipHeaders" => $SkipHeadersList{1}, - "Headers" => \%HeadersInfo, - "Constants" => $Constants{1}, - "GccConstants" => \%GccConstants, - "NameSpaces" => $NestedNameSpaces{1}, - "Target" => $OStarget, - "Arch" => getArch(1), - "WordSize" => $WORD_SIZE{1}, - "GccVersion" => get_dumpversion($GCC_PATH), - "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION, - "ABI_COMPLIANCE_CHECKER_VERSION" => $TOOL_VERSION - ); - if(diffSets($TargetHeaders{1}, \%HeadersInfo)) { - $ABI{"TargetHeaders"} = $TargetHeaders{1}; - } - if($UseXML) { - $ABI{"XML_ABI_DUMP_VERSION"} = $XML_ABI_DUMP_VERSION; - } - if($ExtendedCheck) - { # --ext option - $ABI{"Mode"} = "Extended"; - } - if($BinaryOnly) - { # --binary - $ABI{"BinOnly"} = 1; - } - if($ExtraDump) - { # --extra-dump - $ABI{"Extra"} = 1; - $ABI{"UndefinedSymbols"} = $UndefinedSymbols{1}; - $ABI{"Needed"} = $Library_Needed{1}; + loadModule("ABIDump"); + readDesc(createDesc($In::Desc{1}{"Path"}, 1), 1); + + initLogging(1); + detectDefaultPaths("inc", "lib", undef, "gcc"); + createABIDump(1); } - my $ABI_DUMP = ""; - if($UseXML) + if(isDump($In::Desc{2}{"Path"})) { - loadModule("XmlDump"); - $ABI_DUMP = createXmlDump(\%ABI); - } - else - { # default - $ABI_DUMP = Dumper(\%ABI); - } - if($StdOut) - { # --stdout option - print STDOUT $ABI_DUMP; - printMsg("INFO", "ABI dump has been generated to stdout"); - return; - } - else - { # write to gzipped file - my ($DDir, $DName) = separate_path($DumpPath); - my $DPath = $TMP_DIR."/".$DName; - if(not $Archive) { - $DPath = $DumpPath; - } - - mkpath($DDir); - - open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n"); - print DUMP $ABI_DUMP; - close(DUMP); - - if(not -s $DPath) { - exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module"); - } - if($Archive) { - $DumpPath = createArchive($DPath, $DDir); - } + $In::ABI{2} = readABIDump(2, $In::Desc{2}{"Path"}); + initAliases(2); - if($OutputDumpPath) { - printMsg("INFO", "library ABI has been dumped to:\n $OutputDumpPath"); + if(my $V = $In::Desc{2}{"TargetVersion"}) { + $In::Desc{2}{"Version"} = $V; } else { - printMsg("INFO", "library ABI has been dumped to:\n $DumpPath"); - } - printMsg("INFO", "you can transfer this dump everywhere and use instead of the ".$Descriptor{1}{"Version"}." version descriptor"); - } -} - -sub quickEmptyReports() -{ # Quick "empty" reports - # 4 times faster than merging equal dumps - # NOTE: the dump contains the "LibraryVersion" attribute - # if you change the version, then your dump will be different - # OVERCOME: use -v1 and v2 options for comparing dumps - # and don't change version in the XML descriptor (and dumps) - # OVERCOME 2: separate meta info from the dumps in ACC 2.0 - if(-s $Descriptor{1}{"Path"} == -s $Descriptor{2}{"Path"}) - { - my $FilePath1 = unpackDump($Descriptor{1}{"Path"}); - my $FilePath2 = unpackDump($Descriptor{2}{"Path"}); - if($FilePath1 and $FilePath2) - { - my $Line = readLineNum($FilePath1, 0); - if($Line=~/xml/) - { # XML format - # is not supported yet - return; - } - - local $/ = undef; - - open(DUMP1, $FilePath1); - my $Content1 = ; - close(DUMP1); - - open(DUMP2, $FilePath2); - my $Content2 = ; - close(DUMP2); - - if($Content1 eq $Content2) - { - # clean memory - undef $Content2; - - # read a number of headers, libs, symbols and types - my $ABIdump = eval($Content1); - - # clean memory - undef $Content1; - - if(not $ABIdump) { - exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again"); - } - if(not $ABIdump->{"TypeInfo"}) - { # support for old dumps - $ABIdump->{"TypeInfo"} = $ABIdump->{"TypeDescr"}; - } - if(not $ABIdump->{"SymbolInfo"}) - { # support for old dumps - $ABIdump->{"SymbolInfo"} = $ABIdump->{"FuncDescr"}; - } - read_Source_DumpInfo($ABIdump, 1); - read_Libs_DumpInfo($ABIdump, 1); - read_Machine_DumpInfo($ABIdump, 1); - read_Machine_DumpInfo($ABIdump, 2); - - %{$CheckedTypes{"Binary"}} = %{$ABIdump->{"TypeInfo"}}; - %{$CheckedTypes{"Source"}} = %{$ABIdump->{"TypeInfo"}}; - - %{$CheckedSymbols{"Binary"}} = %{$ABIdump->{"SymbolInfo"}}; - %{$CheckedSymbols{"Source"}} = %{$ABIdump->{"SymbolInfo"}}; - - $Descriptor{1}{"Version"} = $TargetVersion{1}?$TargetVersion{1}:$ABIdump->{"LibraryVersion"}; - $Descriptor{2}{"Version"} = $TargetVersion{2}?$TargetVersion{2}:$ABIdump->{"LibraryVersion"}; - exitReport(); - } + $In::Desc{2}{"Version"} = $In::ABI{2}{"LibraryVersion"}; } - } -} - -sub initLogging($) -{ - my $LibVersion = $_[0]; - # create log directory - my ($LOG_DIR, $LOG_FILE) = ("logs/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"}, "log.txt"); - if($OutputLogPath{$LibVersion}) - { # user-defined by -log-path option - ($LOG_DIR, $LOG_FILE) = separate_path($OutputLogPath{$LibVersion}); - } - if($LogMode ne "n") { - mkpath($LOG_DIR); - } - $LOG_PATH{$LibVersion} = get_abs_path($LOG_DIR)."/".$LOG_FILE; - if($Debug) - { # debug directory - $DEBUG_PATH{$LibVersion} = "debug/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"}; - if(not $ExtraInfo) - { # enable --extra-info - $ExtraInfo = $DEBUG_PATH{$LibVersion}."/extra-info"; - } - } - resetLogging($LibVersion); -} - -sub writeLog($$) -{ - my ($LibVersion, $Msg) = @_; - if($LogMode ne "n") { - appendFile($LOG_PATH{$LibVersion}, $Msg); - } -} - -sub resetLogging($) -{ - my $LibVersion = $_[0]; - if($LogMode!~/a|n/) - { # remove old log - unlink($LOG_PATH{$LibVersion}); - if($Debug) { - rmtree($DEBUG_PATH{$LibVersion}); + if(not defined $In::Desc{2}{"Version"}) { + $In::Desc{2}{"Version"} = "Y"; } } -} - -sub printErrorLog($) -{ - my $LibVersion = $_[0]; - if($LogMode ne "n") { - printMsg("ERROR", "see log for details:\n ".$LOG_PATH{$LibVersion}."\n"); - } -} - -sub isDump($) -{ - if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump)(\.tar\.gz(\.\w+|)|\.zip|\.xml|)\Z/) - { # NOTE: name.abi.tar.gz.amd64 (dh & cdbs) - return $1; - } - return 0; -} - -sub isDump_U($) -{ - if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump)(\.xml|)\Z/) { - return $1; - } - return 0; -} - -sub compareInit() -{ - # read input XML descriptors or ABI dumps - if(not $Descriptor{1}{"Path"}) { - exitStatus("Error", "-old option is not specified"); - } - if(not -e $Descriptor{1}{"Path"}) { - exitStatus("Access_Error", "can't access \'".$Descriptor{1}{"Path"}."\'"); - } - - if(not $Descriptor{2}{"Path"}) { - exitStatus("Error", "-new option is not specified"); - } - if(not -e $Descriptor{2}{"Path"}) { - exitStatus("Access_Error", "can't access \'".$Descriptor{2}{"Path"}."\'"); - } - - detect_default_paths("bin"); # to extract dumps - if(isDump($Descriptor{1}{"Path"}) - and isDump($Descriptor{2}{"Path"})) - { # optimization: equal ABI dumps - quickEmptyReports(); - } - - printMsg("INFO", "preparation, please wait ..."); - - if(isDump($Descriptor{1}{"Path"})) { - read_ABI_Dump(1, $Descriptor{1}{"Path"}); - } - else { - readDescriptor(1, createDescriptor(1, $Descriptor{1}{"Path"})); - } - - if(isDump($Descriptor{2}{"Path"})) { - read_ABI_Dump(2, $Descriptor{2}{"Path"}); - } - else { - readDescriptor(2, createDescriptor(2, $Descriptor{2}{"Path"})); - } - - if(not $Descriptor{1}{"Version"}) - { # set to default: X - $Descriptor{1}{"Version"} = "X"; - print STDERR "WARNING: version number #1 is not set (use --v1=NUM option)\n"; - } - - if(not $Descriptor{2}{"Version"}) - { # set to default: Y - $Descriptor{2}{"Version"} = "Y"; - print STDERR "WARNING: version number #2 is not set (use --v2=NUM option)\n"; - } - - if(not $UsedDump{1}{"V"}) { - initLogging(1); - } - - if(not $UsedDump{2}{"V"}) { + else + { + loadModule("ABIDump"); + readDesc(createDesc($In::Desc{2}{"Path"}, 2), 2); + initLogging(2); + detectDefaultPaths("inc", "lib", undef, "gcc"); + createABIDump(2); } - # check input data - if(not $Descriptor{1}{"Headers"}) { - exitStatus("Error", "can't find header files info in descriptor d1"); - } - if(not $Descriptor{2}{"Headers"}) { - exitStatus("Error", "can't find header files info in descriptor d2"); - } + clearSysFilesCache(1); + clearSysFilesCache(2); - if(not $CheckHeadersOnly) + if(my $FPath = $In::Opt{"FilterPath"}) { - if(not $Descriptor{1}{"Libs"}) { - exitStatus("Error", "can't find libraries info in descriptor d1"); - } - if(not $Descriptor{2}{"Libs"}) { - exitStatus("Error", "can't find libraries info in descriptor d2"); - } - } - - if($UseDumps) - { # --use-dumps - # parallel processing - my $DumpPath1 = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"}); - my $DumpPath2 = defaultDumpPath($TargetLibraryName, $Descriptor{2}{"Version"}); - - unlink($DumpPath1); - unlink($DumpPath2); - - my $pid = fork(); - if($pid) - { # dump on two CPU cores - my @PARAMS = ("-dump", $Descriptor{1}{"Path"}, "-l", $TargetLibraryName); - if($RelativeDirectory{1}) { - @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{1}); - } - if($OutputLogPath{1}) { - @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{1}); - } - if($CrossGcc) { - @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc); - } - if($Quiet) - { - @PARAMS = (@PARAMS, "-quiet"); - @PARAMS = (@PARAMS, "-logging-mode", "a"); - } - elsif($LogMode and $LogMode ne "w") - { # "w" is default - @PARAMS = (@PARAMS, "-logging-mode", $LogMode); - } - if($ExtendedCheck) { - @PARAMS = (@PARAMS, "-extended"); - } - if($UserLang) { - @PARAMS = (@PARAMS, "-lang", $UserLang); - } - if($TargetVersion{1}) { - @PARAMS = (@PARAMS, "-vnum", $TargetVersion{1}); - } - if($BinaryOnly) { - @PARAMS = (@PARAMS, "-binary"); - } - if($SourceOnly) { - @PARAMS = (@PARAMS, "-source"); - } - if($SortDump) { - @PARAMS = (@PARAMS, "-sort"); - } - if($DumpFormat and $DumpFormat ne "perl") { - @PARAMS = (@PARAMS, "-dump-format", $DumpFormat); - } - if($CheckHeadersOnly) { - @PARAMS = (@PARAMS, "-headers-only"); - } - if($Debug) - { - @PARAMS = (@PARAMS, "-debug"); - printMsg("INFO", "running perl $0 @PARAMS"); - } - system("perl", $0, @PARAMS); - if(not -f $DumpPath1) { - exit(1); - } - } - else - { # child - my @PARAMS = ("-dump", $Descriptor{2}{"Path"}, "-l", $TargetLibraryName); - if($RelativeDirectory{2}) { - @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{2}); - } - if($OutputLogPath{2}) { - @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{2}); - } - if($CrossGcc) { - @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc); - } - if($Quiet) - { - @PARAMS = (@PARAMS, "-quiet"); - @PARAMS = (@PARAMS, "-logging-mode", "a"); - } - elsif($LogMode and $LogMode ne "w") - { # "w" is default - @PARAMS = (@PARAMS, "-logging-mode", $LogMode); - } - if($ExtendedCheck) { - @PARAMS = (@PARAMS, "-extended"); - } - if($UserLang) { - @PARAMS = (@PARAMS, "-lang", $UserLang); - } - if($TargetVersion{2}) { - @PARAMS = (@PARAMS, "-vnum", $TargetVersion{2}); - } - if($BinaryOnly) { - @PARAMS = (@PARAMS, "-binary"); - } - if($SourceOnly) { - @PARAMS = (@PARAMS, "-source"); - } - if($SortDump) { - @PARAMS = (@PARAMS, "-sort"); - } - if($DumpFormat and $DumpFormat ne "perl") { - @PARAMS = (@PARAMS, "-dump-format", $DumpFormat); - } - if($CheckHeadersOnly) { - @PARAMS = (@PARAMS, "-headers-only"); - } - if($Debug) - { - @PARAMS = (@PARAMS, "-debug"); - printMsg("INFO", "running perl $0 @PARAMS"); - } - system("perl", $0, @PARAMS); - if(not -f $DumpPath2) { - exit(1); - } - else { - exit(0); - } + if(not -f $FPath) { + exitStatus("Access_Error", "can't access \'".$FPath."\'"); } - waitpid($pid, 0); - my @CMP_PARAMS = ("-l", $TargetLibraryName); - @CMP_PARAMS = (@CMP_PARAMS, "-d1", $DumpPath1); - @CMP_PARAMS = (@CMP_PARAMS, "-d2", $DumpPath2); - if($TargetTitle ne $TargetLibraryName) { - @CMP_PARAMS = (@CMP_PARAMS, "-title", $TargetTitle); - } - if($ShowRetVal) { - @CMP_PARAMS = (@CMP_PARAMS, "-show-retval"); - } - if($CrossGcc) { - @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $CrossGcc); - } - @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a"); - if($Quiet) { - @CMP_PARAMS = (@CMP_PARAMS, "-quiet"); - } - if($ReportFormat and $ReportFormat ne "html") - { # HTML is default format - @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $ReportFormat); - } - if($OutputReportPath) { - @CMP_PARAMS = (@CMP_PARAMS, "-report-path", $OutputReportPath); - } - if($BinaryReportPath) { - @CMP_PARAMS = (@CMP_PARAMS, "-bin-report-path", $BinaryReportPath); - } - if($SourceReportPath) { - @CMP_PARAMS = (@CMP_PARAMS, "-src-report-path", $SourceReportPath); - } - if($LoggingPath) { - @CMP_PARAMS = (@CMP_PARAMS, "-log-path", $LoggingPath); - } - if($CheckHeadersOnly) { - @CMP_PARAMS = (@CMP_PARAMS, "-headers-only"); - } - if($BinaryOnly) { - @CMP_PARAMS = (@CMP_PARAMS, "-binary"); - } - if($SourceOnly) { - @CMP_PARAMS = (@CMP_PARAMS, "-source"); - } - if($Debug) - { - @CMP_PARAMS = (@CMP_PARAMS, "-debug"); - printMsg("INFO", "running perl $0 @CMP_PARAMS"); - } - system("perl", $0, @CMP_PARAMS); - exit($?>>8); - } - if(not $Descriptor{1}{"Dump"} - or not $Descriptor{2}{"Dump"}) - { # need GCC toolchain to analyze - # header files and libraries - detect_default_paths("inc|lib|gcc"); - } - if(not $Descriptor{1}{"Dump"}) - { - if(not $CheckHeadersOnly) { - readLibs(1); - } - if($CheckHeadersOnly) { - setLanguage(1, "C++"); - } - searchForHeaders(1); - $WORD_SIZE{1} = detectWordSize(1); - } - if(not $Descriptor{2}{"Dump"}) - { - if(not $CheckHeadersOnly) { - readLibs(2); - } - if($CheckHeadersOnly) { - setLanguage(2, "C++"); - } - searchForHeaders(2); - $WORD_SIZE{2} = detectWordSize(2); - } - if($WORD_SIZE{1} ne $WORD_SIZE{2}) - { # support for old ABI dumps - # try to synch different WORD sizes - if(not checkDump(1, "2.1")) - { - $WORD_SIZE{1} = $WORD_SIZE{2}; - printMsg("WARNING", "set WORD size to ".$WORD_SIZE{2}." bytes"); - } - elsif(not checkDump(2, "2.1")) + if(my $Filt = readFile($FPath)) { - $WORD_SIZE{2} = $WORD_SIZE{1}; - printMsg("WARNING", "set WORD size to ".$WORD_SIZE{1}." bytes"); + readFilter($Filt, 1); + readFilter($Filt, 2); } } - elsif(not $WORD_SIZE{1} - and not $WORD_SIZE{2}) - { # support for old ABI dumps - $WORD_SIZE{1} = "4"; - $WORD_SIZE{2} = "4"; - } - if($Descriptor{1}{"Dump"}) - { # support for old ABI dumps - prepareTypes(1); - } - if($Descriptor{2}{"Dump"}) - { # support for old ABI dumps - prepareTypes(2); - } - if($AppPath and not keys(%{$Symbol_Library{1}})) { - printMsg("WARNING", "the application ".get_filename($AppPath)." has no symbols imported from the $SLIB_TYPE libraries"); - } - # process input data - if($Descriptor{1}{"Headers"} - and not $Descriptor{1}{"Dump"}) { - readHeaders(1); - } - if($Descriptor{2}{"Headers"} - and not $Descriptor{2}{"Dump"}) { - readHeaders(2); - } - # clean memory - %SystemHeaders = (); - %mangled_name_gcc = (); + prepareCompare(1); + prepareCompare(2); + + if($In::Opt{"AppPath"} and not keys(%{$In::ABI{1}{"SymLib"}})) { + printMsg("WARNING", "the application ".getFilename($In::Opt{"AppPath"})." has no symbols imported from libraries"); + } prepareSymbols(1); prepareSymbols(2); - # clean memory - %SymbolInfo = (); - # Virtual Tables registerVTable(1); registerVTable(2); - - if(not checkDump(1, "1.22") - and checkDump(2, "1.22")) - { # support for old ABI dumps - foreach my $ClassName (keys(%{$VirtualTable{2}})) - { - if($ClassName=~/$OStarget, - "Debug"=>$Debug, - "Quiet"=>$Quiet, - "LogMode"=>$LogMode, - "CheckHeadersOnly"=>$CheckHeadersOnly, - - "SystemRoot"=>$SystemRoot, - "GCC_PATH"=>$GCC_PATH, - "TargetSysInfo"=>$TargetSysInfo, - "CrossPrefix"=>$CrossPrefix, - "TargetLibraryName"=>$TargetLibraryName, - "CrossGcc"=>$CrossGcc, - "UseStaticLibs"=>$UseStaticLibs, - "NoStdInc"=>$NoStdInc, + my $LVer = $_[0]; - "BinaryOnly" => $BinaryOnly, - "SourceOnly" => $SourceOnly - ); - return \%Opts; -} - -sub get_CodeError($) -{ - my %CODE_ERROR = reverse(%ERROR_CODE); - return $CODE_ERROR{$_[0]}; + initABI($LVer); + + $SymbolInfo{$LVer} = $In::ABI{$LVer}{"SymbolInfo"}; + $TypeInfo{$LVer} = $In::ABI{$LVer}{"TypeInfo"}; + $TName_Tid{$LVer} = $In::ABI{$LVer}{"TName_Tid"}; + $Constants{$LVer} = $In::ABI{$LVer}{"Constants"}; + + initAliases_TypeAttr($LVer); } sub scenario() { - if($StdOut) + setTarget("default"); + + initAliases(1); + initAliases(2); + + $In::Opt{"Locale"} = "C.UTF-8"; + $In::Opt{"OrigDir"} = cwd(); + $In::Opt{"Tmp"} = tempdir(CLEANUP=>1); + $In::Opt{"TargetLibShort"} = libPart($In::Opt{"TargetLib"}, "shortest"); + + $In::Opt{"DoubleReport"} = 0; + $In::Opt{"JoinReport"} = 1; + + $In::Opt{"SysPaths"}{"include"} = []; + $In::Opt{"SysPaths"}{"lib"} = []; + $In::Opt{"SysPaths"}{"bin"} = []; + + $In::Opt{"CompileError"} = 0; + + if($In::Opt{"TargetComponent"}) { + $In::Opt{"TargetComponent"} = lc($In::Opt{"TargetComponent"}); + } + else + { # default: library + $In::Opt{"TargetComponent"} = "library"; + } + + foreach (keys(%{$In::Desc{0}})) + { # common options + $In::Desc{1}{$_} = $In::Desc{0}{$_}; + $In::Desc{2}{$_} = $In::Desc{0}{$_}; + } + + $In::Opt{"AddTemplateInstances"} = 1; + $In::Opt{"GccMissedMangling"} = 0; + + if($In::Opt{"StdOut"}) { # enable quiet mode - $Quiet = 1; - $JoinReport = 1; + $In::Opt{"Quiet"} = 1; + $In::Opt{"JoinReport"} = 1; } - if(not $LogMode) + if(not $In::Opt{"LogMode"}) { # default - $LogMode = "w"; + $In::Opt{"LogMode"} = "w"; } - if($UserLang) - { # --lang=C++ - $UserLang = uc($UserLang); - $COMMON_LANGUAGE{1}=$UserLang; - $COMMON_LANGUAGE{2}=$UserLang; + + if($In::Opt{"UserLang"}) { + $In::Opt{"UserLang"} = uc($In::Opt{"UserLang"}); } - if($LoggingPath) + + if(my $LoggingPath = $In::Opt{"LoggingPath"}) { - $OutputLogPath{1} = $LoggingPath; - $OutputLogPath{2} = $LoggingPath; - if($Quiet) { - $COMMON_LOG_PATH = $LoggingPath; + $In::Desc{1}{"OutputLogPath"} = $LoggingPath; + $In::Desc{2}{"OutputLogPath"} = $LoggingPath; + if($In::Opt{"Quiet"}) { + $In::Opt{"DefaultLog"} = $LoggingPath; } } - if($SkipInternalSymbols) { - $SkipInternalSymbols=~s/\*/.*/g; - } - - if($SkipInternalTypes) { - $SkipInternalTypes=~s/\*/.*/g; + if($In::Opt{"Force"}) { + $In::Opt{"GccMissedMangling"} = 1; } - if($Quick) { - $ADD_TMPL_INSTANCES = 0; + if($In::Opt{"Quick"}) { + $In::Opt{"AddTemplateInstances"} = 0; } - if($OutputDumpPath) + if(my $DP = $In::Opt{"OutputDumpPath"}) { # validate - if(not isDump($OutputDumpPath)) { - exitStatus("Error", "the dump path should be a path to *.abi.$AR_EXT or *.abi file"); + if(not isDump($DP)) { + exitStatus("Error", "the dump path should be a path to *.dump or *.dump.".$In::Opt{"Ar"}." file"); } } - if($BinaryOnly and $SourceOnly) + if($In::Opt{"BinOnly"} + and $In::Opt{"SrcOnly"}) { # both --binary and --source # is the default mode - if(not $CmpSystems) + if(not $In::Opt{"CmpSystems"}) { - $BinaryOnly = 0; - $SourceOnly = 0; + $In::Opt{"BinOnly"} = 0; + $In::Opt{"SrcOnly"} = 0; } - $DoubleReport = 1; - $JoinReport = 0; + $In::Opt{"DoubleReport"} = 1; + $In::Opt{"JoinReport"} = 0; - if($OutputReportPath) + if($In::Opt{"OutputReportPath"}) { # --report-path - $DoubleReport = 0; - $JoinReport = 1; + $In::Opt{"DoubleReport"} = 0; + $In::Opt{"JoinReport"} = 1; } } - elsif($BinaryOnly or $SourceOnly) + elsif($In::Opt{"BinOnly"} + or $In::Opt{"SrcOnly"}) { # --binary or --source - $DoubleReport = 0; - $JoinReport = 0; + $In::Opt{"DoubleReport"} = 0; + $In::Opt{"JoinReport"} = 0; } - if($UseXML) + if($In::Opt{"UseXML"}) { # --xml option - $ReportFormat = "xml"; - $DumpFormat = "xml"; + $In::Opt{"ReportFormat"} = "xml"; + $In::Opt{"DumpFormat"} = "xml"; } - if($ReportFormat) + if($In::Opt{"ReportFormat"}) { # validate - $ReportFormat = lc($ReportFormat); - if($ReportFormat!~/\A(xml|html|htm)\Z/) { - exitStatus("Error", "unknown report format \'$ReportFormat\'"); + $In::Opt{"ReportFormat"} = lc($In::Opt{"ReportFormat"}); + if($In::Opt{"ReportFormat"}!~/\A(xml|html|htm)\Z/) { + exitStatus("Error", "unknown report format \'".$In::Opt{"ReportFormat"}."\'"); } - if($ReportFormat eq "htm") + if($In::Opt{"ReportFormat"} eq "htm") { # HTM == HTML - $ReportFormat = "html"; + $In::Opt{"ReportFormat"} = "html"; } - elsif($ReportFormat eq "xml") + elsif($In::Opt{"ReportFormat"} eq "xml") { # --report-format=XML equal to --xml - $UseXML = 1; + $In::Opt{"UseXML"} = 1; } } else { # default: HTML - $ReportFormat = "html"; + $In::Opt{"ReportFormat"} = "html"; } - if($DumpFormat) + if($In::Opt{"DumpFormat"}) { # validate - $DumpFormat = lc($DumpFormat); - if($DumpFormat!~/\A(xml|perl)\Z/) { - exitStatus("Error", "unknown ABI dump format \'$DumpFormat\'"); + $In::Opt{"DumpFormat"} = lc($In::Opt{"DumpFormat"}); + if($In::Opt{"DumpFormat"}!~/\A(xml|perl)\Z/) { + exitStatus("Error", "unknown ABI dump format \'".$In::Opt{"DumpFormat"}."\'"); } - if($DumpFormat eq "xml") + if($In::Opt{"DumpFormat"} eq "xml") { # --dump-format=XML equal to --xml - $UseXML = 1; + $In::Opt{"UseXML"} = 1; } } else { # default: Perl Data::Dumper - $DumpFormat = "perl"; + $In::Opt{"DumpFormat"} = "perl"; } - if($Quiet and $LogMode!~/a|n/) + if($In::Opt{"Quiet"} and $In::Opt{"LogMode"}!~/a|n/) { # --quiet log - if(-f $COMMON_LOG_PATH) { - unlink($COMMON_LOG_PATH); + if(-f $In::Opt{"DefaultLog"}) { + unlink($In::Opt{"DefaultLog"}); } } - if($ExtraInfo) { - $CheckUndefined = 1; + if($In::Opt{"ExtraInfo"}) { + $In::Opt{"CheckUndefined"} = 1; } - if($TestTool and $UseDumps) + + if($In::Opt{"TestTool"} and $In::Opt{"UseDumps"}) { # --test && --use-dumps == --test-dump - $TestDump = 1; + $In::Opt{"TestDump"} = 1; } - if($Tolerant) + if($In::Opt{"Tolerant"}) { # enable all - $Tolerance = 1234; + $In::Opt{"Tolerance"} = 1234; } - if($Help) + if($In::Opt{"Help"}) { - HELP_MESSAGE(); + helpMsg(); exit(0); } - if($InfoMsg) + if($In::Opt{"InfoMsg"}) { - INFO_MESSAGE(); + infoMsg(); exit(0); } - if($ShowVersion) + if($In::Opt{"ShowVersion"}) { - printMsg("INFO", "ABI Compliance Checker (ABICC) $TOOL_VERSION\nCopyright (C) 2015 Andrey Ponomarenko's ABI Laboratory\nLicense: LGPL or GPL \nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko."); + printMsg("INFO", "ABI Compliance Checker (ABICC) $TOOL_VERSION\nCopyright (C) 2018 Andrey Ponomarenko's ABI Laboratory\nLicense: GNU LGPL 2.1 \nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko."); exit(0); } - if($DumpVersion) + if($In::Opt{"DumpVersion"}) { printMsg("INFO", $TOOL_VERSION); exit(0); } - if($ExtendedCheck) { - $CheckHeadersOnly = 1; + if($In::Opt{"ExtendedCheck"}) { + $In::Opt{"CheckHeadersOnly"} = 1; } - if($SystemRoot_Opt) + if($In::Opt{"SystemRoot"}) { # user defined root - if(not -e $SystemRoot_Opt) { - exitStatus("Access_Error", "can't access \'$SystemRoot\'"); + if(not -e $In::Opt{"SystemRoot"}) { + exitStatus("Access_Error", "can't access \'".$In::Opt{"SystemRoot"}."\'"); } - $SystemRoot = $SystemRoot_Opt; - $SystemRoot=~s/[\/]+\Z//g; - if($SystemRoot) { - $SystemRoot = get_abs_path($SystemRoot); + $In::Opt{"SystemRoot"}=~s/[\/]+\Z//g; + if($In::Opt{"SystemRoot"}) { + $In::Opt{"SystemRoot"} = getAbsPath($In::Opt{"SystemRoot"}); } } $Data::Dumper::Sortkeys = 1; - if($SortDump) + if($In::Opt{"SortDump"}) { $Data::Dumper::Useperl = 1; $Data::Dumper::Sortkeys = \&dump_sorting; } - if($TargetLibsPath) + if(my $TargetLibsPath = $In::Opt{"TargetLibsPath"}) { if(not -f $TargetLibsPath) { exitStatus("Access_Error", "can't access file \'$TargetLibsPath\'"); } - foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath))) { - $TargetLibs{$Lib} = 1; + foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath))) + { + if($In::Opt{"OS"} eq "windows") { + $In::Opt{"TargetLibs"}{lc($Lib)} = 1; + } + else { + $In::Opt{"TargetLibs"}{$Lib} = 1; + } } } - if($TargetHeadersPath) + if(my $TPath = $In::Opt{"TargetHeadersPath"}) { # --headers-list - if(not -f $TargetHeadersPath) { - exitStatus("Access_Error", "can't access file \'$TargetHeadersPath\'"); + if(not -f $TPath) { + exitStatus("Access_Error", "can't access file \'$TPath\'"); } - foreach my $Header (split(/\s*\n\s*/, readFile($TargetHeadersPath))) + + $In::Desc{1}{"TargetHeader"} = {}; + $In::Desc{2}{"TargetHeader"} = {}; + + foreach my $Header (split(/\s*\n\s*/, readFile($TPath))) { - $TargetHeaders{1}{get_filename($Header)} = 1; - $TargetHeaders{2}{get_filename($Header)} = 1; + my $Name = getFilename($Header); + $In::Desc{1}{"TargetHeader"}{$Name} = 1; + $In::Desc{2}{"TargetHeader"}{$Name} = 1; } } - if($TargetHeader) + if($In::Opt{"TargetHeader"}) { # --header - $TargetHeaders{1}{get_filename($TargetHeader)} = 1; - $TargetHeaders{2}{get_filename($TargetHeader)} = 1; + $In::Desc{1}{"TargetHeader"} = {}; + $In::Desc{2}{"TargetHeader"} = {}; + + my $Name = getFilename($In::Opt{"TargetHeader"}); + $In::Desc{1}{"TargetHeader"}{$Name} = 1; + $In::Desc{2}{"TargetHeader"}{$Name} = 1; } - if($TestTool - or $TestDump) + if($In::Opt{"TestABIDumper"}) + { + if($In::Opt{"OS"} ne "linux") { + exitStatus("Error", "-test-abi-dumper option is available on Linux only"); + } + } + if($In::Opt{"TestTool"} + or $In::Opt{"TestDump"} + or $In::Opt{"TestABIDumper"}) { # --test, --test-dump - detect_default_paths("bin|gcc"); # to compile libs + detectDefaultPaths(undef, undef, "bin", "gcc"); # to compile libs loadModule("RegTests"); - testTool($TestDump, $Debug, $Quiet, $ExtendedCheck, $LogMode, $ReportFormat, $DumpFormat, - $LIB_EXT, $GCC_PATH, $SortDump, $CheckHeadersOnly); + testTool(); exit(0); } - if($DumpSystem) + if($In::Opt{"DumpSystem"}) { # --dump-system + if(not $In::Opt{"TargetSysInfo"}) + { + if(-d $MODULES_DIR."/Targets/" + and -d $MODULES_DIR."/Targets/".$In::Opt{"Target"}) { + $In::Opt{"TargetSysInfo"} = $MODULES_DIR."/Targets/".$In::Opt{"Target"}; + } + } - if(not $TargetSysInfo) { + if(not $In::Opt{"TargetSysInfo"}) { exitStatus("Error", "-sysinfo option should be specified to dump system ABI"); } - if(not -d $TargetSysInfo) { - exitStatus("Access_Error", "can't access \'$TargetSysInfo\'"); + if(not -d $In::Opt{"TargetSysInfo"}) { + exitStatus("Access_Error", "can't access \'".$In::Opt{"TargetSysInfo"}."\'"); } loadModule("SysCheck"); - if($DumpSystem=~/\.(xml|desc)\Z/) + if($In::Opt{"DumpSystem"}=~/\.(xml|desc)\Z/) { # system XML descriptor - if(not -f $DumpSystem) { - exitStatus("Access_Error", "can't access file \'$DumpSystem\'"); + if(not -f $In::Opt{"DumpSystem"}) { + exitStatus("Access_Error", "can't access file \'".$In::Opt{"DumpSystem"}."\'"); } - my $SDesc = readFile($DumpSystem); - if(my $RelDir = $RelativeDirectory{1}) { - $SDesc =~ s/{RELPATH}/$RelDir/g; + my $SDesc = readFile($In::Opt{"DumpSystem"}); + if(my $RelDir = $In::Desc{1}{"RelativeDirectory"}) { + $SDesc=~s/\{RELPATH\}/$RelDir/g; } - my $Ret = readSystemDescriptor($SDesc); - foreach (@{$Ret->{"Tools"}}) - { - push_U($SystemPaths{"bin"}, $_); - $TargetTools{$_} = 1; - } - if($Ret->{"CrossPrefix"}) { - $CrossPrefix = $Ret->{"CrossPrefix"}; - } + readSysDesc($SDesc); } - elsif($SystemRoot_Opt) + elsif(defined $In::Opt{"SystemRoot"}) { # -sysroot "/" option # default target: /usr/lib, /usr/include # search libs: /usr/lib and /lib + my $SystemRoot = $In::Opt{"SystemRoot"}; + if(not -e $SystemRoot."/usr/lib") { exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/lib'"); } @@ -22594,9 +10438,9 @@ if(not -e $SystemRoot."/usr/include") { exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/include'"); } - readSystemDescriptor(" + readSysDesc(" - $DumpSystem + ".$In::Opt{"DumpSystem"}." $SystemRoot/usr/include @@ -22611,90 +10455,105 @@ else { exitStatus("Error", "-sysroot option should be specified, usually it's \"/\""); } - detect_default_paths("bin|gcc"); # to check symbols - if($OStarget eq "windows") + detectDefaultPaths(undef, undef, "bin", "gcc"); # to check symbols + if($In::Opt{"Target"} eq "windows") { # to run dumpbin.exe # and undname.exe - check_win32_env(); + checkWin32Env(); } - dumpSystem(getSysOpts()); + dumpSystem(); exit(0); } - if($CmpSystems) + + if($In::Opt{"CmpSystems"}) { # --cmp-systems - detect_default_paths("bin"); # to extract dumps + detectDefaultPaths(undef, undef, "bin", undef); # to extract dumps loadModule("SysCheck"); - cmpSystems($Descriptor{1}{"Path"}, $Descriptor{2}{"Path"}, getSysOpts()); + + if(not $In::Opt{"BinOnly"} + and not $In::Opt{"SrcOnly"}) + { # default + $In::Opt{"BinOnly"} = 1; + } + + cmpSystems($In::Desc{1}{"Path"}, $In::Desc{2}{"Path"}); exit(0); } - if(not $TargetLibraryName) { - exitStatus("Error", "library name is not selected (-l option)"); - } - else - { # validate library name - if($TargetLibraryName=~/[\*\/\\]/) { - exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name"); + + if(not $In::Opt{"CountSymbols"}) + { + if(not $In::Opt{"TargetLib"}) { + exitStatus("Error", "library name is not selected (-l option)"); + } + else + { # validate library name + if($In::Opt{"TargetLib"}=~/[\*\/\\]/) { + exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name"); + } } } - if(not $TargetTitle) { - $TargetTitle = $TargetLibraryName; + + if(not $In::Opt{"TargetTitle"}) { + $In::Opt{"TargetTitle"} = $In::Opt{"TargetLib"}; } - if($SymbolsListPath) + if(my $SymbolsListPath = $In::Opt{"SymbolsListPath"}) { if(not -f $SymbolsListPath) { exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'"); } - foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath))) { - $SymbolsList{$Interface} = 1; + foreach my $S (split(/\s*\n\s*/, readFile($SymbolsListPath))) + { + $In::Desc{1}{"SymbolsList"}{$S} = 1; + $In::Desc{2}{"SymbolsList"}{$S} = 1; } } - if($TypesListPath) + if(my $TypesListPath = $In::Opt{"TypesListPath"}) { if(not -f $TypesListPath) { exitStatus("Access_Error", "can't access file \'$TypesListPath\'"); } - foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) { - $TypesList{$Type} = 1; + foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) + { + $In::Desc{1}{"TypesList"}{$Type} = 1; + $In::Desc{2}{"TypesList"}{$Type} = 1; } } - if($SkipSymbolsListPath) + if(my $SymbolsListPath = $In::Opt{"SkipSymbolsListPath"}) { - if(not -f $SkipSymbolsListPath) { - exitStatus("Access_Error", "can't access file \'$SkipSymbolsListPath\'"); + if(not -f $SymbolsListPath) { + exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'"); } - foreach my $Interface (split(/\s*\n\s*/, readFile($SkipSymbolsListPath))) + foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath))) { - $SkipSymbols{1}{$Interface} = 1; - $SkipSymbols{2}{$Interface} = 1; + $In::Desc{1}{"SkipSymbols"}{$Interface} = 1; + $In::Desc{2}{"SkipSymbols"}{$Interface} = 1; } } - if($SkipTypesListPath) + if(my $TypesListPath = $In::Opt{"SkipTypesListPath"}) { - if(not -f $SkipTypesListPath) { - exitStatus("Access_Error", "can't access file \'$SkipTypesListPath\'"); + if(not -f $TypesListPath) { + exitStatus("Access_Error", "can't access file \'$TypesListPath\'"); } - foreach my $Type (split(/\s*\n\s*/, readFile($SkipTypesListPath))) + foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) { - $SkipTypes{1}{$Type} = 1; - $SkipTypes{2}{$Type} = 1; + $In::Desc{1}{"SkipTypes"}{$Type} = 1; + $In::Desc{2}{"SkipTypes"}{$Type} = 1; } } - if($SkipHeadersPath) + if(my $HeadersList = $In::Opt{"SkipHeadersPath"}) { - if(not -f $SkipHeadersPath) { - exitStatus("Access_Error", "can't access file \'$SkipHeadersPath\'"); + if(not -f $HeadersList) { + exitStatus("Access_Error", "can't access file \'$HeadersList\'"); } - foreach my $Path (split(/\s*\n\s*/, readFile($SkipHeadersPath))) - { # register for both versions - $SkipHeadersList{1}{$Path} = 1; - $SkipHeadersList{2}{$Path} = 1; + foreach my $Path (split(/\s*\n\s*/, readFile($HeadersList))) + { my ($CPath, $Type) = classifyPath($Path); - $SkipHeaders{1}{$Type}{$CPath} = 1; - $SkipHeaders{2}{$Type}{$CPath} = 1; + $In::Desc{1}{"SkipHeaders"}{$Type}{$CPath} = 1; + $In::Desc{2}{"SkipHeaders"}{$Type}{$CPath} = 1; } } - if($ParamNamesPath) + if(my $ParamNamesPath = $In::Opt{"ParamNamesPath"}) { if(not -f $ParamNamesPath) { exitStatus("Access_Error", "can't access file \'$ParamNamesPath\'"); @@ -22707,49 +10566,104 @@ if($Line=~/;(\d+);/) { while($Line=~s/(\d+);(\w+)//) { - $AddIntParams{$Interface}{$1}=$2; + $AddSymbolParams{$Interface}{$1}=$2; } } else { my $Num = 0; foreach my $Name (split(/;/, $Line)) { - $AddIntParams{$Interface}{$Num++}=$Name; + $AddSymbolParams{$Interface}{$Num++}=$Name; } } } } } - if($AppPath) + + if(my $AppPath = $In::Opt{"AppPath"}) { if(not -f $AppPath) { exitStatus("Access_Error", "can't access file \'$AppPath\'"); } - detect_default_paths("bin|gcc"); - foreach my $Interface (readSymbols_App($AppPath)) { - $SymbolsList_App{$Interface} = 1; + detectDefaultPaths(undef, undef, "bin", "gcc"); + foreach my $Symbol (readSymbols_App($AppPath)) { + $In::Opt{"SymbolsList_App"}{$Symbol} = 1; + } + } + + if(my $Path = $In::Opt{"CountSymbols"}) + { + if(not -e $Path) { + exitStatus("Access_Error", "can't access \'$Path\'"); + } + + $In::ABI{1} = readABIDump(1, $Path); + initAliases(1); + + foreach my $Id (keys(%{$SymbolInfo{1}})) + { + my $MnglName = $SymbolInfo{1}{$Id}{"MnglName"}; + if(not $MnglName) { + $MnglName = $SymbolInfo{1}{$Id}{"ShortName"} + } + + if(my $SV = $In::ABI{1}{"SymbolVersion"}{$MnglName}) { + $CompSign{1}{$SV} = $SymbolInfo{1}{$Id}; + } + else { + $CompSign{1}{$MnglName} = $SymbolInfo{1}{$Id}; + } + + if(my $Alias = $CompSign{1}{$MnglName}{"Alias"}) { + $CompSign{1}{$Alias} = $SymbolInfo{1}{$Id}; + } + } + + my $Count = 0; + foreach my $Symbol (sort keys(%{$CompSign{1}})) + { + if($CompSign{1}{$Symbol}{"PureVirt"}) { + next; + } + + if(not $CompSign{1}{$Symbol}{"Header"}) + { + if(index($CompSign{1}{$Symbol}{"Source"}, ".f")==-1) + { # Fortran + next; + } + } + + $Count += symbolFilter($Symbol, $CompSign{1}{$Symbol}, "Affected + InlineVirt", "Binary", 1); } + + printMsg("INFO", $Count); + exit(0); } - if($DumpAPI) - { # --dump-abi - # make an API dump - create_ABI_Dump(); - exit($COMPILE_ERRORS); + + if($In::Opt{"DumpABI"}) + { + createABIFile(1, $In::Opt{"DumpABI"}); + + if($In::Opt{"CompileError"}) { + exit(getErrorCode("Compile_Error")); + } + + exit(0); } + # default: compare APIs - # -d1 - # -d2 compareInit(); - if($JoinReport or $DoubleReport) + if($In::Opt{"JoinReport"} or $In::Opt{"DoubleReport"}) { compareAPIs("Binary"); compareAPIs("Source"); } - elsif($BinaryOnly) { + elsif($In::Opt{"BinOnly"}) { compareAPIs("Binary"); } - elsif($SourceOnly) { + elsif($In::Opt{"SrcOnly"}) { compareAPIs("Source"); } exitReport(); diff -Nru abi-compliance-checker-1.99.14/debian/changelog abi-compliance-checker-2.3/debian/changelog --- abi-compliance-checker-1.99.14/debian/changelog 2015-11-06 20:42:31.000000000 +0000 +++ abi-compliance-checker-2.3/debian/changelog 2024-03-19 00:45:44.000000000 +0000 @@ -1,3 +1,119 @@ +abi-compliance-checker (2.3-2~16.04.sav0) xenial; urgency=medium + + * Backport to Xenial + + -- Rob Savoury Mon, 18 Mar 2024 17:45:44 -0700 + +abi-compliance-checker (2.3-2) unstable; urgency=medium + + [ Mathieu Malaterre ] + * d/control: Point to actual correct homepage + + [ Steve Langasek ] + * d/patches: Fix Invalid use of Perl "next". Closes: #951076 + * d/patches: uses inconsitent flags to GCC for C code. Closes: #1030540 + + [ Mathieu Malaterre ] + * d/s/lintian-overrides: remove warning about doc/index.html + * d/control: Bump Std-Vers to 4.6.2 no changes needed + + -- Mathieu Malaterre Thu, 23 Feb 2023 08:27:46 +0100 + +abi-compliance-checker (2.3-1) unstable; urgency=medium + + * d/patches: statx and statx_timestamp need to be skipped. Closes: #916294 + * d/control: Update location of source code + * d/control: Update new Vcs locations + * d/compat: Move to compat 10 + * d/control: Bumpt Std-Vers to 4.6.0 no changes needed + + -- Mathieu Malaterre Thu, 09 Dec 2021 07:55:56 +0100 + +abi-compliance-checker (2.3-0.2) unstable; urgency=medium + + * Non-maintainer upload + + [ Steve Langasek ] + * debian/patches/fpic-for-arm64.patch: require -fPIC on arm64 in + addition to x86_64 and arm (Closes: #907604). + * debian/patches/oom-exec-helper.patch: Run packing commands in a + subprocess (Closes: #907602). + + -- Gianfranco Costamagna Tue, 04 Sep 2018 10:28:14 +0200 + +abi-compliance-checker (2.3-0.1) unstable; urgency=medium + + * Non-maintainer upload + * New upstream release (Closes: #904126) + * Update copyright and licenses + + -- Gianfranco Costamagna Thu, 19 Jul 2018 20:57:41 +0200 + +abi-compliance-checker (2.2-2) unstable; urgency=medium + + * Update Std-Vers to 4.1.0, no changes needed + * Fix lintian warning: privacy-breach-logo + + -- Mathieu Malaterre Wed, 20 Sep 2017 14:24:17 +0200 + +abi-compliance-checker (2.2-1) unstable; urgency=medium + + * New upstream release: 2.2. Remove patches: + - d/p/emergency-mode.patch + - d/p/fix-test-compilation.patch + - d/p/remove-gcc-check.patch + * Remove Ryan Niebur from Uploaders. Closes: #856362. + Thanks for your work ! + + -- Mathieu Malaterre Fri, 15 Sep 2017 21:44:01 +0200 + +abi-compliance-checker (1.99.22-1.1) unstable; urgency=high + + [ Dmitry Shachnev ] + * Add a patch to fix tests compilation with new GCC. + * Add a patch to not rely on GCC for symbols mangling, it is not working + with GCC 6 and GCC 7 because of PR c++/78040. + + [ Gianfranco Costamagna ] + * Non-maintainer upload + * Fix autopkgtests, due to bad doit call (Closes: #868473) + + -- Gianfranco Costamagna Fri, 01 Sep 2017 07:51:53 +0200 + +abi-compliance-checker (1.99.22-1) unstable; urgency=medium + + * New upstream release + + -- Mathieu Malaterre Sat, 16 Jul 2016 21:24:11 +0200 + +abi-compliance-checker (1.99.20-3) unstable; urgency=medium + + * Fix copyright file missing. Closes: #825556 + + -- Mathieu Malaterre Wed, 01 Jun 2016 17:47:57 +0200 + +abi-compliance-checker (1.99.20-2) unstable; urgency=medium + + * Make sure to build manpage during indep build. + + -- Mathieu Malaterre Mon, 23 May 2016 09:55:55 +0200 + +abi-compliance-checker (1.99.20-1) unstable; urgency=medium + + * New release. Closes: #825053. Remove patches applied upstream: + - d/p/bug798470.patch + - d/p/bug805441.patch + * Bump Std-Vers to 3.9.8, no changes needed + + -- Mathieu Malaterre Mon, 23 May 2016 09:09:59 +0200 + +abi-compliance-checker (1.99.14-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Restore support for GNU/Hurd - backport from upstream (Closes: #805441) + + -- Mattias Ellert Sun, 08 May 2016 06:34:53 +0200 + abi-compliance-checker (1.99.14-1) unstable; urgency=medium * New release. Refresh patches. diff -Nru abi-compliance-checker-1.99.14/debian/compat abi-compliance-checker-2.3/debian/compat --- abi-compliance-checker-1.99.14/debian/compat 2013-04-19 13:08:55.000000000 +0000 +++ abi-compliance-checker-2.3/debian/compat 2021-12-09 06:55:26.000000000 +0000 @@ -1 +1 @@ -9 +10 diff -Nru abi-compliance-checker-1.99.14/debian/control abi-compliance-checker-2.3/debian/control --- abi-compliance-checker-1.99.14/debian/control 2015-09-16 19:54:01.000000000 +0000 +++ abi-compliance-checker-2.3/debian/control 2023-02-23 07:27:22.000000000 +0000 @@ -1,14 +1,13 @@ Source: abi-compliance-checker Section: devel Priority: optional -Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.16.1~), help2man -Maintainer: Ryan Niebur -Uploaders: Mathieu Malaterre -Standards-Version: 3.9.6 -Homepage: http://ispras.linux-foundation.org/index.php/ABI_compliance_checker -Vcs-Svn: svn://anonscm.debian.org/collab-maint/deb-maint/abi-compliance-checker/trunk -Vcs-Browser: http://anonscm.debian.org/viewvc/collab-maint/deb-maint/abi-compliance-checker/trunk/ +Build-Depends: debhelper (>= 10), dpkg-dev (>= 1.16.1~), help2man +Maintainer: Mathieu Malaterre +Standards-Version: 4.6.2 +Homepage: https://lvc.github.io/abi-compliance-checker/ XS-Testsuite: autopkgtest +Vcs-Browser: https://salsa.debian.org/debian/abi-compliance-checker +Vcs-Git: https://salsa.debian.org/debian/abi-compliance-checker.git Package: abi-compliance-checker Architecture: all diff -Nru abi-compliance-checker-1.99.14/debian/copyright abi-compliance-checker-2.3/debian/copyright --- abi-compliance-checker-1.99.14/debian/copyright 2015-09-16 19:54:01.000000000 +0000 +++ abi-compliance-checker-2.3/debian/copyright 2021-12-09 06:53:37.000000000 +0000 @@ -1,11 +1,12 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Source: http://ispras.linux-foundation.org/index.php/ABI_compliance_checker_Downloads +Upstream-Source: https://github.com/lvc/abi-compliance-checker/ Upstream-Name: ABI compliance checker Files: * Copyright: Copyright 2009, The Linux Foundation Copyright 2009, Institute for System Programming, Russian Academy of Sciences -License: GPL-3+ + Copyright: 2015-2018 Andrey Ponomarenko's ABI Laboratory +License: LGPL-2.1+ Files: debian/* Copyright: 2009, Ryan Niebur @@ -15,6 +16,24 @@ Copyright: 2009, Paul Sladen License: GPL-3+ +License: LGPL-2.1+ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, 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 Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + . + On Debian systems, the complete text of the GNU Lesser General Public + License version 2.1 can be found in ‘/usr/share/common-licenses/LGPL-2.1’. + License: GPL-3+ 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 diff -Nru abi-compliance-checker-1.99.14/debian/dh/dh_acc abi-compliance-checker-2.3/debian/dh/dh_acc --- abi-compliance-checker-1.99.14/debian/dh/dh_acc 2013-04-19 13:08:55.000000000 +0000 +++ abi-compliance-checker-2.3/debian/dh/dh_acc 2021-12-09 06:49:16.000000000 +0000 @@ -56,11 +56,11 @@ if ($definition) { # TODO if next command fails, should output debug/log info? - doit("abi-compliance-checker -q -l $package -v1 $version -dump $definition -dump-path $abidump"); + doit('abi-compliance-checker', '-q', '-l', $package, '-v1', $version, '-dump', $definition, '-dump-path', $abidump); } if ($base) { - doit("abi-compliance-checker -l $package -d1 $base -d2 $abidump -report-path ${path}_report.html"); - doit("abi-compliance-checker -q -l $package -d1 $base -d2 $abidump -xml -report-path ${path}_report.xml"); + doit('abi-compliance-checker', '-l', $package, '-d1', $base, '-d2', $abidump, '-report-path', ${path}.'_report.html'); + doit('abi-compliance-checker', '-q', '-l', $package, '-d1', $base, '-d2', $abidump, '-xml', '-report-path', ${path}.'_report.xml'); } # TODO clean up temp files & logs } diff -Nru abi-compliance-checker-1.99.14/debian/patches/0659b3b0a23e3535f41c6fd8dee5255ad8e29f7e.patch abi-compliance-checker-2.3/debian/patches/0659b3b0a23e3535f41c6fd8dee5255ad8e29f7e.patch --- abi-compliance-checker-1.99.14/debian/patches/0659b3b0a23e3535f41c6fd8dee5255ad8e29f7e.patch 1970-01-01 00:00:00.000000000 +0000 +++ abi-compliance-checker-2.3/debian/patches/0659b3b0a23e3535f41c6fd8dee5255ad8e29f7e.patch 2021-12-09 06:53:37.000000000 +0000 @@ -0,0 +1,49 @@ +From 0659b3b0a23e3535f41c6fd8dee5255ad8e29f7e Mon Sep 17 00:00:00 2001 +From: Kier Davis +Date: Mon, 13 Sep 2021 08:41:05 -0500 +Subject: [PATCH] add statx to list of ignored functions + +statx (like stat, wait, flock, sysinfo) is both the name of a function: + + /usr/include/bits/statx-generic.h:int statx (int __dirfd, const char *__restrict __path, int __flags, + /usr/include/bits/statx-generic.h: unsigned int __mask, struct statx *__restrict __buf) + +and a struct: + + /usr/include/linux/stat.h:struct statx { + ... + /usr/include/linux/stat.h:}; + +Typically `statx` always denotes the function and `struct statx` denotes the struct. + +When usage of the struct is encountered in C++ code, abi-compliance-checker generates this line in dump1.h: + + statx* tmp_add_class_66; + +which fails to compile: + + /tmp/qktDQcucCm/dump1.h:103:3: error: 'statx' does not name a type + statx* tmp_add_class_66; + ^~~~~ + +Ideally abi-compliance-checker should instead generate this in dump1.h: + + struct statx* tmp_add_class_66; + +but determining when the `struct` qualifier should/shouldn't be used seems non-trivial, so lets take the same approach as we do for other similarly overloaded names. +--- + modules/Internals/TUDump.pm | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/modules/Internals/TUDump.pm b/modules/Internals/TUDump.pm +index 1bc72f3..4eb7d35 100644 +--- a/modules/Internals/TUDump.pm ++++ b/modules/Internals/TUDump.pm +@@ -45,6 +45,7 @@ my %C_Structure = map {$_=>1} ( + "stat64", + "_stat64", + "_stati64", ++ "statx", + "if_nameindex", + "usb_device", + "sigaltstack", diff -Nru abi-compliance-checker-1.99.14/debian/patches/bug798470.patch abi-compliance-checker-2.3/debian/patches/bug798470.patch --- abi-compliance-checker-1.99.14/debian/patches/bug798470.patch 2015-11-06 20:42:09.000000000 +0000 +++ abi-compliance-checker-2.3/debian/patches/bug798470.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -Description: drop timestamp from generated HTML reports -Forwarded: not-needed -Bug-Debian: http://bugs.debian.org/798470 -Author: Chris Lamb -Reviewed-by: Mathieu Malaterre - -Index: abi-compliance-checker-1.99.14/abi-compliance-checker.pl -=================================================================== ---- abi-compliance-checker-1.99.14.orig/abi-compliance-checker.pl -+++ abi-compliance-checker-1.99.14/abi-compliance-checker.pl -@@ -17598,7 +17598,7 @@ sub getReportFooter() - my $Footer = ""; - - $Footer .= "
"; -- $Footer .= "