blob: b21aedef8071c2ac7734ca57619bc26440c799cd [file] [log] [blame]
#!/usr/bin/perl
###########################################################################
# ABI Compliance Checker (ABICC) 1.99.26
# 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-2016 Andrey Ponomarenko's ABI Laboratory
#
# Written by Andrey Ponomarenko
#
# PLATFORMS
# =========
# Linux, FreeBSD, Mac OS X, Haiku, MS Windows, Symbian
#
# REQUIREMENTS
# ============
# Linux
# - G++ (3.0-4.7, 4.8.3, 4.9 or newer)
# - GNU Binutils (readelf, c++filt, objdump)
# - Perl 5
# - Ctags
# - ABI Dumper >= 0.99.15
#
# Mac OS X
# - Xcode (g++, c++filt, otool, nm)
# - Ctags
#
# MS Windows
# - MinGW (3.0-4.7, 4.8.3, 4.9 or newer)
# - MS Visual C++ (dumpbin, undname, cl)
# - Active Perl 5 (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 vcvars64.bat (C:\Microsoft Visual Studio 9.0\VC\bin\)
#
# 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,
# 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
# and the GNU Lesser General Public License along with this program.
# If not, see <http://www.gnu.org/licenses/>.
###########################################################################
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 Data::Dumper;
use Config;
my $TOOL_VERSION = "1.99.26";
my $ABI_DUMP_VERSION = "3.3";
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));
# 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, $CxxCompat, $LogMode, $StdOut, $ListAffected, $ReportFormat,
$UserLang, $TargetHeadersPath, $BinaryOnly, $SourceOnly, $BinaryReportPath,
$SourceReportPath, $UseXML, $SortDump, $DumpFormat,
$ExtraInfo, $ExtraDump, $Force, $Tolerance, $Tolerant, $SkipSymbolsListPath,
$CheckInfo, $Quick, $AffectLimit, $AllAffected, $CxxIncompat,
$SkipInternalSymbols, $SkipInternalTypes, $TargetArch, $GccOptions,
$TypesListPath, $SkipTypesListPath, $CheckPrivateABI, $CountSymbols, $OldStyle,
$DisableQuickEmptyReport, $SkipTypedefUncover, $MinGWCompat, $SkipUnidentified,
$DisableConstantsCheck, $SkipAddedConstants, $SkipRemovedConstants, $TestABIDumper);
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 %OS_Archive = (
"windows"=>"zip",
"default"=>"tar.gz"
);
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 = (
"Dev"=>"https://github.com/lvc/abi-compliance-checker",
"Wiki"=>"https://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) 2016 Andrey Ponomarenko's ABI Laboratory
License: GNU LGPL or GNU GPL
Usage: $CmdName [options]
Example: $CmdName -lib NAME -old OLD.xml -new NEW.xml
OLD.xml and NEW.xml are XML-descriptors:
<version>
1.0
</version>
<headers>
/path/to/headers/
</headers>
<libs>
/path/to/libraries/
</libs>
More info: $CmdName --help\n";
if($#ARGV==-1)
{
printMsg("INFO", $ShortUsage);
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|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,
"disable-constants-check!" => \$DisableConstantsCheck,
"skip-added-constants!" => \$SkipAddedConstants,
"skip-removed-constants!" => \$SkipRemovedConstants,
"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,
"count-symbols=s" => \$CountSymbols,
"old-style!" => \$OldStyle,
# other options
"test!" => \$TestTool,
"test-dump!" => \$TestDump,
"test-abi-dumper!" => \$TestABIDumper,
"debug!" => \$Debug,
"cpp-compatible!" => \$CxxCompat,
"cxx-incompatible|cpp-incompatible!" => \$CxxIncompat,
"mingw-compatible!" => \$MinGWCompat,
"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,
"skip-unidentified!" => \$SkipUnidentified,
"check!" => \$CheckInfo,
"quick!" => \$Quick,
"disable-quick-empty-report!" => \$DisableQuickEmptyReport,
"all-affected!" => \$AllAffected,
"skip-internal-symbols|skip-internal=s" => \$SkipInternalSymbols,
"skip-internal-types=s" => \$SkipInternalTypes,
"skip-typedef-uncover!" => \$SkipTypedefUncover,
"check-private-abi!" => \$CheckPrivateABI
) or ERR_MESSAGE();
sub ERR_MESSAGE()
{
printMsg("INFO", "\n".$ShortUsage);
exit($ERROR_CODE{"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";
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.
The tool is intended for developers of software libraries and maintainers
of operating systems who are interested in ensuring backward compatibility,
i.e. allow old applications to run or to be recompiled with newer library
versions.
Also the tool can be used by ISVs for checking applications portability to
new library versions. Found issues can be taken into account when adapting
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.
USAGE:
$CmdName [options]
EXAMPLE:
$CmdName -lib NAME -old OLD.xml -new NEW.xml
OLD.xml and NEW.xml are XML-descriptors:
<version>
1.0
</version>
<headers>
/path1/to/header(s)/
/path2/to/header(s)/
...
</headers>
<libs>
/path1/to/library(ies)/
/path2/to/library(ies)/
...
</libs>
INFORMATION OPTIONS:
-h|-help
Print this help.
-i|-info
Print complete info.
-v|-version
Print version information.
-dumpversion
Print the tool version ($TOOL_VERSION) and don't do anything else.
GENERAL OPTIONS:
-l|-lib|-library NAME
Library name (without version).
-d1|-old|-o PATH
Descriptor of 1st (old) library version.
It may be one of the following:
1. XML-descriptor (VERSION.xml file):
<version>
1.0
</version>
<headers>
/path1/to/header(s)/
/path2/to/header(s)/
...
</headers>
<libs>
/path1/to/library(ies)/
/path2/to/library(ies)/
...
</libs>
...
2. ABI dump generated by -dump option
3. Directory with headers and/or $SLIB_TYPE libraries
4. Single header file
If you are using an 2-4 descriptor types then you should
specify version numbers with -v1 and -v2 options too.
For more information, please see:
http://ispras.linuxbase.org/index.php/Library_Descriptor
-d2|-new|-n PATH
Descriptor of 2nd (new) library version.
-dump|-dump-abi 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";
sub HELP_MESSAGE() {
printMsg("INFO", $HelpMessage."
MORE INFO:
$CmdName --info\n");
}
sub INFO_MESSAGE()
{
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.
-static
Check static libraries instead of the shared ones. The <libs> section
of the XML-descriptor should point to static libraries location.
-gcc-path PATH
Path to the cross GCC compiler to use instead of the usual (host) GCC.
-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.
-v1|-version1 NUM
Specify 1st library version outside the descriptor. This option is needed
if you have preferred an alternative descriptor type (see -d1 option).
In general case you should specify it in the XML-descriptor:
<version>
VERSION
</version>
-v2|-version2 NUM
Specify 2nd library version outside the descriptor.
-vnum NUM
Specify the library version in the generated ABI dump. The <version> section
of the input XML descriptor will be overwritten in this case.
-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 <libs> section
in the XML-descriptor:
<libs>
none
</libs>
-show-retval
Show the symbol's return type in the report.
-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.
-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.
-headers-list PATH
The file with a list of headers, that should be checked/dumped.
-skip-headers PATH
The file with the list of header files, that should not be checked.
-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.
-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
set of ABI dumps can be compared (--cmp-systems) with the other one
created for other version of operating system in order to check them for
compatibility. Do not forget to specify -cross-gcc option if your target
system requires some specific version of GCC compiler (different from
the host GCC). The system ABI dump will be generated to:
sys_dumps/NAME/ARCH
-dump-system DESCRIPTOR.xml
The same as the previous option but takes an XML descriptor of the target
system as input, where you should describe it:
/* Primary sections */
<name>
/* Name of the system */
</name>
<headers>
/* The list of paths to header files and/or
directories with header files, one per line */
</headers>
<libs>
/* The list of paths to shared libraries and/or
directories with shared libraries, one per line */
</libs>
/* Optional sections */
<search_headers>
/* List of directories to be searched
for header files to automatically
generate include paths, one per line */
</search_headers>
<search_libs>
/* List of directories to be searched
for shared libraries to resolve
dependencies, one per line */
</search_libs>
<tools>
/* List of directories with tools used
for analysis (GCC toolchain), one per line */
</tools>
<cross_prefix>
/* GCC toolchain prefix.
Examples:
arm-linux-gnueabi
arm-none-symbianelf */
</cross_prefix>
<gcc_options>
/* Additional GCC options, one per line */
</gcc_options>
-sysinfo DIR
This option should be used with -dump-system option to dump
ABI of operating systems and configure the dumping process.
-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
-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.
-count-symbols PATH
Count total public symbols in the ABI dump.
-old-style
Generate old-style 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.
-test-abi-dumper
Compare ABI dumps created by the ABI Dumper tool.
-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
Do nothing.
-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.
-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.
-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
-log-path PATH
Log path for all messages.
Default:
logs/LIB_NAME/VERSION/log.txt
-log1-path PATH
Log path for 1st version of a library.
Default:
logs/LIB_NAME/V1/log.txt
-log2-path PATH
Log path for 2nd version of a library.
Default:
logs/LIB_NAME/V2/log.txt
-logging-mode MODE
Change logging mode.
Modes:
w - overwrite old logs (default)
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 enable this option if the tool checked not all
types and symbols in 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.
-check
Check completeness of the ABI dump.
-quick
Quick analysis. Disable check of some template instances.
-disable-quick-empty-report
Do not generate quick empty report if input ABI dumps are equal.
-skip-internal-symbols PATTERN
Do not check symbols matched by the pattern.
-skip-internal-types PATTERN
Do not check types matched by the pattern.
-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
REPORT:
Compatibility report will be generated to:
compat_reports/LIB_NAME/V1_to_V2/compat_report.html
Log will be generated to:
logs/LIB_NAME/V1/log.txt
logs/LIB_NAME/V2/log.txt
EXIT CODES:
0 - Compatible. The tool has run without any errors.
non-zero - Incompatible or the tool has run with errors.
MORE INFORMATION:
".$HomePage{"Wiki"}."
".$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 $GCC_MISSED_MNGL = 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, %CLANG_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";
}
my $TOP_REF = "<a class='top_ref' href='#Top'>to the top</a>";
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"=>{} );
my %CheckedTypes;
my %TName_Tid;
my %EnumMembName_Id;
my %NestedNameSpaces = (
"1"=>{},
"2"=>{} );
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 %GlobalDataObject;
my %WeakSymbols;
my %Library_Needed= (
"1"=>{},
"2"=>{} );
my $DisabledMSVCUnmangling = undef;
# 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 %AddedInt;
my %RemovedInt;
my %AddedInt_Virt;
my %RemovedInt_Virt;
my %VirtualReplacement;
my %ChangedTypedef;
my %CompatRules;
my %IncompleteRules;
my %UnknownRules;
my %VTableChanged_M;
my %ExtendedSymbols;
my %ReturnedClass;
my %ParamClass;
my %SourceAlternative;
my %SourceAlternative_B;
my %SourceReplacement;
my $CurrentSymbol; # for debugging
#Report
my %TypeChanges;
#Speedup
my %TypeProblemsIndex;
# Calling Conventions
my %UseConv_Real = (
1=>{ "R"=>0, "P"=>0 },
2=>{ "R"=>0, "P"=>0 }
);
# ABI Dump
my %UsedDump;
# Filters
my %TargetLibs;
my %TargetHeaders;
# Format of objects
my $OStarget = $OSgroup;
my %TargetTools;
# 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;
my %TotalAffected;
# Reports
my $ContentID = 1;
my $ContentSpanStart = "<span class=\"section\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
my $ContentSpanStart_Affected = "<span class=\"sect_aff\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
my $ContentSpanStart_Info = "<span class=\"sect_info\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
my $ContentSpanEnd = "</span>\n";
my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
my $ContentDivEnd = "</div>\n";
my $Content_Counter = 0;
# Modes
my $JoinReport = 1;
my $DoubleReport = 0;
my %Severity_Val=(
"High"=>3,
"Medium"=>2,
"Low"=>1,
"Safe"=>-1
);
sub get_Modules()
{
my $TOOL_DIR = get_dirname($0);
if(not $TOOL_DIR)
{ # patch for MS Windows
$TOOL_DIR = ".";
}
my @SEARCH_DIRS = (
# tool's directory
abs_path($TOOL_DIR),
# relative path to modules
abs_path($TOOL_DIR)."/../share/abi-compliance-checker",
# install path
'MODULES_INSTALL_PATH'
);
foreach my $DIR (@SEARCH_DIRS)
{
if(not is_abs($DIR))
{ # relative path
$DIR = abs_path($TOOL_DIR)."/".$DIR;
}
if(-d $DIR."/modules") {
return $DIR."/modules";
}
}
exitStatus("Module_Error", "can't find modules");
}
my %LoadedModules = ();
sub loadModule($)
{
my $Name = $_[0];
if(defined $LoadedModules{$Name}) {
return;
}
my $Path = $MODULES_DIR."/Internals/$Name.pm";
if(not -f $Path) {
exitStatus("Module_Error", "can't access \'$Path\'");
}
require $Path;
$LoadedModules{$Name} = 1;
}
sub readModule($$)
{
my ($Module, $Name) = @_;
my $Path = $MODULES_DIR."/Internals/$Module/".$Name;
if(not -f $Path) {
exitStatus("Module_Error", "can't access \'$Path\'");
}
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($)
{
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})
{
if($CrossPrefix)
{ # user-defined prefix
$Path = search_Cmd($CrossPrefix."-".$Name);
}
}
if(not $Path and $BinUtils{$Name})
{
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(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)
{
if($Name=~/gcc/) {
next if(not check_gcc($CmdPath, "3"));
}
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=~/[\*\+\(\[\|]/)
{ # pattern
return ($Path, "Pattern");
}
elsif($Path=~/[\/\\]/)
{ # directory or relative path
return (path_format($Path, $OSgroup), "Path");
}
else {
return ($Path, "Name");
}
}
sub readDescriptor($$)
{
my ($LibVersion, $Content) = @_;
return if(not $LibVersion);
my $DName = $DumpAPI?"descriptor":"descriptor \"d$LibVersion\"";
if(not $Content) {
exitStatus("Error", "$DName is empty");
}
if($Content!~/\</) {
exitStatus("Error", "incorrect descriptor (see -d1 option)");
}
$Content=~s/\/\*(.|\n)+?\*\///g;
$Content=~s/<\!--(.|\n)+?-->//g;
$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 (<version> section)");
}
if($Content=~/{RELPATH}/)
{
if(my $RelDir = $RelativeDirectory{$LibVersion}) {
$Content =~ s/{RELPATH}/$RelDir/g;
}
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 (<headers> 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 (<libs> 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))
{
if(not -e $Path) {
exitStatus("Access_Error", "can't access \'$Path\'");
}
}
}
}
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;
}
}
}
sub parseTag(@)
{
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 $End = index(${$CodeRef}, "</$Tag>");
if($End!=-1)
{
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(substr($Content, 0, 1) ne "<") {
$Content = xmlSpecChars_R($Content);
}
return $Content;
}
}
return undef;
}
sub getInfo($)
{
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);
}
}
sub readTUDump($)
{
my $DumpPath = $_[0];
open(TU_DUMP, $DumpPath);
local $/ = undef;
my $Content = <TU_DUMP>;
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;
}
sub simplifyConstants()
{
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"};
}
}
}
}
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;
}
}
}
}
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;
}
}
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;
}
}
}
}
sub setTemplateParams_All()
{
foreach (keys(%{$LibInfo{$Version}{"info"}}))
{
if($LibInfo{$Version}{"info_type"}{$_} eq "template_decl") {
setTemplateParams($_);
}
}
}
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(my $TypeId = getTreeAttr_Type($_[0]))
{
if(my $IType = $LibInfo{$Version}{"info_type"}{$TypeId})
{
if($IType eq "record_type") {
$TemplateDecl{$Version}{$TypeId} = 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;
}
}
}
}
}
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);
}
}
}
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<QStyle::StateFlag>
# but QStyleOption.state is of type QFlags<QStyle::StateFlag> in the TU dump
# FIXME: check GCC versions
addMissedTypes_Pre();
}
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("$_");
}
}
# 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($ADD_TMPL_INSTANCES)
{
# 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
and $NBid ne $Tid)
{
%{$TypeInfo{$Version}{$Tid}{"Base"}{$NBid}} = %{$TypeInfo{$Version}{$Tid}{"Base"}{$Bid}};
delete($TypeInfo{$Version}{$Tid}{"Base"}{$Bid});
}
}
}
}
}
}
}
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});
foreach my $Key (sort keys(%{$Map}))
{
if(my $Val = $Map->{$Key})
{
$Attr->{"Name"}=~s/\b$Key\b/$Val/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;
}
}
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;
}
}
}
}
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
}
}
}
}
if(my $Id = getTypeIdByName($Attr->{"Name"}, $LibVersion)) {
return "$Id";
}
else
{
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
and $NBid ne $New)
{
%{$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);
}
}
}
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(defined $TypeInfo{$LibVersion}{$New}{"Return"}) {
$TypeInfo{$LibVersion}{$New}{"Return"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Return"}, $LibVersion);
}
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<Qt::DropAction>::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\E</) {
next;
}
if($TypedefName=~/\A\Q$TName\E/)
{ # QDateTimeEdit::Section and QDateTimeEdit::Sections::enum_type
next;
}
if(get_depth($TypedefName)==0 and get_depth($TName)!=0)
{ # std::_Vector_base and std::vector::_Base
next;
}
}
$AddTypes{$MissedInfo{"Tid"}} = \%MissedInfo;
# register typedef
$MissedTypedef{$Version}{$Tid}{"Tid"} = $MissedInfo{"Tid"};
$MissedTypedef{$Version}{$Tid}{"TDid"} = $MissedTDid;
$TName_Tid{$Version}{$TypedefName} = $MissedInfo{"Tid"};
}
# add missed & remove other
$TypeInfo{$Version} = \%AddTypes;
delete($Cache{"getTypeAttr"}{$Version});
}
sub addMissedTypes_Post()
{
foreach my $BaseId (keys(%{$MissedTypedef{$Version}}))
{
if(my $Tid = $MissedTypedef{$Version}{$BaseId}{"Tid"})
{
$TypeInfo{$Version}{$Tid}{"Size"} = $TypeInfo{$Version}{$BaseId}{"Size"};
if(my $TName = $TypeInfo{$Version}{$Tid}{"Name"}) {
$Typedef_BaseName{$Version}{$TName} = $TypeInfo{$Version}{$BaseId}{"Name"};
}
}
}
}
sub getTypeInfo($)
{
my $TypeId = $_[0];
%{$TypeInfo{$Version}{$TypeId}} = getTypeAttr($TypeId);
my $TName = $TypeInfo{$Version}{$TypeId}{"Name"};
if(not $TName) {
delete($TypeInfo{$Version}{$TypeId});
}
}
sub getArraySize($$)
{
my ($TypeId, $BaseName) = @_;
if(my $Size = getSize($TypeId))
{
my $Elems = $Size/$BYTE_SIZE;
while($BaseName=~s/\s*\[(\d+)\]//) {
$Elems/=$1;
}
if(my $BasicId = $TName_Tid{$Version}{$BaseName})
{
if(my $BasicSize = $TypeInfo{$Version}{$BasicId}{"Size"}) {
$Elems/=$BasicSize;
}
}
return $Elems;
}
return 0;
}
sub getTParams($$)
{
my ($TypeId, $Kind) = @_;
my @TmplParams = ();
my @Positions = sort {int($a)<=>int($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<P1>::type<P2> )
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))
{
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
{
$TypeAttr{"NameSpace"} = $NS;
$TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
if($TypeAttr{"NameSpace"}=~/\Astd(::|\Z)/
and $TypeAttr{"Name"}!~/>(::\w+)+\Z/)
{
if($BTAttr{"NameSpace"}
and $BTAttr{"NameSpace"}=~/\Astd(::|\Z)/ and $BTAttr{"Name"}=~/</)
{ # types like "std::fpos<__mbstate_t>" 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"}=~/</)
{
if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) {
$Typedef_Tr{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
}
}
}
}
($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeDeclId);
}
if(not $TypeAttr{"Size"})
{
if($TypeAttr{"Type"} eq "Pointer") {
$TypeAttr{"Size"} = $WORD_SIZE{$Version};
}
elsif($BTAttr{"Size"}) {
$TypeAttr{"Size"} = $BTAttr{"Size"};
}
}
if(my $Algn = getAlgn($TypeId)) {
$TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
}
$TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
if(not $TypeAttr{"Header"} and $BTAttr{"Header"}) {
$TypeAttr{"Header"} = $BTAttr{"Header"};
}
%{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
if($TypeAttr{"Name"} ne $BTAttr{"Name"})
{ # typedef to "class Class"
# should not be registered in TName_Tid
if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
$TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
}
}
return %TypeAttr;
}
}
sub getTreeVec($)
{
my %Vector = ();
if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
{
while($Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
{ # string length is N-1 because of the null terminator
$Vector{$1} = $2;
}
}
return \%Vector;
}
sub get_TemplateParam($$)
{
my ($Pos, $Type_Id) = @_;
return () if(not $Type_Id);
my $NodeType = $LibInfo{$Version}{"info_type"}{$Type_Id};
return () if(not $NodeType);
if($NodeType eq "integer_cst")
{ # int (1), unsigned (2u), char ('c' as 99), ...
my $CstTid = getTreeAttr_Type($Type_Id);
my %CstType = getTypeAttr($CstTid); # without recursion
my $Num = getNodeIntCst($Type_Id);
if(my $CstSuffix = $ConstantSuffix{$CstType{"Name"}}) {
return ($Num.$CstSuffix);
}
else {
return ("(".$CstType{"Name"}.")".$Num);
}
}
elsif($NodeType eq "string_cst") {
return (getNodeStrCst($Type_Id));
}
elsif($NodeType eq "tree_vec")
{
my $Vector = getTreeVec($Type_Id);
my @Params = ();
foreach my $P1 (sort {int($a)<=>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<typename _Tp, typename _Alloc = std::allocator<_Tp> >
# template<typename _Key, typename _Compare = std::less<_Key>
# template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
# template<typename _Ch_type, typename _Rx_traits = regex_traits<_Ch_type> >
# template<typename _CharT, typename _InIter = istreambuf_iterator<_CharT> >
# template<typename _CharT, typename _OutIter = ostreambuf_iterator<_CharT> >
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;
}
}
else {
last;
}
}
}
$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")
{
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]})
{
if(my $TDid = getTypeDeclId($_[0]))
{
if(getTypeId($TDid) eq $_[0]
and getNameByInfo($TDid))
{
if($Info=~/unql[ ]*:[ ]*\@(\d+) /) {
return $1;
}
}
}
}
}
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(defined $TemplateMap{$Version}{$ClassId})
{
foreach (keys(%{$TemplateMap{$Version}{$ClassId}})) {
$Map{$_} = $TemplateMap{$Version}{$ClassId}{$_};
}
}
}
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]=~/\<built\-in\>|\<internal\>|\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;