blob: a6716f834e8181df80e5b1271af7481e18cf2586 [file] [log] [blame]
Andrey Ponomarenkoab282102012-03-11 11:57:02 +04001#!/usr/bin/perl
2###########################################################################
3# ABI Compliance Checker (ACC) 1.96.8
4# A tool for checking backward binary compatibility of a C/C++ library API
5#
6# Copyright (C) 2009-2010 The Linux Foundation.
7# Copyright (C) 2009-2011 Institute for System Programming, RAS.
8# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
9# Copyright (C) 2011-2012 ROSA Laboratory.
10#
11# Written by Andrey Ponomarenko
12#
13# PLATFORMS
14# =========
15# Linux, FreeBSD, Mac OS X, Haiku, MS Windows, Symbian
16#
17# REQUIREMENTS
18# ============
19# Linux
20# - G++ (3.0-4.6.2, recommended >= 4.5)
21# - GNU Binutils (readelf, c++filt, objdump)
22# - Perl 5 (5.8-5.14)
23#
24# Mac OS X
25# - Xcode (gcc, otool, c++filt)
26#
27# MS Windows
28# - MinGW (3.0-4.6.2, recommended >= 4.5)
29# - MS Visual C++ (dumpbin, undname, cl)
30# - Active Perl 5 (5.8-5.14)
31# - Sigcheck v1.71 or newer
32# - Info-ZIP 3.0 (zip, unzip)
33# - Add gcc.exe path (C:\MinGW\bin\) to your system PATH variable
34# - Run vsvars32.bat (C:\Microsoft Visual Studio 9.0\Common7\Tools\)
35#
36# This program is free software: you can redistribute it and/or modify
37# it under the terms of the GNU General Public License or the GNU Lesser
38# General Public License as published by the Free Software Foundation.
39#
40# This program is distributed in the hope that it will be useful,
41# but WITHOUT ANY WARRANTY; without even the implied warranty of
42# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43# GNU General Public License for more details.
44#
45# You should have received a copy of the GNU General Public License
46# and the GNU Lesser General Public License along with this program.
47# If not, see <http://www.gnu.org/licenses/>.
48###########################################################################
49use Getopt::Long;
50Getopt::Long::Configure ("posix_default", "no_ignore_case");
51use File::Path qw(mkpath rmtree);
52use File::Temp qw(tempdir);
53use File::Copy qw(copy move);
54use Cwd qw(abs_path cwd);
55use Data::Dumper;
56use Config;
57
58my $TOOL_VERSION = "1.96.8";
59my $ABI_DUMP_VERSION = "2.10.1";
60my $OLDEST_SUPPORTED_VERSION = "1.18";
61my $XML_REPORT_VERSION = "1.0";
62my $OSgroup = get_OSgroup();
63my $ORIG_DIR = cwd();
64my $TMP_DIR = tempdir(CLEANUP=>1);
65
66# Internal modules
67my $MODULES_DIR = get_Modules();
68push(@INC, get_dirname($MODULES_DIR));
69# Rules DB
70my %RULES_PATH = (
71 "Binary" => $MODULES_DIR."/RulesBin.xml",
72 "Source" => $MODULES_DIR."/RulesSrc.xml");
73
74my ($Help, $ShowVersion, %Descriptor, $TargetLibraryName, $GenerateTemplate,
75$TestTool, $DumpAPI, $SymbolsListPath, $CheckHeadersOnly_Opt, $UseDumps,
76$CheckObjectsOnly_Opt, $AppPath, $StrictCompat, $DumpVersion, $ParamNamesPath,
77%RelativeDirectory, $TargetLibraryFName, $TestDump, $CheckImpl, $LoggingPath,
78%TargetVersion, $InfoMsg, $UseOldDumps, %UsedDump, $CrossGcc, %OutputLogPath,
79$OutputReportPath, $OutputDumpPath, $ShowRetVal, $SystemRoot_Opt, $DumpSystem,
80$CmpSystems, $TargetLibsPath, $Debug, $CrossPrefix, $UseStaticLibs, $NoStdInc,
81$TargetComponent_Opt, $TargetSysInfo, $TargetHeader, $ExtendedCheck, $Quiet,
82$SkipHeadersPath, $Cpp2003, $LogMode, $StdOut, $ListAffected, $ReportFormat,
83$UserLang, $TargetHeadersPath);
84
85my $CmdName = get_filename($0);
86my %OS_LibExt = (
87 "dynamic" => {
88 "default"=>"so",
89 "macos"=>"dylib",
90 "windows"=>"dll",
91 "symbian"=>"dso"
92 },
93 "static" => {
94 "default"=>"a",
95 "windows"=>"lib",
96 "symbian"=>"lib"
97 }
98);
99
100my %OS_Archive = (
101 "windows"=>"zip",
102 "default"=>"tar.gz"
103);
104
105my %ERROR_CODE = (
106 # Compatible verdict
107 "Compatible"=>0,
108 "Success"=>0,
109 # Incompatible verdict
110 "Incompatible"=>1,
111 # Undifferentiated error code
112 "Error"=>2,
113 # System command is not found
114 "Not_Found"=>3,
115 # Cannot access input files
116 "Access_Error"=>4,
117 # Cannot compile header files
118 "Cannot_Compile"=>5,
119 # Header compiled with errors
120 "Compile_Error"=>6,
121 # Invalid input ABI dump
122 "Invalid_Dump"=>7,
123 # Incompatible version of ABI dump
124 "Dump_Version"=>8,
125 # Cannot find a module
126 "Module_Error"=>9,
127 # Empty intersection between
128 # headers and shared objects
129 "Empty_Intersection"=>10,
130 # Empty set of symbols in headers
131 "Empty_Set"=>11
132);
133
134my %HomePage = (
135 "Wiki"=>"http://ispras.linux-foundation.org/index.php/ABI_compliance_checker",
136 "Dev"=>"http://forge.ispras.ru/projects/abi-compliance-checker"
137);
138
139my $ShortUsage = "ABI Compliance Checker (ACC) $TOOL_VERSION
140A tool for checking backward binary compatibility of a C/C++ library API
141Copyright (C) 2012 ROSA Laboratory
142License: GNU LGPL or GNU GPL
143
144Usage: $CmdName [options]
145Example: $CmdName -lib NAME -d1 OLD.xml -d2 NEW.xml
146
147OLD.xml and NEW.xml are XML-descriptors:
148
149 <version>
150 1.0
151 </version>
152
153 <headers>
154 /path/to/headers/
155 </headers>
156
157 <libs>
158 /path/to/libraries/
159 </libs>
160
161More info: $CmdName --help\n";
162
163if($#ARGV==-1) {
164 printMsg("INFO", $ShortUsage);
165 exit(0);
166}
167
168foreach (2 .. $#ARGV)
169{# correct comma separated options
170 if($ARGV[$_-1] eq ",") {
171 $ARGV[$_-2].=",".$ARGV[$_];
172 splice(@ARGV, $_-1, 2);
173 }
174 elsif($ARGV[$_-1]=~/,\Z/) {
175 $ARGV[$_-1].=$ARGV[$_];
176 splice(@ARGV, $_, 1);
177 }
178 elsif($ARGV[$_]=~/\A,/
179 and $ARGV[$_] ne ",") {
180 $ARGV[$_-1].=$ARGV[$_];
181 splice(@ARGV, $_, 1);
182 }
183}
184
185GetOptions("h|help!" => \$Help,
186 "i|info!" => \$InfoMsg,
187 "v|version!" => \$ShowVersion,
188 "dumpversion!" => \$DumpVersion,
189# general options
190 "l|lib|library=s" => \$TargetLibraryName,
191 "d1|old|o=s" => \$Descriptor{1}{"Path"},
192 "d2|new|n=s" => \$Descriptor{2}{"Path"},
193 "dump|dump-abi|dump_abi=s" => \$DumpAPI,
194 "old-dumps!" => \$UseOldDumps,
195# extra options
196 "d|descriptor-template!" => \$GenerateTemplate,
197 "app|application=s" => \$AppPath,
198 "static-libs!" => \$UseStaticLibs,
199 "cross-gcc=s" => \$CrossGcc,
200 "cross-prefix=s" => \$CrossPrefix,
201 "sysroot=s" => \$SystemRoot_Opt,
202 "v1|version1|vnum=s" => \$TargetVersion{1},
203 "v2|version2=s" => \$TargetVersion{2},
204 "s|strict!" => \$StrictCompat,
205 "symbols-list=s" => \$SymbolsListPath,
206 "skip-headers=s" => \$SkipHeadersPath,
207 "headers-only|headers_only!" => \$CheckHeadersOnly_Opt,
208 "objects-only!" => \$CheckObjectsOnly_Opt,
209 "check-impl|check-implementation!" => \$CheckImpl,
210 "show-retval!" => \$ShowRetVal,
211 "use-dumps!" => \$UseDumps,
212 "nostdinc!" => \$NoStdInc,
213 "dump-system=s" => \$DumpSystem,
214 "sysinfo=s" => \$TargetSysInfo,
215 "cmp-systems!" => \$CmpSystems,
216 "libs-list=s" => \$TargetLibsPath,
217 "headers-list=s" => \$TargetHeadersPath,
218 "header=s" => \$TargetHeader,
219 "ext|extended!" => \$ExtendedCheck,
220 "q|quiet!" => \$Quiet,
221 "stdout!" => \$StdOut,
222 "report-format=s" => \$ReportFormat,
223 "lang=s" => \$UserLang,
224# other options
225 "test!" => \$TestTool,
226 "test-dump!" => \$TestDump,
227 "debug!" => \$Debug,
228 "cpp-compatible!" => \$Cpp2003,
229 "p|params=s" => \$ParamNamesPath,
230 "relpath1|relpath=s" => \$RelativeDirectory{1},
231 "relpath2=s" => \$RelativeDirectory{2},
232 "dump-path=s" => \$OutputDumpPath,
233 "report-path=s" => \$OutputReportPath,
234 "log-path=s" => \$LoggingPath,
235 "log1-path=s" => \$OutputLogPath{1},
236 "log2-path=s" => \$OutputLogPath{2},
237 "logging-mode=s" => \$LogMode,
238 "list-affected!" => \$ListAffected,
239 "l-full|lib-full=s" => \$TargetLibraryFName,
240 "component=s" => \$TargetComponent_Opt
241) or ERR_MESSAGE();
242
243sub ERR_MESSAGE()
244{
245 printMsg("INFO", "\n".$ShortUsage);
246 exit($ERROR_CODE{"Error"});
247}
248
249my $LIB_TYPE = $UseStaticLibs?"static":"dynamic";
250my $SLIB_TYPE = $LIB_TYPE;
251if($OSgroup!~/macos|windows/ and $SLIB_TYPE eq "dynamic")
252{ # show as "shared" library
253 $SLIB_TYPE = "shared";
254}
255my $LIB_EXT = getLIB_EXT($OSgroup);
256my $AR_EXT = getAR_EXT($OSgroup);
257my $BYTE_SIZE = 8;
258my $COMMON_LOG_PATH = "logs/run.log";
259
260my $HelpMessage="
261NAME:
262 ABI Compliance Checker ($CmdName)
263 Check backward binary compatibility of a $SLIB_TYPE C/C++ library API
264
265DESCRIPTION:
266 ABI Compliance Checker (ACC) is a tool for checking backward binary
267 compatibility of a $SLIB_TYPE C/C++ library API. The tool checks header
268 files and $SLIB_TYPE libraries (*.$LIB_EXT) of old and new versions and
269 analyzes changes in Application Binary Interface (ABI=API+compiler ABI)
270 that may break binary compatibility: changes in calling stack, v-table
271 changes, removed symbols, etc. Binary incompatibility may result in
272 crashing or incorrect behavior of applications built with an old version
273 of a library if they run on a new one.
274
275 The tool is intended for library developers and OS maintainers who are
276 interested in ensuring binary compatibility, i.e. allow old applications
277 to run with new library versions without the need to recompile. Also it
278 can be used by ISVs for checking applications portability to new library
279 versions. Found issues can be taken into account when adapting the
280 application to a new library version.
281
282 This tool is free software: you can redistribute it and/or modify it
283 under the terms of the GNU LGPL or GNU GPL.
284
285USAGE:
286 $CmdName [options]
287
288EXAMPLE:
289 $CmdName -lib NAME -d1 OLD.xml -d2 NEW.xml
290
291 OLD.xml and NEW.xml are XML-descriptors:
292
293 <version>
294 1.0
295 </version>
296
297 <headers>
298 /path1/to/header(s)/
299 /path2/to/header(s)/
300 ...
301 </headers>
302
303 <libs>
304 /path1/to/library(ies)/
305 /path2/to/library(ies)/
306 ...
307 </libs>
308
309INFORMATION OPTIONS:
310 -h|-help
311 Print this help.
312
313 -i|-info
314 Print complete info.
315
316 -v|-version
317 Print version information.
318
319 -dumpversion
320 Print the tool version ($TOOL_VERSION) and don't do anything else.
321
322GENERAL OPTIONS:
323 -l|-lib|-library <name>
324 Library name (without version).
325 It affects only on the path and the title of the report.
326
327 -d1|-old|-o <path>
328 Descriptor of 1st (old) library version.
329 It may be one of the following:
330
331 1. XML-descriptor (VERSION.xml file):
332
333 <version>
334 1.0
335 </version>
336
337 <headers>
338 /path1/to/header(s)/
339 /path2/to/header(s)/
340 ...
341 </headers>
342
343 <libs>
344 /path1/to/library(ies)/
345 /path2/to/library(ies)/
346 ...
347 </libs>
348
349 ... (XML-descriptor template
350 can be generated by -d option)
351
352 2. ABI dump generated by -dump option
353 3. Directory with headers and/or $SLIB_TYPE libraries
354 4. Single header file
355 5. Single $SLIB_TYPE library
356 6. Comma separated list of headers and/or libraries
357
358 If you are using an 2-6 descriptor types then you should
359 specify version numbers with -v1 <num> and -v2 <num> options too.
360
361 For more information, please see:
362 http://ispras.linux-foundation.org/index.php/Library_Descriptor
363
364 -d2|-new|-n <path>
365 Descriptor of 2nd (new) library version.
366
367 -dump|-dump-abi <descriptor path(s)>
368 Dump library ABI to gzipped TXT format file. You can transfer it
369 anywhere and pass instead of the descriptor. Also it can be used
370 for debugging the tool. Compatible dump versions: ".majorVersion($ABI_DUMP_VERSION).".0<=V<=$ABI_DUMP_VERSION
371
372 -old-dumps
373 Enable support for old-version ABI dumps ($OLDEST_SUPPORTED_VERSION<=V<".majorVersion($ABI_DUMP_VERSION).".0).\n";
374
375sub HELP_MESSAGE() {
376 printMsg("INFO", $HelpMessage."
377MORE INFO:
378 $CmdName --info\n");
379}
380
381sub INFO_MESSAGE()
382{
383 printMsg("INFO", "$HelpMessage
384EXTRA OPTIONS:
385 -d|-descriptor-template
386 Create XML-descriptor template ./VERSION.xml
387
388 -app|-application <path>
389 This option allows to specify the application that should be checked
390 for portability to the new library version.
391
392 -static-libs
393 Check static libraries instead of the shared ones. The <libs> section
394 of the XML-descriptor should point to static libraries location.
395
396 -cross-gcc <path>
397 Path to the cross GCC compiler to use instead of the usual (host) GCC.
398
399 -cross-prefix <prefix>
400 GCC toolchain prefix.
401
402 -sysroot <dirpath>
403 Specify the alternative root directory. The tool will search for include
404 paths in the <dirpath>/usr/include and <dirpath>/usr/lib directories.
405
406 -v1|-version1 <num>
407 Specify 1st library version outside the descriptor. This option is needed
408 if you have prefered an alternative descriptor type (see -d1 option).
409
410 In general case you should specify it in the XML-descriptor:
411 <version>
412 VERSION
413 </version>
414
415 -v2|-version2 <num>
416 Specify 2nd library version outside the descriptor.
417
418 -s|-strict
419 Treat all compatibility warnings as problems. Add a number of \"Low\"
420 severity problems to the return value of the tool.
421
422 -headers-only
423 Check header files without $SLIB_TYPE libraries. It is easy to run, but may
424 provide a low quality compatibility report with false positives and
425 without detecting of added/removed symbols.
426
427 Alternatively you can write \"none\" word to the <libs> section
428 in the XML-descriptor:
429 <libs>
430 none
431 </libs>
432
433 -objects-only
434 Check $SLIB_TYPE libraries without header files. It is easy to run, but may
435 provide a low quality compatibility report with false positives and
436 without analysis of changes in parameters and data types.
437
438 Alternatively you can write \"none\" word to the <headers> section
439 in the XML-descriptor:
440 <headers>
441 none
442 </headers>
443
444 -check-impl|-check-implementation
445 Compare canonified disassembled binary code of $SLIB_TYPE libraries to
446 detect changes in the implementation. Add \'Problems with Implementation\'
447 section to the report.
448
449 -show-retval
450 Show the symbol's return type in the report.
451
452 -symbols-list <path>
453 This option allows to specify a file with a list of symbols (mangled
454 names in C++) that should be checked, other symbols will not be checked.
455
456 -skip-headers <path>
457 The file with the list of header files, that should not be checked.
458
459 -use-dumps
460 Make dumps for two versions of a library and compare dumps. This should
461 increase the performance of the tool and decrease the system memory usage.
462
463 -nostdinc
464 Do not search the GCC standard system directories for header files.
465
466 -dump-system <name> -sysroot <dirpath>
467 Find all the shared libraries and header files in <dirpath> directory,
468 create XML descriptors and make ABI dumps for each library. The result
469 set of ABI dumps can be compared (--cmp-systems) with the other one
470 created for other version of operating system in order to check them for
471 compatibility. Do not forget to specify -cross-gcc option if your target
472 system requires some specific version of GCC compiler (different from
473 the host GCC). The system ABI dump will be generated to:
474 sys_dumps/<name>/<arch>
475
476 -dump-system <descriptor.xml>
477 The same as the previous option but takes an XML descriptor of the target
478 system as input, where you should describe it:
479
480 /* Primary sections */
481
482 <name>
483 /* Name of the system */
484 </name>
485
486 <headers>
487 /* The list of paths to header files and/or
488 directories with header files, one per line */
489 </headers>
490
491 <libs>
492 /* The list of paths to shared libraries and/or
493 directories with shared libraries, one per line */
494 </libs>
495
496 /* Optional sections */
497
498 <search_headers>
499 /* List of directories to be searched
500 for header files to automatically
501 generate include paths, one per line */
502 </search_headers>
503
504 <search_libs>
505 /* List of directories to be searched
506 for shared libraries to resolve
507 dependencies, one per line */
508 </search_libs>
509
510 <tools>
511 /* List of directories with tools used
512 for analysis (GCC toolchain), one per line */
513 </tools>
514
515 <cross_prefix>
516 /* GCC toolchain prefix.
517 Examples:
518 arm-linux-gnueabi
519 arm-none-symbianelf */
520 </cross_prefix>
521
522 <gcc_options>
523 /* Additional GCC options, one per line */
524 </gcc_options>
525
526 -sysinfo <dir>
527 This option may be used with -dump-system to dump ABI of operating
528 systems and configure the dumping process.
529 Default:
530 modules/Targets/{unix, symbian, windows}
531
532 -cmp-systems -d1 sys_dumps/<name1>/<arch> -d2 sys_dumps/<name2>/<arch>
533 Compare two system ABI dumps. Create compatibility reports for each
534 library and the common HTML report including the summary of test
535 results for all checked libraries. Report will be generated to:
536 sys_compat_reports/<name1>_to_<name2>/<arch>
537
538 -libs-list <path>
539 The file with a list of libraries, that should be dumped by
540 the -dump-system option or should be checked by the -cmp-systems option.
541
542 -header <name>
543 Check/Dump ABI of this header only.
544
545 -headers-list <path>
546 The file with a list of headers, that should be checked/dumped.
547
548 -ext|-extended
549 If your library A is supposed to be used by other library B and you
550 want to control the ABI of B, then you should enable this option. The
551 tool will check for changes in all data types, even if they are not
552 used by any function in the library A. Such data types are not part
553 of the A library ABI, but may be a part of the ABI of the B library.
554
555 The short scheme is:
556 app C (broken) -> lib B (broken ABI) -> lib A (stable ABI)
557
558 -q|-quiet
559 Print all messages to the file instead of stdout and stderr.
560 Default path (can be changed by -log-path option):
561 $COMMON_LOG_PATH
562
563 -stdout
564 Print analysis results (compatibility reports and ABI dumps) to stdout
565 instead of creating a file. This would allow piping data to other programs.
566
567 -report-format <fmt>
568 Change format of compatibility report.
569 Formats:
570 htm - HTML format (default)
571 xml - XML format
572
573 -lang <lang>
574 Set library language (C or C++). You can use this option if the tool
575 cannot auto-detect a language. This option may be useful for checking
576 C-library headers (--lang=C) in --headers-only or --extended modes.
577
578OTHER OPTIONS:
579 -test
580 Run internal tests. Create two binary incompatible versions of a sample
581 library and run the tool to check them for compatibility. This option
582 allows to check if the tool works correctly in the current environment.
583
584 -test-dump
585 Test ability to create, read and compare ABI dumps.
586
587 -debug
588 Debugging mode. Print debug info on the screen. Save intermediate
589 analysis stages in the debug directory:
590 debug/<library>/<version>/
591
592 -cpp-compatible
593 If your header file is written in C language and can be compiled by
594 the C++ compiler (i.e. doesn't contain C++-keywords and other bad
595 things), then you can tell ACC about this and speedup the analysis.
596
597 -p|-params <path>
598 Path to file with the function parameter names. It can be used
599 for improving report view if the library header files have no
600 parameter names. File format:
601
602 func1;param1;param2;param3 ...
603 func2;param1;param2;param3 ...
604 ...
605
606 -relpath <path>
607 Replace {RELPATH} macros to <path> in the XML-descriptor used
608 for dumping the library ABI (see -dump option).
609
610 -relpath1 <path>
611 Replace {RELPATH} macros to <path> in the 1st XML-descriptor (-d1).
612
613 -relpath2 <path>
614 Replace {RELPATH} macros to <path> in the 2nd XML-descriptor (-d2).
615
616 -dump-path <path>
617 Specify a file path (*.abi.$AR_EXT) where to generate an ABI dump.
618 Default:
619 abi_dumps/<library>/<library>_<version>.abi.$AR_EXT
620
621 -report-path <path>
622 Compatibility report path.
623 Default:
624 compat_reports/<library name>/<v1>_to_<v2>/abi_compat_report.html
625
626 -log-path <path>
627 Log path for all messages.
628 Default:
629 logs/<library>/<version>/log.txt
630
631 -log1-path <path>
632 Log path for 1st version of a library.
633 Default:
634 logs/<library name>/<v1>/log.txt
635
636 -log2-path <path>
637 Log path for 2nd version of a library.
638 Default:
639 logs/<library name>/<v2>/log.txt
640
641 -logging-mode <mode>
642 Change logging mode.
643 Modes:
644 w - overwrite old logs (default)
645 a - append old logs
646 n - do not write any logs
647
648 -list-affected
649 Generate file with the list of incompatible
650 symbols beside the HTML compatibility report.
651 Use 'c++filt \@file' command from GNU binutils
652 to unmangle C++ symbols in the generated file.
653 Default name:
654 abi_affected.txt
655
656 -component <name>
657 The component name in the title and summary of the HTML report.
658 Default:
659 library
660
661 -l-full|-lib-full <name>
662 Change library name in the report title to <name>. By default
663 will be displayed a name specified by -l option.
664
665REPORT:
666 Compatibility report will be generated to:
667 compat_reports/<library name>/<v1>_to_<v2>/abi_compat_report.html
668
669 Log will be generated to:
670 logs/<library name>/<v1>/log.txt
671 logs/<library name>/<v2>/log.txt
672
673EXIT CODES:
674 0 - Compatible. The tool has run without any errors.
675 non-zero - Incompatible or the tool has run with errors.
676
677REPORT BUGS TO:
678 abi-compliance-checker\@linuxtesting.org
679 Andrey Ponomarenko <aponomarenko\@mandriva.org>
680
681MORE INFORMATION:
682 ".$HomePage{"Wiki"}."
683 ".$HomePage{"Dev"}."\n");
684}
685
686my $DescriptorTemplate = "
687<?xml version=\"1.0\" encoding=\"utf-8\"?>
688<descriptor>
689
690/* Primary sections */
691
692<version>
693 /* Version of the library */
694</version>
695
696<headers>
697 /* The list of paths to header files and/or
698 directories with header files, one per line */
699</headers>
700
701<libs>
702 /* The list of paths to shared libraries (*.$LIB_EXT) and/or
703 directories with shared libraries, one per line */
704</libs>
705
706/* Optional sections */
707
708<include_paths>
709 /* The list of include paths that will be provided
710 to GCC to compile library headers, one per line.
711 NOTE: If you define this section then the tool
712 will not automatically generate include paths */
713</include_paths>
714
715<add_include_paths>
716 /* The list of include paths that will be added
717 to the automatically generated include paths, one per line */
718</add_include_paths>
719
720<skip_include_paths>
721 /* The list of include paths that will be removed from the
722 list of automatically generated include paths, one per line */
723</skip_include_paths>
724
725<gcc_options>
726 /* Additional GCC options, one per line */
727</gcc_options>
728
729<include_preamble>
730 /* The list of header files that will be
731 included before other headers, one per line.
732 Examples:
733 1) tree.h for libxml2
734 2) ft2build.h for freetype2 */
735</include_preamble>
736
737<defines>
738 /* The list of defines that will be added at the
739 headers compiling stage, one per line:
740 #define A B
741 #define C D */
742</defines>
743
744<skip_types>
745 /* The list of data types, that
746 should not be checked, one per line */
747</skip_types>
748
749<skip_symbols>
750 /* The list of functions (mangled/symbol names in C++),
751 that should not be checked, one per line */
752</skip_symbols>
753
754<skip_namespaces>
755 /* The list of C++ namespaces, that
756 should not be checked, one per line */
757</skip_namespaces>
758
759<skip_constants>
760 /* The list of constants that should
761 not be checked, one name per line */
762</skip_constants>
763
764<skip_headers>
765 /* The list of header files and/or directories
766 with header files that should not be checked, one per line */
767</skip_headers>
768
769<skip_libs>
770 /* The list of shared libraries and/or directories
771 with shared libraries that should not be checked, one per line */
772</skip_libs>
773
774<skip_including>
775 /* The list of header files, that cannot be included
776 directly (or non-self compiled ones), one per line */
777</skip_including>
778
779<search_headers>
780 /* List of directories to be searched
781 for header files to automatically
782 generate include paths, one per line. */
783</search_headers>
784
785<search_libs>
786 /* List of directories to be searched
787 for shared librariess to resolve
788 dependencies, one per line */
789</search_libs>
790
791<tools>
792 /* List of directories with tools used
793 for analysis (GCC toolchain), one per line */
794</tools>
795
796<cross_prefix>
797 /* GCC toolchain prefix.
798 Examples:
799 arm-linux-gnueabi
800 arm-none-symbianelf */
801</cross_prefix>
802
803</descriptor>";
804
805my %Operator_Indication = (
806 "not" => "~",
807 "assign" => "=",
808 "andassign" => "&=",
809 "orassign" => "|=",
810 "xorassign" => "^=",
811 "or" => "|",
812 "xor" => "^",
813 "addr" => "&",
814 "and" => "&",
815 "lnot" => "!",
816 "eq" => "==",
817 "ne" => "!=",
818 "lt" => "<",
819 "lshift" => "<<",
820 "lshiftassign" => "<<=",
821 "rshiftassign" => ">>=",
822 "call" => "()",
823 "mod" => "%",
824 "modassign" => "%=",
825 "subs" => "[]",
826 "land" => "&&",
827 "lor" => "||",
828 "rshift" => ">>",
829 "ref" => "->",
830 "le" => "<=",
831 "deref" => "*",
832 "mult" => "*",
833 "preinc" => "++",
834 "delete" => " delete",
835 "vecnew" => " new[]",
836 "vecdelete" => " delete[]",
837 "predec" => "--",
838 "postinc" => "++",
839 "postdec" => "--",
840 "plusassign" => "+=",
841 "plus" => "+",
842 "minus" => "-",
843 "minusassign" => "-=",
844 "gt" => ">",
845 "ge" => ">=",
846 "new" => " new",
847 "multassign" => "*=",
848 "divassign" => "/=",
849 "div" => "/",
850 "neg" => "-",
851 "pos" => "+",
852 "memref" => "->*",
853 "compound" => "," );
854
855my %CppKeywords_C = map {$_=>1} (
856 # C++ 2003 keywords
857 "public",
858 "protected",
859 "private",
860 "default",
861 "template",
862 "new",
863 #"asm",
864 "dynamic_cast",
865 "auto",
866 "try",
867 "namespace",
868 "typename",
869 "using",
870 "reinterpret_cast",
871 "friend",
872 "class",
873 "virtual",
874 "const_cast",
875 "mutable",
876 "static_cast",
877 "export",
878 # C++0x keywords
879 "noexcept",
880 "nullptr",
881 "constexpr",
882 "static_assert",
883 "explicit",
884 # cannot be used as a macro name
885 # as it is an operator in C++
886 "and",
887 #"and_eq",
888 "not",
889 #"not_eq",
890 "or"
891 #"or_eq",
892 #"bitand",
893 #"bitor",
894 #"xor",
895 #"xor_eq",
896 #"compl"
897);
898
899my %CppKeywords_F = map {$_=>1} (
900 "delete",
901 "catch",
902 "alignof",
903 "thread_local",
904 "decltype",
905 "typeid"
906);
907
908my %CppKeywords_O = map {$_=>1} (
909 "bool",
910 "register",
911 "inline",
912 "operator"
913);
914
915my %CppKeywords_A = map {$_=>1} (
916 "this",
917 "throw"
918);
919
920foreach (keys(%CppKeywords_C),
921keys(%CppKeywords_F),
922keys(%CppKeywords_O)) {
923 $CppKeywords_A{$_}=1;
924}
925
926# Header file extensions as described by gcc
927my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+";
928
929my %IntrinsicMangling = (
930 "void" => "v",
931 "bool" => "b",
932 "wchar_t" => "w",
933 "char" => "c",
934 "signed char" => "a",
935 "unsigned char" => "h",
936 "short" => "s",
937 "unsigned short" => "t",
938 "int" => "i",
939 "unsigned int" => "j",
940 "long" => "l",
941 "unsigned long" => "m",
942 "long long" => "x",
943 "__int64" => "x",
944 "unsigned long long" => "y",
945 "__int128" => "n",
946 "unsigned __int128" => "o",
947 "float" => "f",
948 "double" => "d",
949 "long double" => "e",
950 "__float80" => "e",
951 "__float128" => "g",
952 "..." => "z"
953);
954
955my %StdcxxMangling = (
956 "3std"=>"St",
957 "3std9allocator"=>"Sa",
958 "3std12basic_string"=>"Sb",
959 "3std12basic_stringIcE"=>"Ss",
960 "3std13basic_istreamIcE"=>"Si",
961 "3std13basic_ostreamIcE"=>"So",
962 "3std14basic_iostreamIcE"=>"Sd"
963);
964
965my %ConstantSuffix = (
966 "unsigned int"=>"u",
967 "long"=>"l",
968 "unsigned long"=>"ul",
969 "long long"=>"ll",
970 "unsigned long long"=>"ull"
971);
972
973my %ConstantSuffixR =
974reverse(%ConstantSuffix);
975
976my %OperatorMangling = (
977 "~" => "co",
978 "=" => "aS",
979 "|" => "or",
980 "^" => "eo",
981 "&" => "an",#ad (addr)
982 "==" => "eq",
983 "!" => "nt",
984 "!=" => "ne",
985 "<" => "lt",
986 "<=" => "le",
987 "<<" => "ls",
988 "<<=" => "lS",
989 ">" => "gt",
990 ">=" => "ge",
991 ">>" => "rs",
992 ">>=" => "rS",
993 "()" => "cl",
994 "%" => "rm",
995 "[]" => "ix",
996 "&&" => "aa",
997 "||" => "oo",
998 "*" => "ml",#de (deref)
999 "++" => "pp",#
1000 "--" => "mm",#
1001 "new" => "nw",
1002 "delete" => "dl",
1003 "new[]" => "na",
1004 "delete[]" => "da",
1005 "+=" => "pL",
1006 "+" => "pl",#ps (pos)
1007 "-" => "mi",#ng (neg)
1008 "-=" => "mI",
1009 "*=" => "mL",
1010 "/=" => "dV",
1011 "&=" => "aN",
1012 "|=" => "oR",
1013 "%=" => "rM",
1014 "^=" => "eO",
1015 "/" => "dv",
1016 "->*" => "pm",
1017 "->" => "pt",#rf (ref)
1018 "," => "cm",
1019 "?" => "qu",
1020 "." => "dt",
1021 "sizeof"=> "sz"#st
1022);
1023
1024my %GlibcHeader = map {$_=>1} (
1025 "aliases.h",
1026 "argp.h",
1027 "argz.h",
1028 "assert.h",
1029 "cpio.h",
1030 "ctype.h",
1031 "dirent.h",
1032 "envz.h",
1033 "errno.h",
1034 "error.h",
1035 "execinfo.h",
1036 "fcntl.h",
1037 "fstab.h",
1038 "ftw.h",
1039 "glob.h",
1040 "grp.h",
1041 "iconv.h",
1042 "ifaddrs.h",
1043 "inttypes.h",
1044 "langinfo.h",
1045 "limits.h",
1046 "link.h",
1047 "locale.h",
1048 "malloc.h",
1049 "math.h",
1050 "mntent.h",
1051 "monetary.h",
1052 "nl_types.h",
1053 "obstack.h",
1054 "printf.h",
1055 "pwd.h",
1056 "regex.h",
1057 "sched.h",
1058 "search.h",
1059 "setjmp.h",
1060 "shadow.h",
1061 "signal.h",
1062 "spawn.h",
1063 "stdarg.h",
1064 "stdint.h",
1065 "stdio.h",
1066 "stdlib.h",
1067 "string.h",
1068 "tar.h",
1069 "termios.h",
1070 "time.h",
1071 "ulimit.h",
1072 "unistd.h",
1073 "utime.h",
1074 "wchar.h",
1075 "wctype.h",
1076 "wordexp.h" );
1077
1078my %GlibcDir = map {$_=>1} (
1079 "arpa",
1080 "bits",
1081 "gnu",
1082 "netinet",
1083 "net",
1084 "nfs",
1085 "rpc",
1086 "sys",
1087 "linux" );
1088
1089my %LocalIncludes = map {$_=>1} (
1090 "/usr/local/include",
1091 "/usr/local" );
1092
1093my %OS_AddPath=(
1094# These paths are needed if the tool cannot detect them automatically
1095 "macos"=>{
1096 "include"=>{
1097 "/Library"=>1,
1098 "/Developer/usr/include"=>1
1099 },
1100 "lib"=>{
1101 "/Library"=>1,
1102 "/Developer/usr/lib"=>1
1103 },
1104 "bin"=>{
1105 "/Developer/usr/bin"=>1
1106 }
1107 },
1108 "beos"=>{
1109 # Haiku has GCC 2.95.3 by default
1110 # try to find GCC>=3.0 in /boot/develop/abi
1111 "include"=>{
1112 "/boot/common"=>1,
1113 "/boot/develop"=>1},
1114 "lib"=>{
1115 "/boot/common/lib"=>1,
1116 "/boot/system/lib"=>1,
1117 "/boot/apps"=>1},
1118 "bin"=>{
1119 "/boot/common/bin"=>1,
1120 "/boot/system/bin"=>1,
1121 "/boot/develop/abi"=>1
1122 }
1123}
1124);
1125
1126my %Slash_Type=(
1127 "default"=>"/",
1128 "windows"=>"\\"
1129);
1130
1131my $SLASH = $Slash_Type{$OSgroup}?$Slash_Type{$OSgroup}:$Slash_Type{"default"};
1132
1133# Global Variables
1134my %COMMON_LANGUAGE=(
1135 1 => "C",
1136 2 => "C" );
1137
1138my $MAX_COMMAND_LINE_ARGUMENTS = 4096;
1139my (%WORD_SIZE, %CPU_ARCH, %GCC_VERSION);
1140
1141my %LIB_ARCH;
1142
1143my $STDCXX_TESTING = 0;
1144my $GLIBC_TESTING = 0;
1145
1146my $CheckHeadersOnly = $CheckHeadersOnly_Opt;
1147my $CheckObjectsOnly = $CheckObjectsOnly_Opt;
1148my $TargetComponent = lc($TargetComponent_Opt);
1149if(not $TargetComponent)
1150{ # default: library
1151 # other components: header, system, ...
1152 $TargetComponent = "library";
1153}
1154
1155my $SystemRoot;
1156
1157my $MAIN_CPP_DIR;
1158my %RESULT=(
1159 "Problems"=>0,
1160 "Warnings"=>0,
1161 "Affected"=>0
1162);
1163my %LOG_PATH;
1164my %DEBUG_PATH;
1165my %Cache;
1166my %LibInfo;
1167my $COMPILE_ERRORS = 0;
1168my %CompilerOptions;
1169my %CheckedDyLib;
1170my $TargetLibraryShortName = parse_libname($TargetLibraryName, "shortest", $OSgroup);
1171
1172# Constants (#defines)
1173my %Constants;
1174my %SkipConstants;
1175
1176# Types
1177my %TypeInfo;
1178my %TemplateInstance_Func;
1179my %TemplateInstance;
1180my %SkipTypes = (
1181 "1"=>{},
1182 "2"=>{} );
1183my %Tid_TDid = (
1184 "1"=>{},
1185 "2"=>{} );
1186my %CheckedTypes;
1187my %TName_Tid;
1188my %EnumMembName_Id;
1189my %NestedNameSpaces = (
1190 "1"=>{},
1191 "2"=>{} );
1192my %UsedType;
1193my %VirtualTable;
1194my %VirtualTable_Full;
1195my %ClassVTable;
1196my %ClassVTable_Content;
1197my %VTableClass;
1198my %AllocableClass;
1199my %ClassMethods;
1200my %ClassToId;
1201my %Class_SubClasses;
1202my %OverriddenMethods;
1203my $MAX_TID;
1204
1205# Typedefs
1206my %Typedef_BaseName;
1207my %Typedef_Tr;
1208my %Typedef_Eq;
1209my %StdCxxTypedef;
1210my %MissedTypedef;
1211
1212# Symbols
1213my %SymbolInfo;
1214my %tr_name;
1215my %mangled_name_gcc;
1216my %mangled_name;
1217my %SkipSymbols = (
1218 "1"=>{},
1219 "2"=>{} );
1220my %SkipNameSpaces = (
1221 "1"=>{},
1222 "2"=>{} );
1223my %SymbolsList;
1224my %SymbolsList_App;
1225my %CheckedSymbols;
1226my %GeneratedSymbols;
1227my %DepSymbols = (
1228 "1"=>{},
1229 "2"=>{} );
1230my %MangledNames;
1231my %AddIntParams;
1232my %Interface_Impl;
1233
1234# Headers
1235my %Include_Preamble;
1236my %Registered_Headers;
1237my %HeaderName_Paths;
1238my %Header_Dependency;
1239my %Include_Neighbors;
1240my %Include_Paths;
1241my %INC_PATH_AUTODETECT = (
1242 "1"=>1,
1243 "2"=>1 );
1244my %Add_Include_Paths;
1245my %Skip_Include_Paths;
1246my %RegisteredDirs;
1247my %RegisteredDeps;
1248my %Header_ErrorRedirect;
1249my %Header_Includes;
1250my %Header_ShouldNotBeUsed;
1251my %RecursiveIncludes;
1252my %Header_Include_Prefix;
1253my %SkipHeaders;
1254my %SkipHeadersList=(
1255 "1"=>{},
1256 "2"=>{} );
1257my %SkipLibs;
1258my %Include_Order;
1259my %TUnit_NameSpaces;
1260
1261my %C99Mode = (
1262 "1"=>0,
1263 "2"=>0 );
1264my %AutoPreambleMode = (
1265 "1"=>0,
1266 "2"=>0 );
1267my %MinGWMode = (
1268 "1"=>0,
1269 "2"=>0 );
1270
1271# Shared Objects
1272my %DyLib_DefaultPath;
1273my %InputObject_Paths;
1274my %RegisteredObjDirs;
1275
1276# System Objects
1277my %SystemObjects;
1278my %DefaultLibPaths;
1279
1280# System Headers
1281my %SystemHeaders;
1282my %DefaultCppPaths;
1283my %DefaultGccPaths;
1284my %DefaultIncPaths;
1285my %DefaultCppHeader;
1286my %DefaultGccHeader;
1287my %UserIncPath;
1288
1289# Merging
1290my %CompleteSignature;
1291my %Symbol_Library;
1292my %Library_Symbol = (
1293 "1"=>{},
1294 "2"=>{} );
1295my $Version;
1296my %AddedInt;
1297my %RemovedInt;
1298my %AddedInt_Virt;
1299my %RemovedInt_Virt;
1300my %VirtualReplacement;
1301my %ChangedTypedef;
1302my %CompatRules;
1303my %IncompleteRules;
1304my %UnknownRules;
1305my %VTableChanged;
1306my %ExtendedFuncs;
1307my %ReturnedClass;
1308my %ParamClass;
1309
1310# OS Compliance
1311my %TargetLibs;
1312my %TargetHeaders;
1313
1314# OS Specifics
1315my $OStarget = $OSgroup;
1316my %TargetTools;
1317
1318# Compliance Report
1319my %Type_MaxPriority;
1320
1321# Recursion locks
1322my @RecurLib;
1323my @RecurSymlink;
1324my @RecurTypes;
1325my @RecurInclude;
1326my @RecurConstant;
1327
1328# System
1329my %SystemPaths;
1330my %DefaultBinPaths;
1331my $GCC_PATH;
1332
1333# Symbols versioning
1334my %SymVer = (
1335 "1"=>{},
1336 "2"=>{} );
1337
1338# Problem descriptions
1339my %CompatProblems;
1340my %ProblemsWithConstants;
1341my %ImplProblems;
1342my %TotalAffected;
1343
1344# Rerorts
1345my $ContentID = 1;
1346my $ContentSpanStart = "<span class=\"section\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1347my $ContentSpanStart_Affected = "<span class=\"section_affected\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1348my $ContentSpanStart_Info = "<span class=\"section_info\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1349my $ContentSpanEnd = "</span>\n";
1350my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
1351my $ContentDivEnd = "</div>\n";
1352my $Content_Counter = 0;
1353
1354my $JScripts = "
1355<script type=\"text/javascript\" language=\"JavaScript\">
1356<!--
1357function showContent(header, id) {
1358 e = document.getElementById(id);
1359 if(e.style.display == 'none')
1360 {
1361 e.style.display = 'block';
1362 e.style.visibility = 'visible';
1363 header.innerHTML = header.innerHTML.replace(/\\\[[^0-9 ]\\\]/gi,\"[&minus;]\");
1364 }
1365 else
1366 {
1367 e.style.display = 'none';
1368 e.style.visibility = 'hidden';
1369 header.innerHTML = header.innerHTML.replace(/\\\[[^0-9 ]\\\]/gi,\"[+]\");
1370 }
1371}
1372-->
1373</script>";
1374
1375sub get_Modules()
1376{
1377 my $TOOL_DIR = get_dirname($0);
1378 if(not $TOOL_DIR)
1379 { # patch for MS Windows
1380 $TOOL_DIR = ".";
1381 }
1382 my @SEARCH_DIRS = (
1383 # tool's directory
1384 abs_path($TOOL_DIR),
1385 # relative path to modules
1386 abs_path($TOOL_DIR)."/../share/abi-compliance-checker",
1387 # system directory
1388 "ACC_MODULES_INSTALL_PATH"
1389 );
1390 foreach my $DIR (@SEARCH_DIRS)
1391 {
1392 if(not is_abs($DIR))
1393 { # relative path
1394 $DIR = abs_path($TOOL_DIR)."/".$DIR;
1395 }
1396 if(-d $DIR."/modules") {
1397 return $DIR."/modules";
1398 }
1399 }
1400 exitStatus("Module_Error", "can't find modules");
1401}
1402
1403sub loadModule($)
1404{
1405 my $Name = $_[0];
1406 my $Path = $MODULES_DIR."/Internals/$Name.pm";
1407 if(not -f $Path) {
1408 exitStatus("Module_Error", "can't access \'$Path\'");
1409 }
1410 require $Path;
1411}
1412
1413sub numToStr($)
1414{
1415 my $Number = int($_[0]);
1416 if($Number>3) {
1417 return $Number."th";
1418 }
1419 elsif($Number==1) {
1420 return "1st";
1421 }
1422 elsif($Number==2) {
1423 return "2nd";
1424 }
1425 elsif($Number==3) {
1426 return "3rd";
1427 }
1428 else {
1429 return $Number;
1430 }
1431}
1432
1433sub search_Tools($)
1434{
1435 my $Name = $_[0];
1436 return "" if(not $Name);
1437 if(my @Paths = keys(%TargetTools))
1438 {
1439 foreach my $Path (@Paths)
1440 {
1441 if(-f joinPath($Path, $Name)) {
1442 return joinPath($Path, $Name);
1443 }
1444 if($CrossPrefix)
1445 { # user-defined prefix (arm-none-symbianelf, ...)
1446 my $Candidate = joinPath($Path, $CrossPrefix."-".$Name);
1447 if(-f $Candidate) {
1448 return $Candidate;
1449 }
1450 }
1451 }
1452 }
1453 else {
1454 return "";
1455 }
1456}
1457
1458sub synch_Cmd($)
1459{
1460 my $Name = $_[0];
1461 if(not $GCC_PATH)
1462 { # GCC was not found yet
1463 return "";
1464 }
1465 my $Candidate = $GCC_PATH;
1466 if($Candidate=~s/(\W|\A)gcc(|\.\w+)\Z/$1$Name$2/) {
1467 return $Candidate;
1468 }
1469 return "";
1470}
1471
1472sub get_CmdPath($)
1473{
1474 my $Name = $_[0];
1475 return "" if(not $Name);
1476 if(defined $Cache{"get_CmdPath"}{$Name}) {
1477 return $Cache{"get_CmdPath"}{$Name};
1478 }
1479 my %BinUtils = map {$_=>1} (
1480 "c++filt",
1481 "objdump",
1482 "readelf"
1483 );
1484 if($BinUtils{$Name}) {
1485 if(my $Dir = get_dirname($GCC_PATH)) {
1486 $TargetTools{$Dir}=1;
1487 }
1488 }
1489 my $Path = search_Tools($Name);
1490 if(not $Path and $OSgroup eq "windows") {
1491 $Path = search_Tools($Name.".exe");
1492 }
1493 if(not $Path and $BinUtils{$Name})
1494 {
1495 if($CrossPrefix)
1496 { # user-defined prefix
1497 $Path = search_Cmd($CrossPrefix."-".$Name);
1498 }
1499 }
1500 if(not $Path and $BinUtils{$Name})
1501 {
1502 if(my $Candidate = synch_Cmd($Name))
1503 { # synch with GCC
1504 if($Candidate=~/[\/\\]/)
1505 {# command path
1506 if(-f $Candidate) {
1507 $Path = $Candidate;
1508 }
1509 }
1510 elsif($Candidate = search_Cmd($Candidate))
1511 {# command name
1512 $Path = $Candidate;
1513 }
1514 }
1515 }
1516 if(not $Path) {
1517 $Path = search_Cmd($Name);
1518 }
1519 if(not $Path and $OSgroup eq "windows")
1520 {# search for *.exe file
1521 $Path=search_Cmd($Name.".exe");
1522 }
1523 if($Path=~/\s/) {
1524 $Path = "\"".$Path."\"";
1525 }
1526 return ($Cache{"get_CmdPath"}{$Name}=$Path);
1527}
1528
1529sub search_Cmd($)
1530{
1531 my $Name = $_[0];
1532 return "" if(not $Name);
1533 if(defined $Cache{"search_Cmd"}{$Name}) {
1534 return $Cache{"search_Cmd"}{$Name};
1535 }
1536 if(my $DefaultPath = get_CmdPath_Default($Name)) {
1537 return ($Cache{"search_Cmd"}{$Name} = $DefaultPath);
1538 }
1539 foreach my $Path (sort {length($a)<=>length($b)} keys(%{$SystemPaths{"bin"}}))
1540 {
1541 my $CmdPath = joinPath($Path,$Name);
1542 if(-f $CmdPath)
1543 {
1544 if($Name=~/gcc/) {
1545 next if(not check_gcc_version($CmdPath, "3"));
1546 }
1547 return ($Cache{"search_Cmd"}{$Name} = $CmdPath);
1548 }
1549 }
1550 return ($Cache{"search_Cmd"}{$Name} = "");
1551}
1552
1553sub get_CmdPath_Default($)
1554{ # search in PATH
1555 my $Name = $_[0];
1556 return "" if(not $Name);
1557 if(defined $Cache{"get_CmdPath_Default"}{$Name}) {
1558 return $Cache{"get_CmdPath_Default"}{$Name};
1559 }
1560 if($Name=~/find/)
1561 { # special case: search for "find" utility
1562 if(`find . -maxdepth 0 2>$TMP_DIR/null`) {
1563 return ($Cache{"get_CmdPath_Default"}{$Name} = "find");
1564 }
1565 }
1566 elsif($Name=~/gcc/) {
1567 return check_gcc_version($Name, "3");
1568 }
1569 if(check_command($Name)) {
1570 return ($Cache{"get_CmdPath_Default"}{$Name} = $Name);
1571 }
1572 if($OSgroup eq "windows"
1573 and `$Name /? 2>$TMP_DIR/null`) {
1574 return ($Cache{"get_CmdPath_Default"}{$Name} = $Name);
1575 }
1576 if($Name!~/which/)
1577 {
1578 my $WhichCmd = get_CmdPath("which");
1579 if($WhichCmd and `$WhichCmd $Name 2>$TMP_DIR/null`) {
1580 return ($Cache{"get_CmdPath_Default"}{$Name} = $Name);
1581 }
1582 }
1583 foreach my $Path (sort {length($a)<=>length($b)} keys(%DefaultBinPaths))
1584 {
1585 if(-f $Path."/".$Name) {
1586 return ($Cache{"get_CmdPath_Default"}{$Name} = joinPath($Path,$Name));
1587 }
1588 }
1589 return ($Cache{"get_CmdPath_Default"}{$Name} = "");
1590}
1591
1592sub clean_path($)
1593{
1594 my $Path = $_[0];
1595 $Path=~s/[\/\\]+\Z//g;
1596 return $Path;
1597}
1598
1599sub classifyPath($)
1600{
1601 my $Path = $_[0];
1602 if($Path=~/[\*\[]/)
1603 { # wildcard
1604 $Path=~s/\*/.*/g;
1605 $Path=~s/\\/\\\\/g;
1606 return ($Path, "Pattern");
1607 }
1608 elsif($Path=~/[\/\\]/)
1609 { # directory or relative path
1610 return (path_format($Path, $OSgroup), "Path");
1611 }
1612 else {
1613 return ($Path, "Name");
1614 }
1615}
1616
1617sub readDescriptor($$)
1618{
1619 my ($LibVersion, $Content) = @_;
1620 return if(not $LibVersion);
1621 my $DName = $DumpAPI?"descriptor":"descriptor \"d$LibVersion\"";
1622 if(not $Content) {
1623 exitStatus("Error", "$DName is empty");
1624 }
1625 if($Content!~/\</) {
1626 exitStatus("Error", "$DName is not a descriptor (see -d1 option)");
1627 }
1628 $Content=~s/\/\*(.|\n)+?\*\///g;
1629 $Content=~s/<\!--(.|\n)+?-->//g;
1630 $Descriptor{$LibVersion}{"Version"} = parseTag(\$Content, "version");
1631 if($TargetVersion{$LibVersion}) {
1632 $Descriptor{$LibVersion}{"Version"} = $TargetVersion{$LibVersion};
1633 }
1634 if(not $Descriptor{$LibVersion}{"Version"}) {
1635 exitStatus("Error", "version in the $DName is not specified (<version> section)");
1636 }
1637 if($Content=~/{RELPATH}/)
1638 {
1639 if(my $RelDir = $RelativeDirectory{$LibVersion}) {
1640 $Content =~ s/{RELPATH}/$RelDir/g;
1641 }
1642 else
1643 {
1644 my $NeedRelpath = $DumpAPI?"-relpath":"-relpath$LibVersion";
1645 exitStatus("Error", "you have not specified $NeedRelpath option, but the $DName contains {RELPATH} macro");
1646 }
1647 }
1648
1649 if(not $CheckObjectsOnly_Opt)
1650 {
1651 my $DHeaders = parseTag(\$Content, "headers");
1652 if(not $DHeaders) {
1653 exitStatus("Error", "header files in the $DName are not specified (<headers> section)");
1654 }
1655 elsif(lc($DHeaders) ne "none")
1656 { # append the descriptor headers list
1657 if($Descriptor{$LibVersion}{"Headers"})
1658 { # multiple descriptors
1659 $Descriptor{$LibVersion}{"Headers"} .= "\n".$DHeaders;
1660 }
1661 else {
1662 $Descriptor{$LibVersion}{"Headers"} = $DHeaders;
1663 }
1664 foreach my $Path (split(/\s*\n\s*/, $DHeaders))
1665 {
1666 if(not -e $Path) {
1667 exitStatus("Access_Error", "can't access \'$Path\'");
1668 }
1669 }
1670 }
1671 }
1672 if(not $CheckHeadersOnly_Opt)
1673 {
1674 my $DObjects = parseTag(\$Content, "libs");
1675 if(not $DObjects) {
1676 exitStatus("Error", "$SLIB_TYPE libraries in the $DName are not specified (<libs> section)");
1677 }
1678 elsif(lc($DObjects) ne "none")
1679 { # append the descriptor libraries list
1680 if($Descriptor{$LibVersion}{"Libs"})
1681 { # multiple descriptors
1682 $Descriptor{$LibVersion}{"Libs"} .= "\n".$DObjects;
1683 }
1684 else {
1685 $Descriptor{$LibVersion}{"Libs"} .= $DObjects;
1686 }
1687 foreach my $Path (split(/\s*\n\s*/, $DObjects))
1688 {
1689 if(not -e $Path) {
1690 exitStatus("Access_Error", "can't access \'$Path\'");
1691 }
1692 }
1693 }
1694 }
1695 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers")))
1696 {
1697 $Path = clean_path($Path);
1698 if(not -d $Path) {
1699 exitStatus("Access_Error", "can't access directory \'$Path\'");
1700 }
1701 $Path = path_format($Path, $OSgroup);
1702 $SystemPaths{"include"}{$Path}=1;
1703 }
1704 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs")))
1705 {
1706 $Path = clean_path($Path);
1707 if(not -d $Path) {
1708 exitStatus("Access_Error", "can't access directory \'$Path\'");
1709 }
1710 $Path = path_format($Path, $OSgroup);
1711 $SystemPaths{"lib"}{$Path}=1;
1712 }
1713 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools")))
1714 {
1715 $Path=clean_path($Path);
1716 if(not -d $Path) {
1717 exitStatus("Access_Error", "can't access directory \'$Path\'");
1718 }
1719 $Path = path_format($Path, $OSgroup);
1720 $SystemPaths{"bin"}{$Path}=1;
1721 $TargetTools{$Path}=1;
1722 }
1723 if(my $Prefix = parseTag(\$Content, "cross_prefix")) {
1724 $CrossPrefix = $Prefix;
1725 }
1726 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "include_paths")))
1727 {
1728 $Path=clean_path($Path);
1729 if(not -d $Path) {
1730 exitStatus("Access_Error", "can't access directory \'$Path\'");
1731 }
1732 $Path = path_format($Path, $OSgroup);
1733 $Descriptor{$LibVersion}{"IncludePaths"}{$Path} = 1;
1734 }
1735 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "add_include_paths")))
1736 {
1737 $Path=clean_path($Path);
1738 if(not -d $Path) {
1739 exitStatus("Access_Error", "can't access directory \'$Path\'");
1740 }
1741 $Path = path_format($Path, $OSgroup);
1742 $Descriptor{$LibVersion}{"AddIncludePaths"}{$Path} = 1;
1743 }
1744 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_include_paths")))
1745 {
1746 # skip some auto-generated include paths
1747 $Skip_Include_Paths{$LibVersion}{path_format($Path)}=1;
1748 }
1749 foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_including")))
1750 {
1751 # skip direct including of some headers
1752 my ($CPath, $Type) = classifyPath($Path);
1753 $SkipHeaders{$LibVersion}{$Type}{$CPath} = 2;
1754 $SkipHeadersList{$LibVersion}{$Path} = 2;
1755 }
1756 $Descriptor{$LibVersion}{"GccOptions"} = parseTag(\$Content, "gcc_options");
1757 foreach my $Option (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"GccOptions"})) {
1758 $CompilerOptions{$LibVersion} .= " ".$Option;
1759 }
1760 $Descriptor{$LibVersion}{"SkipHeaders"} = parseTag(\$Content, "skip_headers");
1761 foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipHeaders"}))
1762 {
1763 my ($CPath, $Type) = classifyPath($Path);
1764 $SkipHeaders{$LibVersion}{$Type}{$CPath} = 1;
1765 $SkipHeadersList{$LibVersion}{$Path} = 1;
1766 }
1767 $Descriptor{$LibVersion}{"SkipLibs"} = parseTag(\$Content, "skip_libs");
1768 foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipLibs"}))
1769 {
1770 my ($CPath, $Type) = classifyPath($Path);
1771 $SkipLibs{$LibVersion}{$Type}{$CPath} = 1;
1772 }
1773 if(my $DDefines = parseTag(\$Content, "defines"))
1774 {
1775 if($Descriptor{$LibVersion}{"Defines"})
1776 { # multiple descriptors
1777 $Descriptor{$LibVersion}{"Defines"} .= "\n".$DDefines;
1778 }
1779 else {
1780 $Descriptor{$LibVersion}{"Defines"} = $DDefines;
1781 }
1782 }
1783 foreach my $Order (split(/\s*\n\s*/, parseTag(\$Content, "include_order")))
1784 {
1785 if($Order=~/\A(.+):(.+)\Z/) {
1786 $Include_Order{$LibVersion}{$1} = $2;
1787 }
1788 }
1789 foreach my $Type_Name (split(/\s*\n\s*/, parseTag(\$Content, "opaque_types")),
1790 split(/\s*\n\s*/, parseTag(\$Content, "skip_types")))
1791 {# opaque_types renamed to skip_types (1.23.4)
1792 $SkipTypes{$LibVersion}{$Type_Name} = 1;
1793 }
1794 foreach my $Symbol (split(/\s*\n\s*/, parseTag(\$Content, "skip_interfaces")),
1795 split(/\s*\n\s*/, parseTag(\$Content, "skip_symbols")))
1796 {# skip_interfaces renamed to skip_symbols (1.22.1)
1797 $SkipSymbols{$LibVersion}{$Symbol} = 1;
1798 }
1799 foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "skip_namespaces"))) {
1800 $SkipNameSpaces{$LibVersion}{$NameSpace} = 1;
1801 }
1802 foreach my $Constant (split(/\s*\n\s*/, parseTag(\$Content, "skip_constants"))) {
1803 $SkipConstants{$LibVersion}{$Constant} = 1;
1804 }
1805 if(my $DIncPreamble = parseTag(\$Content, "include_preamble"))
1806 {
1807 if($Descriptor{$LibVersion}{"IncludePreamble"})
1808 {# multiple descriptors
1809 $Descriptor{$LibVersion}{"IncludePreamble"} .= "\n".$DIncPreamble;
1810 }
1811 else {
1812 $Descriptor{$LibVersion}{"IncludePreamble"} = $DIncPreamble;
1813 }
1814 }
1815}
1816
1817sub parseTag($$)
1818{
1819 my ($CodeRef, $Tag) = @_;
1820 return "" if(not $CodeRef or not ${$CodeRef} or not $Tag);
1821 if(${$CodeRef}=~s/\<\Q$Tag\E\>((.|\n)+?)\<\/\Q$Tag\E\>//)
1822 {
1823 my $Content = $1;
1824 $Content=~s/(\A\s+|\s+\Z)//g;
1825 return $Content;
1826 }
1827 else {
1828 return "";
1829 }
1830}
1831
1832my %check_node= map {$_=>1} (
1833 "array_type",
1834 "binfo",
1835 "boolean_type",
1836 "complex_type",
1837 "const_decl",
1838 "enumeral_type",
1839 "field_decl",
1840 "function_decl",
1841 "function_type",
1842 "identifier_node",
1843 "integer_cst",
1844 "integer_type",
1845 "method_type",
1846 "namespace_decl",
1847 "parm_decl",
1848 "pointer_type",
1849 "real_cst",
1850 "real_type",
1851 "record_type",
1852 "reference_type",
1853 "string_cst",
1854 "template_decl",
1855 "template_type_parm",
1856 "tree_list",
1857 "tree_vec",
1858 "type_decl",
1859 "union_type",
1860 "var_decl",
1861 "void_type",
1862 "offset_type" );
1863
1864sub getInfo($)
1865{
1866 my $InfoPath = $_[0];
1867 return if(not $InfoPath or not -f $InfoPath);
1868 my $Content = readFile($InfoPath);
1869 unlink($InfoPath);
1870 $Content=~s/\n[ ]+/ /g;
1871 my @Lines = split("\n", $Content);
1872 $Content="";# clear
1873 foreach (@Lines)
1874 {
1875 if(/\A\@(\d+)\s+([a-z_]+)\s+(.+)\Z/oi)
1876 { # get a number and attributes of a node
1877 next if(not $check_node{$2});
1878 $LibInfo{$Version}{"info_type"}{$1}=$2;
1879 $LibInfo{$Version}{"info"}{$1}=$3;
1880 }
1881 }
1882 $MAX_TID = $#Lines+1;
1883 @Lines=();# clear
1884 # processing info
1885 setTemplateParams_All();
1886 getTypeInfo_All();
1887 simplifyNames();
1888 getSymbolInfo_All();
1889 getVarInfo_All();
1890
1891 # cleaning memory
1892 %LibInfo = ();
1893 %TemplateInstance = ();
1894 %TemplateInstance_Func = ();
1895 %MangledNames = ();
1896
1897 if($Debug) {
1898 # debugMangling($Version);
1899 }
1900}
1901
1902sub simplifyNames()
1903{
1904 foreach my $Base (keys(%{$Typedef_Tr{$Version}}))
1905 {
1906 my @Translations = keys(%{$Typedef_Tr{$Version}{$Base}});
1907 if($#Translations==0 and length($Translations[0])<=length($Base)) {
1908 $Typedef_Eq{$Version}{$Base} = $Translations[0];
1909 }
1910 }
1911 foreach my $TDid (keys(%{$TypeInfo{$Version}}))
1912 {
1913 foreach my $Tid (keys(%{$TypeInfo{$Version}{$TDid}}))
1914 {
1915 my $TypeName = $TypeInfo{$Version}{$TDid}{$Tid}{"Name"};
1916 if(not $TypeName) {
1917 next;
1918 }
1919 next if(index($TypeName,"<")==-1);# template instances only
1920 if($TypeName=~/>(::\w+)+\Z/)
1921 { # skip unused types
1922 next;
1923 };
1924 foreach my $Base (sort {length($b)<=>length($a)}
1925 sort {$b cmp $a} keys(%{$Typedef_Eq{$Version}}))
1926 {
1927 next if(not $Base);
1928 next if(index($TypeName,$Base)==-1);
1929 next if(length($TypeName) - length($Base) <= 3);
1930 my $Typedef = $Typedef_Eq{$Version}{$Base};
1931 $TypeName=~s/(\<|\,)\Q$Base\E(\W|\Z)/$1$Typedef$2/g;
1932 $TypeName=~s/(\<|\,)\Q$Base\E(\w|\Z)/$1$Typedef $2/g;
1933 }
1934 if($TypeName ne $TypeInfo{$Version}{$TDid}{$Tid}{"Name"})
1935 {
1936 $TypeInfo{$Version}{$TDid}{$Tid}{"Name"} = formatName($TypeName);
1937 $TName_Tid{$Version}{$TypeName} = $Tid;
1938 }
1939 }
1940 }
1941}
1942
1943sub setTemplateParams_All()
1944{
1945 foreach (keys(%{$LibInfo{$Version}{"info"}}))
1946 {
1947 if($LibInfo{$Version}{"info_type"}{$_} eq "template_decl") {
1948 setTemplateParams($_);
1949 }
1950 }
1951}
1952
1953sub setTemplateParams($)
1954{
1955 my $TypeInfoId = $_[0];
1956 if($LibInfo{$Version}{"info"}{$TypeInfoId}=~/(inst|spcs)[ ]*:[ ]*@(\d+) /)
1957 {
1958 my $TmplInst_InfoId = $2;
1959 setTemplateInstParams($TmplInst_InfoId);
1960 my $TmplInst_Info = $LibInfo{$Version}{"info"}{$TmplInst_InfoId};
1961 while($TmplInst_Info=~/(chan|chain)[ ]*:[ ]*@(\d+) /)
1962 {
1963 $TmplInst_InfoId = $2;
1964 $TmplInst_Info = $LibInfo{$Version}{"info"}{$TmplInst_InfoId};
1965 setTemplateInstParams($TmplInst_InfoId);
1966 }
1967 }
1968}
1969
1970sub setTemplateInstParams($)
1971{
1972 my $TmplInst_Id = $_[0];
1973 my $Info = $LibInfo{$Version}{"info"}{$TmplInst_Id};
1974 my ($Params_InfoId, $ElemId) = ();
1975 if($Info=~/purp[ ]*:[ ]*@(\d+) /) {
1976 $Params_InfoId = $1;
1977 }
1978 if($Info=~/valu[ ]*:[ ]*@(\d+) /) {
1979 $ElemId = $1;
1980 }
1981 if($Params_InfoId and $ElemId)
1982 {
1983 my $Params_Info = $LibInfo{$Version}{"info"}{$Params_InfoId};
1984 while($Params_Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
1985 {
1986 my ($Param_Pos, $Param_TypeId) = ($1, $2);
1987 return if($LibInfo{$Version}{"info_type"}{$Param_TypeId} eq "template_type_parm");
1988 if($LibInfo{$Version}{"info_type"}{$ElemId} eq "function_decl") {
1989 $TemplateInstance_Func{$Version}{$ElemId}{$Param_Pos} = $Param_TypeId;
1990 }
1991 else {
1992 $TemplateInstance{$Version}{getTypeDeclId($ElemId)}{$ElemId}{$Param_Pos} = $Param_TypeId;
1993 }
1994 }
1995 }
1996}
1997
1998sub getTypeDeclId($)
1999{
2000 if($LibInfo{$Version}{"info"}{$_[0]}=~/name[ ]*:[ ]*@(\d+)/) {
2001 return $1;
2002 }
2003 else {
2004 return "";
2005 }
2006}
2007
2008sub isFuncPtr($)
2009{
2010 my $Ptd = pointTo($_[0]);
2011 return 0 if(not $Ptd);
2012 if($LibInfo{$Version}{"info"}{$_[0]}=~/unql[ ]*:/
2013 and $LibInfo{$Version}{"info"}{$_[0]}!~/qual[ ]*:/) {
2014 return 0;
2015 }
2016 elsif($LibInfo{$Version}{"info_type"}{$_[0]} eq "pointer_type"
2017 and $LibInfo{$Version}{"info_type"}{$Ptd} eq "function_type") {
2018 return 1;
2019 }
2020 return 0;
2021}
2022
2023sub isMethodPtr($)
2024{
2025 my $Ptd = pointTo($_[0]);
2026 return 0 if(not $Ptd);
2027 if($LibInfo{$Version}{"info_type"}{$_[0]} eq "record_type"
2028 and $LibInfo{$Version}{"info_type"}{$Ptd} eq "method_type"
2029 and $LibInfo{$Version}{"info"}{$_[0]}=~/ ptrmem /) {
2030 return 1;
2031 }
2032 return 0;
2033}
2034
2035sub isFieldPtr($)
2036{
2037 if($LibInfo{$Version}{"info_type"}{$_[0]} eq "offset_type"
2038 and $LibInfo{$Version}{"info"}{$_[0]}=~/ ptrmem /) {
2039 return 1;
2040 }
2041 return 0;
2042}
2043
2044sub pointTo($)
2045{
2046 if($LibInfo{$Version}{"info"}{$_[0]}=~/ptd[ ]*:[ ]*@(\d+)/) {
2047 return $1;
2048 }
2049 else {
2050 return "";
2051 }
2052}
2053
2054sub getTypeInfo_All()
2055{
2056 if(not check_gcc_version($GCC_PATH, "4.5"))
2057 { # support for GCC < 4.5
2058 # missed typedefs: QStyle::State is typedef to QFlags<QStyle::StateFlag>
2059 # but QStyleOption.state is of type QFlags<QStyle::StateFlag> in the TU dump
2060 # FIXME: check GCC versions
2061 addMissedTypes_Pre();
2062 }
2063 foreach (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2064 {
2065 my $IType = $LibInfo{$Version}{"info_type"}{$_};
2066 if($IType=~/_type\Z/ and $IType ne "function_type"
2067 and $IType ne "method_type") {
2068 getTypeInfo(getTypeDeclId("$_"), "$_");
2069 }
2070 }
2071 $TypeInfo{$Version}{""}{-1}{"Name"} = "...";
2072 $TypeInfo{$Version}{""}{-1}{"Type"} = "Intrinsic";
2073 $TypeInfo{$Version}{""}{-1}{"Tid"} = -1;
2074 if(not check_gcc_version($GCC_PATH, "4.5"))
2075 { # support for GCC < 4.5
2076 addMissedTypes_Post();
2077 }
2078}
2079
2080sub addMissedTypes_Pre()
2081{
2082 foreach my $MissedTDid (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2083 { # detecting missed typedefs
2084 if($LibInfo{$Version}{"info_type"}{$MissedTDid} eq "type_decl")
2085 {
2086 my $TypeId = getTreeAttr($MissedTDid, "type");
2087 next if(not $TypeId);
2088 my $TypeType = getTypeType($MissedTDid, $TypeId);
2089 if($TypeType eq "Unknown")
2090 { # template_type_parm
2091 next;
2092 }
2093 my $TypeDeclId = getTypeDeclId($TypeId);
2094 next if($TypeDeclId eq $MissedTDid);#or not $TypeDeclId
2095 my $TypedefName = getNameByInfo($MissedTDid);
2096 next if(not $TypedefName);
2097 next if($TypedefName eq "__float80");
2098 next if(isAnon($TypedefName));
2099 if(not $TypeDeclId
2100 or getNameByInfo($TypeDeclId) ne $TypedefName) {
2101 $MissedTypedef{$Version}{$TypeId}{"$MissedTDid"} = 1;
2102 }
2103 }
2104 }
2105 foreach my $Tid (keys(%{$MissedTypedef{$Version}}))
2106 { # add missed typedefs
2107 my @Missed = keys(%{$MissedTypedef{$Version}{$Tid}});
2108 if(not @Missed or $#Missed>=1) {
2109 delete($MissedTypedef{$Version}{$Tid});
2110 next;
2111 }
2112 my $MissedTDid = $Missed[0];
2113 my $TDid = getTypeDeclId($Tid);
2114 my ($TypedefName, $TypedefNS) = getTrivialName($MissedTDid, $Tid);
2115 my %MissedInfo = ( # typedef info
2116 "Name" => $TypedefName,
2117 "NameSpace" => $TypedefNS,
2118 "BaseType" => {
2119 "TDid" => $TDid,
2120 "Tid" => $Tid
2121 },
2122 "Type" => "Typedef",
2123 "Tid" => ++$MAX_TID,
2124 "TDid" => $MissedTDid );
2125 my ($H, $L) = getLocation($MissedTDid);
2126 $MissedInfo{"Header"} = $H;
2127 $MissedInfo{"Line"} = $L;
2128 # $MissedInfo{"Size"} = getSize($Tid)/$BYTE_SIZE;
2129 my $MName = $MissedInfo{"Name"};
2130 next if(not $MName);
2131 if($MName=~/\*|\&|\s/)
2132 { # other types
2133 next;
2134 }
2135 if($MName=~/>(::\w+)+\Z/)
2136 { # QFlags<Qt::DropAction>::enum_type
2137 delete($MissedTypedef{$Version}{$Tid});
2138 next;
2139 }
2140 if(getTypeType($TDid, $Tid)=~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/)
2141 { # double-check for the name of typedef
2142 my ($TName, $TNS) = getTrivialName($TDid, $Tid); # base type info
2143 next if(not $TName);
2144 if(length($MName)>=length($TName))
2145 { # too long typedef
2146 delete($MissedTypedef{$Version}{$Tid});
2147 next;
2148 }
2149 if($TName=~/\A\Q$MName\E</) {
2150 next;
2151 }
2152 if($MName=~/\A\Q$TName\E/)
2153 { # QDateTimeEdit::Section and QDateTimeEdit::Sections::enum_type
2154 delete($MissedTypedef{$Version}{$Tid});
2155 next;
2156 }
2157 if(get_depth($MName)==0 and get_depth($TName)!=0)
2158 { # std::_Vector_base and std::vector::_Base
2159 delete($MissedTypedef{$Version}{$Tid});
2160 next;
2161 }
2162 }
2163 %{$TypeInfo{$Version}{$MissedTDid}{$MissedInfo{"Tid"}}} = %MissedInfo;
2164 $Tid_TDid{$Version}{$MissedInfo{"Tid"}} = $MissedTDid;
2165 delete($TypeInfo{$Version}{$MissedTDid}{$Tid});
2166 # register typedef
2167 $MissedTypedef{$Version}{$Tid}{"TDid"} = $MissedTDid;
2168 $MissedTypedef{$Version}{$Tid}{"Tid"} = $MissedInfo{"Tid"};
2169 }
2170}
2171
2172sub addMissedTypes_Post()
2173{
2174 foreach my $BaseId (keys(%{$MissedTypedef{$Version}}))
2175 {
2176 my $Tid = $MissedTypedef{$Version}{$BaseId}{"Tid"};
2177 my $TDid = $MissedTypedef{$Version}{$BaseId}{"TDid"};
2178 $TypeInfo{$Version}{$TDid}{$Tid}{"Size"} = get_TypeAttr($BaseId, $Version, "Size");
2179 }
2180}
2181
2182sub getTypeInfo($$)
2183{
2184 my ($TDId, $TId) = @_;
2185 %{$TypeInfo{$Version}{$TDId}{$TId}} = getTypeAttr($TDId, $TId);
2186 my $TName = $TypeInfo{$Version}{$TDId}{$TId}{"Name"};
2187 if(not $TName) {
2188 delete($TypeInfo{$Version}{$TDId}{$TId});
2189 return;
2190 }
2191 if($TDId) {
2192 $Tid_TDid{$Version}{$TId} = $TDId;
2193 }
2194 if(not $TName_Tid{$Version}{$TName}) {
2195 $TName_Tid{$Version}{$TName} = $TId;
2196 }
2197}
2198
2199sub getArraySize($$)
2200{
2201 my ($TypeId, $BaseName) = @_;
2202 my $SizeBytes = getSize($TypeId)/$BYTE_SIZE;
2203 while($BaseName=~s/\s*\[(\d+)\]//) {
2204 $SizeBytes/=$1;
2205 }
2206 my $BasicId = $TName_Tid{$Version}{$BaseName};
2207 if(my $BasicSize = $TypeInfo{$Version}{getTypeDeclId($BasicId)}{$BasicId}{"Size"}) {
2208 $SizeBytes/=$BasicSize;
2209 }
2210 return $SizeBytes;
2211}
2212
2213sub getTParams_Func($)
2214{
2215 my $FuncInfoId = $_[0];
2216 my @TmplParams = ();
2217 foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$TemplateInstance_Func{$Version}{$FuncInfoId}}))
2218 {
2219 my $Param = get_TemplateParam($Pos, $TemplateInstance_Func{$Version}{$FuncInfoId}{$Pos});
2220 if($Param eq "") {
2221 return ();
2222 }
2223 elsif($Param ne "\@skip\@") {
2224 push(@TmplParams, $Param);
2225 }
2226 }
2227 return @TmplParams;
2228}
2229
2230sub getTParams($$)
2231{
2232 my ($TypeDeclId, $TypeId) = @_;
2233 my @Template_Params = ();
2234 foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateInstance{$Version}{$TypeDeclId}{$TypeId}}))
2235 {
2236 my $Param_TypeId = $TemplateInstance{$Version}{$TypeDeclId}{$TypeId}{$Pos};
2237 my $Param = get_TemplateParam($Pos, $Param_TypeId);
2238 if($Param eq "") {
2239 return ();
2240 }
2241 elsif($Param ne "\@skip\@") {
2242 @Template_Params = (@Template_Params, $Param);
2243 }
2244 }
2245 return @Template_Params;
2246}
2247
2248sub getTypeAttr($$)
2249{
2250 my ($TypeDeclId, $TypeId) = @_;
2251 my ($BaseTypeSpec, %TypeAttr) = ();
2252 if(defined $TypeInfo{$Version}{$TypeDeclId}{$TypeId}
2253 and $TypeInfo{$Version}{$TypeDeclId}{$TypeId}{"Name"}) {
2254 return %{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}};
2255 }
2256 $TypeAttr{"Tid"} = $TypeId;
2257 $TypeAttr{"TDid"} = $TypeDeclId;
2258 $TypeAttr{"Type"} = getTypeType($TypeDeclId, $TypeId);
2259 if($TypeAttr{"Type"} eq "Unknown") {
2260 return ();
2261 }
2262 elsif($TypeAttr{"Type"}=~/(Func|Method|Field)Ptr/)
2263 {
2264 %{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}} = getMemPtrAttr(pointTo($TypeId), $TypeDeclId, $TypeId, $TypeAttr{"Type"});
2265 $TName_Tid{$Version}{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}{"Name"}} = $TypeId;
2266 return %{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}};
2267 }
2268 elsif($TypeAttr{"Type"} eq "Array")
2269 {
2270 ($TypeAttr{"BaseType"}{"Tid"}, $TypeAttr{"BaseType"}{"TDid"}, $BaseTypeSpec) = selectBaseType($TypeDeclId, $TypeId);
2271 my %BaseTypeAttr = getTypeAttr($TypeAttr{"BaseType"}{"TDid"}, $TypeAttr{"BaseType"}{"Tid"});
2272 if(my $NElems = getArraySize($TypeId, $BaseTypeAttr{"Name"}))
2273 {
2274 $TypeAttr{"Size"} = getSize($TypeId)/$BYTE_SIZE;
2275 if($BaseTypeAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2276 $TypeAttr{"Name"} = $1."[$NElems]".$2;
2277 }
2278 else {
2279 $TypeAttr{"Name"} = $BaseTypeAttr{"Name"}."[$NElems]";
2280 }
2281 }
2282 else
2283 {
2284 $TypeAttr{"Size"} = $WORD_SIZE{$Version}; # pointer
2285 if($BaseTypeAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2286 $TypeAttr{"Name"} = $1."[]".$2;
2287 }
2288 else {
2289 $TypeAttr{"Name"} = $BaseTypeAttr{"Name"}."[]";
2290 }
2291 }
2292 $TypeAttr{"Name"} = formatName($TypeAttr{"Name"});
2293 if($BaseTypeAttr{"Header"}) {
2294 $TypeAttr{"Header"} = $BaseTypeAttr{"Header"};
2295 }
2296 %{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}} = %TypeAttr;
2297 $TName_Tid{$Version}{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}{"Name"}} = $TypeId;
2298 return %TypeAttr;
2299 }
2300 elsif($TypeAttr{"Type"}=~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/)
2301 {
2302 %{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}} = getTrivialTypeAttr($TypeDeclId, $TypeId);
2303 return %{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}};
2304 }
2305 else
2306 {
2307 ($TypeAttr{"BaseType"}{"Tid"}, $TypeAttr{"BaseType"}{"TDid"}, $BaseTypeSpec) = selectBaseType($TypeDeclId, $TypeId);
2308 if(my $MissedTDid = $MissedTypedef{$Version}{$TypeAttr{"BaseType"}{"Tid"}}{"TDid"})
2309 {
2310 if($MissedTDid ne $TypeDeclId)
2311 {
2312 $TypeAttr{"BaseType"}{"TDid"} = $MissedTDid;
2313 $TypeAttr{"BaseType"}{"Tid"} = $MissedTypedef{$Version}{$TypeAttr{"BaseType"}{"Tid"}}{"Tid"};
2314 }
2315 }
2316 my %BaseTypeAttr = getTypeAttr($TypeAttr{"BaseType"}{"TDid"}, $TypeAttr{"BaseType"}{"Tid"});
2317 if(not $BaseTypeAttr{"Name"})
2318 { # const "template_type_parm"
2319 return ();
2320 }
2321 if($BaseTypeAttr{"Type"} eq "Typedef")
2322 { # relinking typedefs
2323 my %BaseBase = get_Type($BaseTypeAttr{"BaseType"}{"TDid"},$BaseTypeAttr{"BaseType"}{"Tid"}, $Version);
2324 if($BaseTypeAttr{"Name"} eq $BaseBase{"Name"}) {
2325 ($TypeAttr{"BaseType"}{"Tid"}, $TypeAttr{"BaseType"}{"TDid"}) = ($BaseBase{"Tid"}, $BaseBase{"TDid"});
2326 }
2327 }
2328 if($BaseTypeSpec)
2329 {
2330 if($TypeAttr{"Type"} eq "Pointer"
2331 and $BaseTypeAttr{"Name"}=~/\([\*]+\)/) {
2332 $TypeAttr{"Name"} = $BaseTypeAttr{"Name"};
2333 $TypeAttr{"Name"}=~s/\(([*]+)\)/($1*)/g;
2334 }
2335 else {
2336 $TypeAttr{"Name"} = $BaseTypeAttr{"Name"}." ".$BaseTypeSpec;
2337 }
2338 }
2339 else {
2340 $TypeAttr{"Name"} = $BaseTypeAttr{"Name"};
2341 }
2342 if($TypeAttr{"Type"} eq "Typedef")
2343 {
2344 $TypeAttr{"Name"} = getNameByInfo($TypeDeclId);
2345 if(my $NS = getNameSpace($TypeDeclId))
2346 {
2347 my $TypeName = $TypeAttr{"Name"};
2348 if($NS=~/\A(struct |union |class |)((.+)::|)\Q$TypeName\E\Z/)
2349 { # "some_type" is the typedef to "struct some_type" in C++
2350 if($3) {
2351 $TypeAttr{"Name"} = $3."::".$TypeName;
2352 }
2353 }
2354 else
2355 {
2356 $TypeAttr{"NameSpace"} = $NS;
2357 $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
2358 if($TypeAttr{"NameSpace"}=~/\Astd(::|\Z)/ and $BaseTypeAttr{"NameSpace"}=~/\Astd(::|\Z)/
2359 and $BaseTypeAttr{"Name"}=~/</ and $TypeAttr{"Name"}!~/>(::\w+)+\Z/)
2360 { # types like "std::fpos<__mbstate_t>" are
2361 # not covered by typedefs in the ABI dump
2362 # so trying to add such typedefs manually
2363 $StdCxxTypedef{$Version}{$BaseTypeAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
2364 if(length($TypeAttr{"Name"})<=length($BaseTypeAttr{"Name"}))
2365 {
2366 if(($BaseTypeAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/))
2367 { # skip "other" in "std" and "type" in "boost"
2368 $Typedef_Eq{$Version}{$BaseTypeAttr{"Name"}} = $TypeAttr{"Name"};
2369 }
2370 }
2371 }
2372 }
2373 }
2374 if($TypeAttr{"Name"} ne $BaseTypeAttr{"Name"}
2375 and $TypeAttr{"Name"}!~/>(::\w+)+\Z/ and $BaseTypeAttr{"Name"}!~/>(::\w+)+\Z/)
2376 {
2377 $Typedef_BaseName{$Version}{$TypeAttr{"Name"}} = $BaseTypeAttr{"Name"};
2378 if($BaseTypeAttr{"Name"}=~/</)
2379 {
2380 if(($BaseTypeAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) {
2381 $Typedef_Tr{$Version}{$BaseTypeAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
2382 }
2383 }
2384 }
2385 ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeDeclId);
2386 }
2387 if(not $TypeAttr{"Size"})
2388 {
2389 if($TypeAttr{"Type"} eq "Pointer") {
2390 $TypeAttr{"Size"} = $WORD_SIZE{$Version};
2391 }
2392 elsif($BaseTypeAttr{"Size"}) {
2393 $TypeAttr{"Size"} = $BaseTypeAttr{"Size"};
2394 }
2395 }
2396 $TypeAttr{"Name"} = formatName($TypeAttr{"Name"});
2397 if(not $TypeAttr{"Header"} and $BaseTypeAttr{"Header"}) {
2398 $TypeAttr{"Header"} = $BaseTypeAttr{"Header"};
2399 }
2400 %{$TypeInfo{$Version}{$TypeDeclId}{$TypeId}} = %TypeAttr;
2401 if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2402 $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2403 }
2404 return %TypeAttr;
2405 }
2406}
2407
2408sub get_TemplateParam($$)
2409{
2410 my ($Pos, $Type_Id) = @_;
2411 return "" if(not $Type_Id);
2412 if($Cache{"get_TemplateParam"}{$Type_Id}) {
2413 return $Cache{"get_TemplateParam"}{$Type_Id};
2414 }
2415 if(getNodeType($Type_Id) eq "integer_cst")
2416 { # int (1), unsigned (2u), char ('c' as 99), ...
2417 my $CstTid = getTreeAttr($Type_Id, "type");
2418 my %CstType = getTypeAttr(getTypeDeclId($CstTid), $CstTid);
2419 my $Num = getNodeIntCst($Type_Id);
2420 if(my $CstSuffix = $ConstantSuffix{$CstType{"Name"}}) {
2421 return $Num.$CstSuffix;
2422 }
2423 else {
2424 return "(".$CstType{"Name"}.")".$Num;
2425 }
2426 }
2427 elsif(getNodeType($Type_Id) eq "string_cst") {
2428 return getNodeStrCst($Type_Id);
2429 }
2430 elsif(getNodeType($Type_Id) eq "tree_vec") {
2431 return "\@skip\@";
2432 }
2433 else
2434 {
2435 my $Type_DId = getTypeDeclId($Type_Id);
2436 my %ParamAttr = getTypeAttr($Type_DId, $Type_Id);
2437 if(not $ParamAttr{"Name"}) {
2438 return "";
2439 }
2440 my $PName = $ParamAttr{"Name"};
2441 if($ParamAttr{"Name"}=~/\>/) {
2442 if(my $Cover = cover_stdcxx_typedef($ParamAttr{"Name"})) {
2443 $PName = $Cover;
2444 }
2445 }
2446 if($Pos>=1 and
2447 $PName=~/\Astd::(allocator|less|((char|regex)_traits)|((i|o)streambuf_iterator))\</)
2448 { # template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
2449 # template<typename _Key, typename _Compare = std::less<_Key>
2450 # template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
2451 # template<typename _Ch_type, typename _Rx_traits = regex_traits<_Ch_type> >
2452 # template<typename _CharT, typename _InIter = istreambuf_iterator<_CharT> >
2453 # template<typename _CharT, typename _OutIter = ostreambuf_iterator<_CharT> >
2454 return "\@skip\@";
2455 }
2456 return $PName;
2457 }
2458}
2459
2460sub cover_stdcxx_typedef($)
2461{
2462 my $TypeName = $_[0];
2463 if(my @Covers = sort {length($a)<=>length($b)}
2464 sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
2465 { # take the shortest typedef
2466 # FIXME: there may be more than
2467 # one typedefs to the same type
2468 return $Covers[0];
2469 }
2470 my $TypeName_Covered = $TypeName;
2471 while($TypeName=~s/(>)[ ]*(const|volatile|restrict| |\*|\&)\Z/$1/g){};
2472 if(my @Covers = sort {length($a)<=>length($b)} sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
2473 {
2474 my $Cover = $Covers[0];
2475 $TypeName_Covered=~s/(\W|\A)\Q$TypeName\E(\W|\Z)/$1$Cover$2/g;
2476 $TypeName_Covered=~s/(\W|\A)\Q$TypeName\E(\w|\Z)/$1$Cover $2/g;
2477 }
2478 return formatName($TypeName_Covered);
2479}
2480
2481sub getNodeType($)
2482{
2483 return $LibInfo{$Version}{"info_type"}{$_[0]};
2484}
2485
2486sub getNodeIntCst($)
2487{
2488 my $CstId = $_[0];
2489 my $CstTypeId = getTreeAttr($CstId, "type");
2490 if($EnumMembName_Id{$Version}{$CstId}) {
2491 return $EnumMembName_Id{$Version}{$CstId};
2492 }
2493 elsif((my $Value = getTreeValue($CstId)) ne "")
2494 {
2495 if($Value eq "0") {
2496 if(getNodeType($CstTypeId) eq "boolean_type") {
2497 return "false";
2498 }
2499 else {
2500 return "0";
2501 }
2502 }
2503 elsif($Value eq "1") {
2504 if(getNodeType($CstTypeId) eq "boolean_type") {
2505 return "true";
2506 }
2507 else {
2508 return "1";
2509 }
2510 }
2511 else {
2512 return $Value;
2513 }
2514 }
2515 else {
2516 return "";
2517 }
2518}
2519
2520sub getNodeStrCst($)
2521{
2522 if($LibInfo{$Version}{"info"}{$_[0]}=~/strg[ ]*: (.+) lngt:[ ]*(\d+)/)
2523 { # string length is N-1 because of the null terminator
2524 return substr($1, 0, $2-1);
2525 }
2526 else {
2527 return "";
2528 }
2529}
2530
2531sub getMemPtrAttr($$$$)
2532{ # function, method and field pointers
2533 my ($PtrId, $TypeDeclId, $TypeId, $Type) = @_;
2534 my $MemInfo = $LibInfo{$Version}{"info"}{$PtrId};
2535 if($Type eq "FieldPtr") {
2536 $MemInfo = $LibInfo{$Version}{"info"}{$TypeId};
2537 }
2538 my $MemInfo_Type = $LibInfo{$Version}{"info_type"}{$PtrId};
2539 my $MemPtrName = "";
2540 my %TypeAttr = ("Size"=>$WORD_SIZE{$Version}, "Type"=>$Type, "TDid"=>$TypeDeclId, "Tid"=>$TypeId);
2541 if($Type eq "MethodPtr")
2542 { # size of "method pointer" may be greater than WORD size
2543 $TypeAttr{"Size"} = getSize($TypeId)/$BYTE_SIZE;
2544 }
2545 # Return
2546 if($Type eq "FieldPtr")
2547 {
2548 my %ReturnAttr = getTypeAttr(getTypeDeclId($PtrId), $PtrId);
2549 $MemPtrName .= $ReturnAttr{"Name"};
2550 $TypeAttr{"Return"} = $PtrId;
2551 }
2552 else
2553 {
2554 if($MemInfo=~/retn[ ]*:[ ]*\@(\d+) /)
2555 {
2556 my $ReturnTypeId = $1;
2557 my %ReturnAttr = getTypeAttr(getTypeDeclId($ReturnTypeId), $ReturnTypeId);
2558 $MemPtrName .= $ReturnAttr{"Name"};
2559 $TypeAttr{"Return"} = $ReturnTypeId;
2560 }
2561 }
2562 # Class
2563 if($MemInfo=~/(clas|cls)[ ]*:[ ]*@(\d+) /)
2564 {
2565 $TypeAttr{"Class"} = $2;
2566 my %Class = getTypeAttr(getTypeDeclId($TypeAttr{"Class"}), $TypeAttr{"Class"});
2567 if($Class{"Name"}) {
2568 $MemPtrName .= " (".$Class{"Name"}."\:\:*)";
2569 }
2570 else {
2571 $MemPtrName .= " (*)";
2572 }
2573 }
2574 else {
2575 $MemPtrName .= " (*)";
2576 }
2577 # Parameters
2578 if($Type eq "FuncPtr"
2579 or $Type eq "MethodPtr")
2580 {
2581 my @ParamTypeName = ();
2582 if($MemInfo=~/prms[ ]*:[ ]*@(\d+) /)
2583 {
2584 my $ParamTypeInfoId = $1;
2585 my $Position = 0;
2586 while($ParamTypeInfoId)
2587 {
2588 my $ParamTypeInfo = $LibInfo{$Version}{"info"}{$ParamTypeInfoId};
2589 last if($ParamTypeInfo!~/valu[ ]*:[ ]*@(\d+) /);
2590 my $ParamTypeId = $1;
2591 my %ParamAttr = getTypeAttr(getTypeDeclId($ParamTypeId), $ParamTypeId);
2592 last if($ParamAttr{"Name"} eq "void");
2593 if($Position!=0 or $Type ne "MethodPtr")
2594 {
2595 $TypeAttr{"Param"}{$Position}{"type"} = $ParamTypeId;
2596 push(@ParamTypeName, $ParamAttr{"Name"});
2597 }
2598 last if($ParamTypeInfo!~/(chan|chain)[ ]*:[ ]*@(\d+) /);
2599 $ParamTypeInfoId = $2;
2600 $Position+=1;
2601 }
2602 }
2603 $MemPtrName .= " (".join(", ", @ParamTypeName).")";
2604 }
2605 $TypeAttr{"Name"} = formatName($MemPtrName);
2606 return %TypeAttr;
2607}
2608
2609sub getTreeTypeName($)
2610{
2611 my $Info = $LibInfo{$Version}{"info"}{$_[0]};
2612 if($Info=~/name[ ]*:[ ]*@(\d+) /) {
2613 return getNameByInfo($1);
2614 }
2615 else
2616 {
2617 if($LibInfo{$Version}{"info_type"}{$_[0]} eq "integer_type")
2618 {
2619 if($LibInfo{$Version}{"info"}{$_[0]}=~/unsigned/) {
2620 return "unsigned int";
2621 }
2622 else {
2623 return "int";
2624 }
2625 }
2626 else {
2627 return "";
2628 }
2629 }
2630}
2631
2632sub getTypeType($$)
2633{
2634 my ($TypeDeclId, $TypeId) = @_;
2635 if($MissedTypedef{$Version}{$TypeId}{"TDid"}
2636 and $MissedTypedef{$Version}{$TypeId}{"TDid"} eq $TypeDeclId)
2637 { # support for old GCC versions
2638 return "Typedef";
2639 }
2640 my $Info = $LibInfo{$Version}{"info"}{$TypeId};
2641 if($Info=~/unql[ ]*:/ and $Info!~/qual[ ]*:/
2642 and getNameByInfo($TypeDeclId)) {
2643 return "Typedef";
2644 }
2645 elsif(my ($Qual, $To) = getQual($TypeId))
2646 {
2647 if($Qual eq "const volatile") {
2648 return "ConstVolatile";
2649 }
2650 else {
2651 return ucfirst($Qual);
2652 }
2653 }
2654 my $TypeType = getTypeTypeByTypeId($TypeId);
2655 if($TypeType eq "Struct")
2656 {
2657 if($TypeDeclId
2658 and $LibInfo{$Version}{"info_type"}{$TypeDeclId} eq "template_decl") {
2659 return "Template";
2660 }
2661 else {
2662 return "Struct";
2663 }
2664 }
2665 else {
2666 return $TypeType;
2667 }
2668}
2669
2670sub getQual($)
2671{
2672 my $TypeId = $_[0];
2673 my $Info = $LibInfo{$Version}{"info"}{$TypeId};
2674 my ($Qual, $To) = ();
2675 my %UnQual = (
2676 "r"=>"restrict",
2677 "v"=>"volatile",
2678 "c"=>"const",
2679 "cv"=>"const volatile"
2680 );
2681 if($Info=~/qual[ ]*:[ ]*(r|c|v|cv) /) {
2682 $Qual = $UnQual{$1};
2683 }
2684 if($Info=~/unql[ ]*:[ ]*\@(\d+)/) {
2685 $To = $1;
2686 }
2687 if($Qual and $To) {
2688 return ($Qual, $To);
2689 }
2690 return ();
2691}
2692
2693sub selectBaseType($$)
2694{
2695 my ($TypeDeclId, $TypeId) = @_;
2696 if($MissedTypedef{$Version}{$TypeId}{"TDid"}
2697 and $MissedTypedef{$Version}{$TypeId}{"TDid"} eq $TypeDeclId) {
2698 return ($TypeId, getTypeDeclId($TypeId), "");
2699 }
2700 my $TInfo = $LibInfo{$Version}{"info"}{$TypeId};
2701 if(my ($Qual, $To) = getQual($TypeId)
2702 and $TInfo=~/name[ ]*:[ ]*\@(\d+) /
2703 and (getTypeId($1) ne $TypeId)) {
2704 return (getTypeId($1), $1, $Qual);
2705 }
2706 elsif($TInfo!~/qual[ ]*:/
2707 and $TInfo=~/unql[ ]*:[ ]*\@(\d+) /
2708 and getNameByInfo($TypeDeclId))
2709 { # typedefs
2710 return ($1, getTypeDeclId($1), "");
2711 }
2712 elsif(my ($Qual, $To) = getQual($TypeId)) {
2713 return ($To, getTypeDeclId($To), $Qual);
2714 }
2715 elsif($LibInfo{$Version}{"info_type"}{$TypeId} eq "reference_type")
2716 {
2717 if($TInfo=~/refd[ ]*:[ ]*@(\d+) /) {
2718 return ($1, getTypeDeclId($1), "&");
2719 }
2720 else {
2721 return (0, 0, "");
2722 }
2723 }
2724 elsif($LibInfo{$Version}{"info_type"}{$TypeId} eq "array_type")
2725 {
2726 if($TInfo=~/elts[ ]*:[ ]*@(\d+) /) {
2727 return ($1, getTypeDeclId($1), "");
2728 }
2729 else {
2730 return (0, 0, "");
2731 }
2732 }
2733 elsif($LibInfo{$Version}{"info_type"}{$TypeId} eq "pointer_type")
2734 {
2735 if($TInfo=~/ptd[ ]*:[ ]*@(\d+) /) {
2736 return ($1, getTypeDeclId($1), "*");
2737 }
2738 else {
2739 return (0, 0, "");
2740 }
2741 }
2742 else {
2743 return (0, 0, "");
2744 }
2745}
2746
2747sub getSymbolInfo_All()
2748{
2749 foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
2750 { # reverse order
2751 if($LibInfo{$Version}{"info_type"}{$_} eq "function_decl") {
2752 getSymbolInfo("$_");
2753 }
2754 }
2755}
2756
2757sub getVarInfo_All()
2758{
2759 foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
2760 { # reverse order
2761 if($LibInfo{$Version}{"info_type"}{$_} eq "var_decl") {
2762 getVarInfo("$_");
2763 }
2764 }
2765}
2766
2767sub isBuiltIn($) {
2768 return ($_[0]=~/\<built\-in\>|\<internal\>|\A\./);
2769}
2770
2771sub getVarInfo($)
2772{
2773 my $InfoId = $_[0];
2774 if($LibInfo{$Version}{"info_type"}{getNameSpaceId($InfoId)} eq "function_decl") {
2775 return;
2776 }
2777 ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId);
2778 if(not $SymbolInfo{$Version}{$InfoId}{"Header"}
2779 or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"})) {
2780 delete($SymbolInfo{$Version}{$InfoId});
2781 return;
2782 }
2783 my $ShortName = $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getVarShortName($InfoId);
2784 if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\Atmp_add_class_\d+\Z/) {
2785 delete($SymbolInfo{$Version}{$InfoId});
2786 return;
2787 }
2788 $SymbolInfo{$Version}{$InfoId}{"MnglName"} = getFuncMnglName($InfoId);
2789 if($SymbolInfo{$Version}{$InfoId}{"MnglName"}
2790 and $SymbolInfo{$Version}{$InfoId}{"MnglName"}!~/\A_Z/)
2791 { # validate mangled name
2792 delete($SymbolInfo{$Version}{$InfoId});
2793 return;
2794 }
2795 $SymbolInfo{$Version}{$InfoId}{"Data"} = 1;
2796 $SymbolInfo{$Version}{$InfoId}{"Return"} = getTypeId($InfoId);
2797 if(not $SymbolInfo{$Version}{$InfoId}{"Return"}) {
2798 delete($SymbolInfo{$Version}{$InfoId}{"Return"});
2799 }
2800 set_Class_And_Namespace($InfoId);
2801 if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i) {
2802 $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C";
2803 }
2804 if($UserLang eq "C")
2805 { # --lang=C option
2806 $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
2807 }
2808 if($COMMON_LANGUAGE{$Version} eq "C++")
2809 {
2810 if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
2811 { # for some symbols (_ZTI) the short name is the mangled name
2812 if($ShortName=~/\A_Z/) {
2813 $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
2814 }
2815 }
2816 if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
2817 { # try to mangle symbol (link with libraries)
2818 $SymbolInfo{$Version}{$InfoId}{"MnglName"} = linkSymbol($InfoId);
2819 }
2820 if($OStarget eq "windows")
2821 {
2822 if(my $Mangled = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")})
2823 { # link MS C++ symbols from library with GCC symbols from headers
2824 $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled;
2825 }
2826 }
2827 }
2828 if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) {
2829 $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
2830 }
2831 if(not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps")
2832 and not $CheckHeadersOnly)
2833 {
2834 if(link_symbol($ShortName, $Version, "-Deps")
2835 and $SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/_ZL\d+$ShortName\Z/)
2836 { # const int global_data is mangled as _ZL11global_data in the tree
2837 $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
2838 }
2839 else {
2840 delete($SymbolInfo{$Version}{$InfoId});
2841 return;
2842 }
2843 }
2844 if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) {
2845 delete($SymbolInfo{$Version}{$InfoId});
2846 return;
2847 }
2848 if(my $AddedTid = $MissedTypedef{$Version}{$SymbolInfo{$Version}{$InfoId}{"Return"}}{"Tid"}) {
2849 $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid;
2850 }
2851 setFuncAccess($InfoId);
2852 if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZTV/) {
2853 delete($SymbolInfo{$Version}{$InfoId}{"Return"});
2854 }
2855 if($ShortName=~/\A(_Z|\?)/) {
2856 delete($SymbolInfo{$Version}{$InfoId}{"ShortName"});
2857 }
2858}
2859
2860sub getTrivialName($$)
2861{
2862 my ($TypeInfoId, $TypeId) = @_;
2863 my %TypeAttr = ();
2864 $TypeAttr{"Name"} = getNameByInfo($TypeInfoId);
2865 if(not $TypeAttr{"Name"}) {
2866 $TypeAttr{"Name"} = getTreeTypeName($TypeId);
2867 }
2868 $TypeAttr{"Name"}=~s/<(.+)\Z//g; # GCC 3.4.4 add template params to the name
2869 if(my $NameSpaceId = getNameSpaceId($TypeInfoId)) {
2870 if($NameSpaceId ne $TypeId) {
2871 $TypeAttr{"NameSpace"} = getNameSpace($TypeInfoId);
2872 }
2873 }
2874 if($TypeAttr{"NameSpace"} and isNotAnon($TypeAttr{"Name"})) {
2875 $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
2876 }
2877 $TypeAttr{"Name"} = formatName($TypeAttr{"Name"});
2878 if(isAnon($TypeAttr{"Name"}))
2879 {# anon-struct-header.h-line
2880 $TypeAttr{"Name"} = "anon-".lc(getTypeType($TypeInfoId, $TypeId))."-";
2881 $TypeAttr{"Name"} .= $TypeAttr{"Header"}."-".$TypeAttr{"Line"};
2882 }
2883 if(defined $TemplateInstance{$Version}{$TypeInfoId}{$TypeId})
2884 {
2885 my @TParams = getTParams($TypeInfoId, $TypeId);
2886 if(not @TParams)
2887 { # template declarations with abstract params
2888 # vector (tree_vec) of template_type_parm nodes in the TU dump
2889 return ("", "");
2890 }
2891 $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."< ".join(", ", @TParams)." >");
2892 }
2893 return ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"});
2894}
2895
2896sub getTrivialTypeAttr($$)
2897{
2898 my ($TypeInfoId, $TypeId) = @_;
2899 my %TypeAttr = ();
2900 if(getTypeTypeByTypeId($TypeId)!~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/) {
2901 return ();
2902 }
2903 setTypeAccess($TypeId, \%TypeAttr);
2904 ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId);
2905 if(isBuiltIn($TypeAttr{"Header"}))
2906 {
2907 delete($TypeAttr{"Header"});
2908 delete($TypeAttr{"Line"});
2909 }
2910 $TypeAttr{"Type"} = getTypeType($TypeInfoId, $TypeId);
2911 ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}) = getTrivialName($TypeInfoId, $TypeId);
2912 if(not $TypeAttr{"Name"}) {
2913 return ();
2914 }
2915 if(not $TypeAttr{"NameSpace"}) {
2916 delete($TypeAttr{"NameSpace"});
2917 }
2918 if(isAnon($TypeAttr{"Name"}))
2919 {
2920 $TypeAttr{"Name"} = "anon-".lc($TypeAttr{"Type"})."-";
2921 $TypeAttr{"Name"} .= $TypeAttr{"Header"}."-".$TypeAttr{"Line"};
2922 }
2923 if(my $Size = getSize($TypeId)) {
2924 $TypeAttr{"Size"} = $Size/$BYTE_SIZE;
2925 }
2926 if($TypeAttr{"Type"} eq "Struct"
2927 and detect_lang($TypeId))
2928 {
2929 $TypeAttr{"Type"} = "Class";
2930 $TypeAttr{"Copied"} = 1;# default, will be changed in getSymbolInfo()
2931 }
2932 if($TypeAttr{"Type"} eq "Struct"
2933 or $TypeAttr{"Type"} eq "Class") {
2934 setBaseClasses($TypeInfoId, $TypeId, \%TypeAttr);
2935 }
2936 setSpec($TypeId, \%TypeAttr);
2937 setTypeMemb($TypeId, \%TypeAttr);
2938 $TypeAttr{"Tid"} = $TypeId;
2939 $TypeAttr{"TDid"} = $TypeInfoId;
2940 if($TypeInfoId) {
2941 $Tid_TDid{$Version}{$TypeId} = $TypeInfoId;
2942 }
2943 if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2944 $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2945 }
2946 if(my $VTable = $ClassVTable_Content{$Version}{$TypeAttr{"Name"}})
2947 {
2948 my @Entries = split(/\n/, $VTable);
2949 foreach (1 .. $#Entries)
2950 {
2951 my $Entry = $Entries[$_];
2952 if($Entry=~/\A(\d+)\s+(.+)\Z/) {
2953 $TypeAttr{"VTable"}{$1} = $2;
2954 }
2955 }
2956 }
2957 return %TypeAttr;
2958}
2959
2960sub detect_lang($)
2961{
2962 my $TypeId = $_[0];
2963 my $Info = $LibInfo{$Version}{"info"}{$TypeId};
2964 if(check_gcc_version($GCC_PATH, "4"))
2965 {# GCC 4 fncs-node points to only non-artificial methods
2966 return ($Info=~/(fncs)[ ]*:[ ]*@(\d+) /);
2967 }
2968 else
2969 {# GCC 3
2970 my $Fncs = getTreeAttr($TypeId, "fncs");
2971 while($Fncs)
2972 {
2973 my $Info = $LibInfo{$Version}{"info"}{$Fncs};
2974 if($Info!~/artificial/) {
2975 return 1;
2976 }
2977 $Fncs = getTreeAttr($Fncs, "chan");
2978 }
2979 }
2980 return 0;
2981}
2982
2983sub setSpec($$)
2984{
2985 my ($TypeId, $TypeAttr) = @_;
2986 my $Info = $LibInfo{$Version}{"info"}{$TypeId};
2987 if($Info=~/\s+spec\s+/) {
2988 $TypeAttr->{"Spec"} = 1;
2989 }
2990}
2991
2992sub setBaseClasses($$$)
2993{
2994 my ($TypeInfoId, $TypeId, $TypeAttr) = @_;
2995 my $Info = $LibInfo{$Version}{"info"}{$TypeId};
2996 if($Info=~/binf[ ]*:[ ]*@(\d+) /)
2997 {
2998 $Info = $LibInfo{$Version}{"info"}{$1};
2999 my $Pos = 0;
3000 while($Info=~s/(pub|public|prot|protected|priv|private|)[ ]+binf[ ]*:[ ]*@(\d+) //)
3001 {
3002 my ($Access, $BInfoId) = ($1, $2);
3003 my $ClassId = getBinfClassId($BInfoId);
3004 my $BaseInfo = $LibInfo{$Version}{"info"}{$BInfoId};
3005 if($Access=~/prot/)
3006 {
3007 $TypeAttr->{"Base"}{$ClassId}{"access"} = "protected";
3008 }
3009 elsif($Access=~/priv/)
3010 {
3011 $TypeAttr->{"Base"}{$ClassId}{"access"} = "private";
3012 }
3013 $TypeAttr->{"Base"}{$ClassId}{"pos"} = $Pos++;
3014 if($BaseInfo=~/virt/)
3015 {# virtual base
3016 $TypeAttr->{"Base"}{$ClassId}{"virtual"} = 1;
3017 }
3018 $Class_SubClasses{$Version}{$ClassId}{$TypeId}=1;
3019 }
3020 }
3021}
3022
3023sub getBinfClassId($)
3024{
3025 my $Info = $LibInfo{$Version}{"info"}{$_[0]};
3026 $Info=~/type[ ]*:[ ]*@(\d+) /;
3027 return $1;
3028}
3029
3030sub unmangledFormat($$)
3031{
3032 my ($Name, $LibVersion) = @_;
3033 $Name = uncover_typedefs($Name, $LibVersion);
3034 while($Name=~s/([^\w>*])(const|volatile)(,|>|\Z)/$1$3/g){};
3035 $Name=~s/\(\w+\)(\d)/$1/;
3036 return $Name;
3037}
3038
3039sub modelUnmangled($$)
3040{
3041 my ($InfoId, $Compiler) = @_;
3042 if($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId}) {
3043 return $Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId};
3044 }
3045 my $PureSignature = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
3046 if($SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
3047 $PureSignature = "~".$PureSignature;
3048 }
3049 if(not $SymbolInfo{$Version}{$InfoId}{"Data"})
3050 {
3051 my (@Params, @ParamTypes) = ();
3052 if(defined $SymbolInfo{$Version}{$InfoId}{"Param"}
3053 and not $SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
3054 @Params = keys(%{$SymbolInfo{$Version}{$InfoId}{"Param"}});
3055 }
3056 foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
3057 { # checking parameters
3058 my $PId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"type"};
3059 my %PType = get_PureType($Tid_TDid{$Version}{$PId}, $PId, $Version);
3060 my $PTName = unmangledFormat($PType{"Name"}, $Version);
3061 $PTName=~s/(\A|\W)(restrict|register)(\W|\Z)/$1$3/g;
3062 if($Compiler eq "MSVC") {
3063 $PTName=~s/(\W|\A)long long(\W|\Z)/$1__int64$2/;
3064 }
3065 @ParamTypes = (@ParamTypes, $PTName);
3066 }
3067 if(@ParamTypes) {
3068 $PureSignature .= "(".join(", ", @ParamTypes).")";
3069 }
3070 else
3071 {
3072 if($Compiler eq "MSVC")
3073 {
3074 $PureSignature .= "(void)";
3075 }
3076 else
3077 { # GCC
3078 $PureSignature .= "()";
3079 }
3080 }
3081 $PureSignature = delete_keywords($PureSignature);
3082 }
3083 if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
3084 {
3085 my $ClassName = unmangledFormat(get_TypeName($ClassId, $Version), $Version);
3086 $PureSignature = $ClassName."::".$PureSignature;
3087 }
3088 elsif(my $NS = $SymbolInfo{$Version}{$InfoId}{"NameSpace"}) {
3089 $PureSignature = $NS."::".$PureSignature;
3090 }
3091 if($SymbolInfo{$Version}{$InfoId}{"Const"}) {
3092 $PureSignature .= " const";
3093 }
3094 if($SymbolInfo{$Version}{$InfoId}{"Volatile"}) {
3095 $PureSignature .= " volatile";
3096 }
3097 my $ShowReturn = 0;
3098 if($Compiler eq "MSVC"
3099 and $SymbolInfo{$Version}{$InfoId}{"Data"})
3100 {
3101 $ShowReturn=1;
3102 }
3103 elsif(defined $TemplateInstance_Func{$Version}{$InfoId}
3104 and keys(%{$TemplateInstance_Func{$Version}{$InfoId}}))
3105 {
3106 $ShowReturn=1;
3107 }
3108 if($ShowReturn)
3109 { # mangled names for template function specializations include return value
3110 if(my $ReturnId = $SymbolInfo{$Version}{$InfoId}{"Return"})
3111 {
3112 my %RType = get_PureType($Tid_TDid{$Version}{$ReturnId}, $ReturnId, $Version);
3113 my $ReturnName = unmangledFormat($RType{"Name"}, $Version);
3114 $PureSignature = $ReturnName." ".$PureSignature;
3115 }
3116 }
3117 return ($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId} = formatName($PureSignature));
3118}
3119
3120sub mangle_symbol($$$)
3121{ # mangling for simple methods
3122 # see gcc-4.6.0/gcc/cp/mangle.c
3123 my ($InfoId, $LibVersion, $Compiler) = @_;
3124 if($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler}) {
3125 return $Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler};
3126 }
3127 my $Mangled = "";
3128 if($Compiler eq "GCC") {
3129 $Mangled = mangle_symbol_gcc($InfoId, $LibVersion);
3130 }
3131 elsif($Compiler eq "MSVC") {
3132 $Mangled = mangle_symbol_msvc($InfoId, $LibVersion);
3133 }
3134 return ($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler} = $Mangled);
3135}
3136
3137sub mangle_symbol_msvc($$)
3138{
3139 my ($InfoId, $LibVersion) = @_;
3140 return "";
3141}
3142
3143sub mangle_symbol_gcc($$)
3144{ # see gcc-4.6.0/gcc/cp/mangle.c
3145 my ($InfoId, $LibVersion) = @_;
3146 my ($Mangled, $ClassId, $NameSpace) = ("_Z", 0, "");
3147 my %Repl = ();# SN_ replacements
3148 if($ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
3149 {
3150 my $MangledClass = mangle_param($ClassId, $LibVersion, \%Repl);
3151 if($MangledClass!~/\AN/) {
3152 $MangledClass = "N".$MangledClass;
3153 }
3154 else {
3155 $MangledClass=~s/E\Z//;
3156 }
3157 if($SymbolInfo{$LibVersion}{$InfoId}{"Volatile"}) {
3158 $MangledClass=~s/\AN/NV/;
3159 }
3160 if($SymbolInfo{$LibVersion}{$InfoId}{"Const"}) {
3161 $MangledClass=~s/\AN/NK/;
3162 }
3163 $Mangled .= $MangledClass;
3164 }
3165 elsif($NameSpace = $SymbolInfo{$LibVersion}{$InfoId}{"NameSpace"})
3166 { # mangled by name due to the absence of structured info
3167 my $MangledNS = mangle_ns($NameSpace, $LibVersion, \%Repl);
3168 if($MangledNS!~/\AN/) {
3169 $MangledNS = "N".$MangledNS;
3170 }
3171 else {
3172 $MangledNS=~s/E\Z//;
3173 }
3174 $Mangled .= $MangledNS;
3175 }
3176 my ($ShortName, $TmplParams) = template_base($SymbolInfo{$LibVersion}{$InfoId}{"ShortName"});
3177 my @TParams = getTParams_Func($InfoId);
3178 if(not @TParams and $TmplParams)
3179 { # support for old ABI dumps
3180 @TParams = separate_params($TmplParams, 0);
3181 }
3182 if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"}) {
3183 $Mangled .= "C1";
3184 }
3185 elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
3186 $Mangled .= "D0";
3187 }
3188 elsif($ShortName)
3189 {
3190 if(($NameSpace eq "__gnu_cxx"
3191 or $ShortName=~/\A__(gthrw|gthread)_/)
3192 and not $ClassId)
3193 { # _ZN9__gnu_cxxL25__exchange_and_add_singleEPii
3194 # _ZN9__gnu_cxxL19__atomic_add_singleEPii
3195 # _ZL19__gthrw_sched_yieldv
3196 # _ZL21__gthread_setspecificjPKv
3197 $Mangled .= "L";
3198 }
3199 if($ShortName=~/\Aoperator(\W.*)\Z/)
3200 {
3201 my $Op = $1;
3202 $Op=~s/\A[ ]+//g;
3203 if(my $OpMngl = $OperatorMangling{$Op}) {
3204 $Mangled .= $OpMngl;
3205 }
3206 else { # conversion operator
3207 $Mangled .= "cv".mangle_param(getTypeIdByName($Op, $LibVersion), $LibVersion, \%Repl);
3208 }
3209 }
3210 else {
3211 $Mangled .= length($ShortName).$ShortName;
3212 }
3213 if(@TParams)
3214 { # templates
3215 $Mangled .= "I";
3216 foreach my $TParam (@TParams) {
3217 $Mangled .= mangle_template_param($TParam, $LibVersion, \%Repl);
3218 }
3219 $Mangled .= "E";
3220 }
3221 if(not $ClassId and @TParams) {
3222 add_substitution($ShortName, \%Repl, 0);
3223 }
3224 }
3225 if($ClassId or $NameSpace) {
3226 $Mangled .= "E";
3227 }
3228 if(@TParams) {
3229 if(my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"}) {
3230 $Mangled .= mangle_param($Return, $LibVersion, \%Repl);
3231 }
3232 }
3233 if(not $SymbolInfo{$LibVersion}{$InfoId}{"Data"})
3234 {
3235 my @Params = ();
3236 if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
3237 and not $SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
3238 @Params = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}});
3239 }
3240 foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
3241 { # checking parameters
3242 my $ParamType_Id = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$ParamPos}{"type"};
3243 $Mangled .= mangle_param($ParamType_Id, $LibVersion, \%Repl);
3244 }
3245 if(not @Params) {
3246 $Mangled .= "v";
3247 }
3248 }
3249 $Mangled = correct_incharge($InfoId, $LibVersion, $Mangled);
3250 $Mangled = write_stdcxx_substitution($Mangled);
3251 if($Mangled eq "_Z") {
3252 return "";
3253 }
3254 return $Mangled;
3255}
3256
3257sub correct_incharge($$$)
3258{
3259 my ($InfoId, $LibVersion, $Mangled) = @_;
3260 if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"})
3261 {
3262 if($MangledNames{$LibVersion}{$Mangled}) {
3263 $Mangled=~s/C1E/C2E/;
3264 }
3265 }
3266 elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"})
3267 {
3268 if($MangledNames{$LibVersion}{$Mangled}) {
3269 $Mangled=~s/D0E/D1E/;
3270 }
3271 if($MangledNames{$LibVersion}{$Mangled}) {
3272 $Mangled=~s/D1E/D2E/;
3273 }
3274 }
3275 return $Mangled;
3276}
3277
3278sub template_base($)
3279{ # NOTE: std::_Vector_base<mysqlpp::mysql_type_info>::_Vector_impl
3280 # NOTE: operator<<
3281 my $Name = $_[0];
3282 if($Name!~/>\Z/) {
3283 return $Name;
3284 }
3285 my $TParams = $Name;
3286 while(my $CPos = detect_center($TParams, "<")) {
3287 $TParams = substr($TParams, $CPos);
3288 }
3289 $Name=~s/\Q$TParams\E\Z//;
3290 $TParams=~s/\A<(.+)>\Z/$1/;
3291 return ($Name, $TParams);
3292}
3293
3294sub get_sub_ns($)
3295{
3296 my $Name = $_[0];
3297 my @NS = ();
3298 while(my $CPos = detect_center($Name, ":"))
3299 {
3300 push(@NS, substr($Name, 0, $CPos));
3301 $Name = substr($Name, $CPos);
3302 $Name=~s/\A:://;
3303 }
3304 return (join("::", @NS), $Name);
3305}
3306
3307sub mangle_ns($$$)
3308{
3309 my ($Name, $LibVersion, $Repl) = @_;
3310 if(my $Tid = $TName_Tid{$LibVersion}{$Name})
3311 {
3312 my $Mangled = mangle_param($Tid, $LibVersion, $Repl);
3313 $Mangled=~s/\AN(.+)E\Z/$1/;
3314 return $Mangled;
3315
3316 }
3317 else
3318 {
3319 my ($MangledNS, $SubNS) = ("", "");
3320 ($SubNS, $Name) = get_sub_ns($Name);
3321 if($SubNS) {
3322 $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
3323 }
3324 $MangledNS .= length($Name).$Name;
3325 add_substitution($MangledNS, $Repl, 0);
3326 return $MangledNS;
3327 }
3328}
3329
3330sub mangle_param($$$)
3331{
3332 my ($PTid, $LibVersion, $Repl) = @_;
3333 my ($MPrefix, $Mangled) = ("", "");
3334 my %ReplCopy = %{$Repl};
3335 my %BaseType = get_BaseType($Tid_TDid{$LibVersion}{$PTid}, $PTid, $LibVersion);
3336 my $BaseType_Name = $BaseType{"Name"};
3337 if(not $BaseType_Name) {
3338 return "";
3339 }
3340 my ($ShortName, $TmplParams) = template_base($BaseType_Name);
3341 my $Suffix = get_BaseTypeQual($Tid_TDid{$LibVersion}{$PTid}, $PTid, $LibVersion);
3342 while($Suffix=~s/\s*(const|volatile|restrict)\Z//g){};
3343 while($Suffix=~/(&|\*|const)\Z/)
3344 {
3345 if($Suffix=~s/[ ]*&\Z//) {
3346 $MPrefix .= "R";
3347 }
3348 if($Suffix=~s/[ ]*\*\Z//) {
3349 $MPrefix .= "P";
3350 }
3351 if($Suffix=~s/[ ]*const\Z//)
3352 {
3353 if($MPrefix=~/R|P/
3354 or $Suffix=~/&|\*/) {
3355 $MPrefix .= "K";
3356 }
3357 }
3358 if($Suffix=~s/[ ]*volatile\Z//) {
3359 $MPrefix .= "V";
3360 }
3361 #if($Suffix=~s/[ ]*restrict\Z//) {
3362 #$MPrefix .= "r";
3363 #}
3364 }
3365 if(my $Token = $IntrinsicMangling{$BaseType_Name}) {
3366 $Mangled .= $Token;
3367 }
3368 elsif($BaseType{"Type"}=~/(Class|Struct|Union|Enum)/)
3369 {
3370 my @TParams = getTParams($BaseType{"TDid"}, $BaseType{"Tid"});
3371 if(not @TParams and $TmplParams)
3372 { # support for old ABI dumps
3373 @TParams = separate_params($TmplParams, 0);
3374 }
3375 my $MangledNS = "";
3376 my ($SubNS, $SName) = get_sub_ns($ShortName);
3377 if($SubNS) {
3378 $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
3379 }
3380 $MangledNS .= length($SName).$SName;
3381 if(@TParams) {
3382 add_substitution($MangledNS, $Repl, 0);
3383 }
3384 $Mangled .= "N".$MangledNS;
3385 if(@TParams)
3386 { # templates
3387 $Mangled .= "I";
3388 foreach my $TParam (@TParams) {
3389 $Mangled .= mangle_template_param($TParam, $LibVersion, $Repl);
3390 }
3391 $Mangled .= "E";
3392 }
3393 $Mangled .= "E";
3394 }
3395 elsif($BaseType{"Type"}=~/(FuncPtr|MethodPtr)/)
3396 {
3397 if($BaseType{"Type"} eq "MethodPtr") {
3398 $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl)."F";
3399 }
3400 else {
3401 $Mangled .= "PF";
3402 }
3403 $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
3404 my @Params = keys(%{$BaseType{"Param"}});
3405 foreach my $Num (sort {int($a)<=>int($b)} @Params) {
3406 $Mangled .= mangle_param($BaseType{"Param"}{$Num}{"type"}, $LibVersion, $Repl);
3407 }
3408 if(not @Params) {
3409 $Mangled .= "v";
3410 }
3411 $Mangled .= "E";
3412 }
3413 elsif($BaseType{"Type"} eq "FieldPtr")
3414 {
3415 $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl);
3416 $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
3417 }
3418 $Mangled = $MPrefix.$Mangled;# add prefix (RPK)
3419 if(my $Optimized = write_substitution($Mangled, \%ReplCopy))
3420 {
3421 if($Mangled eq $Optimized)
3422 {
3423 if($ShortName!~/::/)
3424 { # remove "N ... E"
3425 if($MPrefix) {
3426 $Mangled=~s/\A($MPrefix)N(.+)E\Z/$1$2/g;
3427 }
3428 else {
3429 $Mangled=~s/\AN(.+)E\Z/$1/g;
3430 }
3431 }
3432 }
3433 else {
3434 $Mangled = $Optimized;
3435 }
3436 }
3437 add_substitution($Mangled, $Repl, 1);
3438 return $Mangled;
3439}
3440
3441sub mangle_template_param($$$)
3442{ # types + literals
3443 my ($TParam, $LibVersion, $Repl) = @_;
3444 if(my $TPTid = $TName_Tid{$LibVersion}{$TParam}) {
3445 return mangle_param($TPTid, $LibVersion, $Repl);
3446 }
3447 elsif($TParam=~/\A(\d+)(\w+)\Z/)
3448 { # class_name<1u>::method(...)
3449 return "L".$IntrinsicMangling{$ConstantSuffixR{$2}}.$1."E";
3450 }
3451 elsif($TParam=~/\A\(([\w ]+)\)(\d+)\Z/)
3452 { # class_name<(signed char)1>::method(...)
3453 return "L".$IntrinsicMangling{$1}.$2."E";
3454 }
3455 elsif($TParam eq "true")
3456 { # class_name<true>::method(...)
3457 return "Lb1E";
3458 }
3459 elsif($TParam eq "false")
3460 { # class_name<true>::method(...)
3461 return "Lb0E";
3462 }
3463 else { # internal error
3464 return length($TParam).$TParam;
3465 }
3466}
3467
3468sub add_substitution($$$)
3469{
3470 my ($Value, $Repl, $Rec) = @_;
3471 if($Rec)
3472 { # subtypes
3473 my @Subs = ($Value);
3474 while($Value=~s/\A(R|P|K)//) {
3475 push(@Subs, $Value);
3476 }
3477 foreach (reverse(@Subs)) {
3478 add_substitution($_, $Repl, 0);
3479 }
3480 return;
3481 }
3482 return if($Value=~/\AS(\d*)_\Z/);
3483 $Value=~s/\AN(.+)E\Z/$1/g;
3484 return if(defined $Repl->{$Value});
3485 return if(length($Value)<=1);
3486 return if($StdcxxMangling{$Value});
3487 # check for duplicates
3488 my $Base = $Value;
3489 foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
3490 {
3491 my $Num = $Repl->{$Type};
3492 my $Replace = macro_mangle($Num);
3493 $Base=~s/\Q$Replace\E/$Type/;
3494 }
3495 if(my $OldNum = $Repl->{$Base})
3496 {
3497 $Repl->{$Value} = $OldNum;
3498 return;
3499 }
3500 my @Repls = sort {$b<=>$a} values(%{$Repl});
3501 if(@Repls) {
3502 $Repl->{$Value} = $Repls[0]+1;
3503 }
3504 else {
3505 $Repl->{$Value} = -1;
3506 }
3507 # register duplicates
3508 # upward
3509 my $Base = $Value;
3510 foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
3511 {
3512 next if($Base eq $Type);
3513 my $Num = $Repl->{$Type};
3514 my $Replace = macro_mangle($Num);
3515 $Base=~s/\Q$Type\E/$Replace/;
3516 $Repl->{$Base} = $Repl->{$Value};
3517 }
3518}
3519
3520sub macro_mangle($)
3521{
3522 my $Num = $_[0];
3523 if($Num==-1) {
3524 return "S_";
3525 }
3526 else
3527 {
3528 my $Code = "";
3529 if($Num<10)
3530 { # S0_, S1_, S2_, ...
3531 $Code = $Num;
3532 }
3533 elsif($Num>=10 and $Num<=35)
3534 { # SA_, SB_, SC_, ...
3535 $Code = chr(55+$Num);
3536 }
3537 else
3538 { # S10_, S11_, S12_
3539 $Code = $Num-26; # 26 is length of english alphabet
3540 }
3541 return "S".$Code."_";
3542 }
3543}
3544
3545sub write_stdcxx_substitution($)
3546{
3547 my $Mangled = $_[0];
3548 if($StdcxxMangling{$Mangled}) {
3549 return $StdcxxMangling{$Mangled};
3550 }
3551 else
3552 {
3553 my @Repls = keys(%StdcxxMangling);
3554 @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
3555 foreach my $MangledType (@Repls)
3556 {
3557 my $Replace = $StdcxxMangling{$MangledType};
3558 #if($Mangled!~/$Replace/) {
3559 $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
3560 $Mangled=~s/\Q$MangledType\E/$Replace/g;
3561 #}
3562 }
3563 }
3564 return $Mangled;
3565}
3566
3567sub write_substitution($$)
3568{
3569 my ($Mangled, $Repl) = @_;
3570 if(defined $Repl->{$Mangled}
3571 and my $MnglNum = $Repl->{$Mangled}) {
3572 $Mangled = macro_mangle($MnglNum);
3573 }
3574 else
3575 {
3576 my @Repls = keys(%{$Repl});
3577 #@Repls = sort {$Repl->{$a}<=>$Repl->{$b}} @Repls;
3578 # FIXME: how to apply replacements? by num or by pos
3579 @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
3580 foreach my $MangledType (@Repls)
3581 {
3582 my $Replace = macro_mangle($Repl->{$MangledType});
3583 if($Mangled!~/$Replace/) {
3584 $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
3585 $Mangled=~s/\Q$MangledType\E/$Replace/g;
3586 }
3587 }
3588 }
3589 return $Mangled;
3590}
3591
3592sub delete_keywords($)
3593{
3594 my $TypeName = $_[0];
3595 $TypeName=~s/(\W|\A)(enum |struct |union |class )/$1/g;
3596 return $TypeName;
3597}
3598
3599my %Intrinsic_Keywords = map {$_=>1} (
3600 "true",
3601 "false",
3602 "_Bool",
3603 "_Complex",
3604 "const",
3605 "int",
3606 "long",
3607 "void",
3608 "short",
3609 "float",
3610 "volatile",
3611 "restrict",
3612 "unsigned",
3613 "signed",
3614 "char",
3615 "double",
3616 "class",
3617 "struct",
3618 "union",
3619 "enum"
3620);
3621
3622sub uncover_typedefs($$)
3623{
3624 my ($TypeName, $LibVersion) = @_;
3625 return "" if(not $TypeName);
3626 if(defined $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName}) {
3627 return $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName};
3628 }
3629 my ($TypeName_New, $TypeName_Pre) = (formatName($TypeName), "");
3630 while($TypeName_New ne $TypeName_Pre)
3631 {
3632 $TypeName_Pre = $TypeName_New;
3633 my $TypeName_Copy = $TypeName_New;
3634 my %Words = ();
3635 while($TypeName_Copy=~s/(\W|\A)([a-z_][\w:]*)(\W|\Z)//io)
3636 {
3637 my $Word = $2;
3638 next if(not $Word or $Intrinsic_Keywords{$Word});
3639 $Words{$Word} = 1;
3640 }
3641 foreach my $Word (keys(%Words))
3642 {
3643 my $BaseType_Name = $Typedef_BaseName{$LibVersion}{$Word};
3644 next if(not $BaseType_Name);
3645 next if($TypeName_New=~/(\A|\W)(struct|union|enum)\s\Q$Word\E(\W|\Z)/);
3646 if($BaseType_Name=~/\([\*]+\)/)
3647 { # FuncPtr
3648 if($TypeName_New=~/\Q$Word\E(.*)\Z/)
3649 {
3650 my $Type_Suffix = $1;
3651 $TypeName_New = $BaseType_Name;
3652 if($TypeName_New=~s/\(([\*]+)\)/($1 $Type_Suffix)/) {
3653 $TypeName_New = formatName($TypeName_New);
3654 }
3655 }
3656 }
3657 else
3658 {
3659 if($TypeName_New=~s/(\W|\A)\Q$Word\E(\W|\Z)/$1$BaseType_Name$2/g) {
3660 $TypeName_New = formatName($TypeName_New);
3661 }
3662 }
3663 }
3664 }
3665 return ($Cache{"uncover_typedefs"}{$LibVersion}{$TypeName} = $TypeName_New);
3666}
3667
3668sub isInternal($)
3669{
3670 my $FuncInfoId = $_[0];
3671 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
3672 return 0 if($FuncInfo!~/mngl[ ]*:[ ]*@(\d+) /);
3673 my $FuncMnglNameInfoId = $1;
3674 return ($LibInfo{$Version}{"info"}{$FuncMnglNameInfoId}=~/\*[ ]*INTERNAL[ ]*\*/);
3675}
3676
3677sub set_Class_And_Namespace($)
3678{
3679 my $InfoId = $_[0];
3680 my $FuncInfo = $LibInfo{$Version}{"info"}{$InfoId};
3681 if($FuncInfo=~/scpe[ ]*:[ ]*@(\d+) /)
3682 {
3683 my $NameSpaceInfoId = $1;
3684 if($LibInfo{$Version}{"info_type"}{$NameSpaceInfoId} eq "namespace_decl") {
3685 $SymbolInfo{$Version}{$InfoId}{"NameSpace"} = getNameSpace($InfoId);
3686 }
3687 elsif($LibInfo{$Version}{"info_type"}{$NameSpaceInfoId} eq "record_type") {
3688 $SymbolInfo{$Version}{$InfoId}{"Class"} = $NameSpaceInfoId;
3689 }
3690 }
3691 if($SymbolInfo{$Version}{$InfoId}{"Class"}
3692 or $SymbolInfo{$Version}{$InfoId}{"NameSpace"})
3693 { # identify language
3694 setLanguage($Version, "C++");
3695 }
3696}
3697
3698sub debugType($$)
3699{
3700 my ($Tid, $LibVersion) = @_;
3701 my %Type = get_Type($Tid_TDid{$LibVersion}{$Tid}, $Tid, $LibVersion);
3702 printMsg("INFO", Dumper(\%Type));
3703}
3704
3705sub debugMangling($)
3706{
3707 my $LibVersion = $_[0];
3708 my %Mangled = ();
3709 foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
3710 {
3711 if(my $Mngl = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
3712 {
3713 if($Mngl=~/\A(_Z|\?)/) {
3714 $Mangled{$Mngl}=$InfoId;
3715 }
3716 }
3717 }
3718 translateSymbols(keys(%Mangled), $LibVersion);
3719 foreach my $Mngl (keys(%Mangled))
3720 {
3721 my $Unmngl1 = modelUnmangled($Mangled{$Mngl}, "GCC");
3722 my $Unmngl2 = $tr_name{$Mngl};
3723 if($Unmngl1 ne $Unmngl2) {
3724 printMsg("INFO", "INCORRECT MANGLING:\n $Mngl\n $Unmngl1\n $Unmngl2\n");
3725 }
3726 }
3727}
3728
3729sub linkSymbol($)
3730{ # link symbols from shared libraries
3731 # with the symbols from header files
3732 my $InfoId = $_[0];
3733 if($SymbolInfo{$Version}{$InfoId}{"Lang"} eq "C")
3734 { # extern "C"
3735 return $SymbolInfo{$Version}{$InfoId}{"ShortName"};
3736 }
3737 # try to mangle symbol
3738 if((not check_gcc_version($GCC_PATH, "4") and $SymbolInfo{$Version}{$InfoId}{"Class"})
3739 or (check_gcc_version($GCC_PATH, "4") and not $SymbolInfo{$Version}{$InfoId}{"Class"}))
3740 { # 1. GCC 3.x doesn't mangle class methods names in the TU dump (only functions and global data)
3741 # 2. GCC 4.x doesn't mangle C++ functions in the TU dump (only class methods) except extern "C" functions
3742 if($CheckHeadersOnly)
3743 {
3744 if(my $Mangled = mangle_symbol($InfoId, $Version, "GCC")) {
3745 return $Mangled;
3746 }
3747 }
3748 else
3749 {
3750 if(my $Mangled = $mangled_name_gcc{modelUnmangled($InfoId, "GCC")}) {
3751 return correct_incharge($InfoId, $Version, $Mangled);
3752 }
3753 }
3754 }
3755 return "";
3756}
3757
3758sub setLanguage($$)
3759{
3760 my ($LibVersion, $Lang) = @_;
3761 if(not $UserLang) {
3762 $COMMON_LANGUAGE{$LibVersion} = $Lang;
3763 }
3764}
3765
3766sub getSymbolInfo($)
3767{
3768 my $FuncInfoId = $_[0];
3769 return if(isInternal($FuncInfoId));
3770 ($SymbolInfo{$Version}{$FuncInfoId}{"Header"}, $SymbolInfo{$Version}{$FuncInfoId}{"Line"}) = getLocation($FuncInfoId);
3771 if(not $SymbolInfo{$Version}{$FuncInfoId}{"Header"}
3772 or isBuiltIn($SymbolInfo{$Version}{$FuncInfoId}{"Header"})) {
3773 delete($SymbolInfo{$Version}{$FuncInfoId});
3774 return;
3775 }
3776 setFuncAccess($FuncInfoId);
3777 setFuncKind($FuncInfoId);
3778 if($SymbolInfo{$Version}{$FuncInfoId}{"PseudoTemplate"}) {
3779 delete($SymbolInfo{$Version}{$FuncInfoId});
3780 return;
3781 }
3782 $SymbolInfo{$Version}{$FuncInfoId}{"Type"} = getFuncType($FuncInfoId);
3783 $SymbolInfo{$Version}{$FuncInfoId}{"Return"} = getFuncReturn($FuncInfoId);
3784 if(my $AddedTid = $MissedTypedef{$Version}{$SymbolInfo{$Version}{$FuncInfoId}{"Return"}}{"Tid"}) {
3785 $SymbolInfo{$Version}{$FuncInfoId}{"Return"} = $AddedTid;
3786 }
3787 if(not $SymbolInfo{$Version}{$FuncInfoId}{"Return"}) {
3788 delete($SymbolInfo{$Version}{$FuncInfoId}{"Return"});
3789 }
3790 $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"} = getFuncShortName(getFuncOrig($FuncInfoId));
3791 if($SymbolInfo{$Version}{$FuncInfoId}{"ShortName"}=~/\._/) {
3792 delete($SymbolInfo{$Version}{$FuncInfoId});
3793 return;
3794 }
3795 if(defined $TemplateInstance_Func{$Version}{$FuncInfoId})
3796 {
3797 my @TParams = getTParams_Func($FuncInfoId);
3798 if(not @TParams) {
3799 delete($SymbolInfo{$Version}{$FuncInfoId});
3800 return;
3801 }
3802 my $PrmsInLine = join(", ", @TParams);
3803 $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"} .= "<".$PrmsInLine.">";
3804 $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"} = formatName($SymbolInfo{$Version}{$FuncInfoId}{"ShortName"});
3805 }
3806 else
3807 { # support for GCC 3.4
3808 $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"}=~s/<.+>\Z//;
3809 }
3810 $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"} = getFuncMnglName($FuncInfoId);
3811 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}
3812 and $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}!~/\A_Z/)
3813 {
3814 delete($SymbolInfo{$Version}{$FuncInfoId});
3815 return;
3816 }
3817 if($SymbolInfo{$FuncInfoId}{"MnglName"} and not $STDCXX_TESTING)
3818 { # stdc++ interfaces
3819 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}=~/\A(_ZS|_ZNS|_ZNKS)/) {
3820 delete($SymbolInfo{$Version}{$FuncInfoId});
3821 return;
3822 }
3823 }
3824 if(not $SymbolInfo{$Version}{$FuncInfoId}{"Destructor"})
3825 { # destructors have an empty parameter list
3826 my $Skip = setFuncParams($FuncInfoId);
3827 if($CheckHeadersOnly and $Skip)
3828 { # skip template symbols that cannot be
3829 # filtered without access to the library
3830 delete($SymbolInfo{$Version}{$FuncInfoId});
3831 return;
3832 }
3833 }
3834 set_Class_And_Namespace($FuncInfoId);
3835 if(not $CheckHeadersOnly and $SymbolInfo{$Version}{$FuncInfoId}{"Type"} eq "Function"
3836 and not $SymbolInfo{$Version}{$FuncInfoId}{"Class"}
3837 and link_symbol($SymbolInfo{$Version}{$FuncInfoId}{"ShortName"}, $Version, "-Deps"))
3838 { # functions (C++): not mangled in library, but are mangled in TU dump
3839 if(not $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}
3840 or not link_symbol($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}, $Version, "-Deps")) {
3841 $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"} = $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"};
3842 }
3843 }
3844 if($LibInfo{$Version}{"info"}{$FuncInfoId}=~/ lang:[ ]*C /i) {
3845 $SymbolInfo{$Version}{$FuncInfoId}{"Lang"} = "C";
3846 }
3847 if($UserLang eq "C")
3848 { # --lang=C option
3849 $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"} = $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"};
3850 }
3851 if($COMMON_LANGUAGE{$Version} eq "C++")
3852 { # correct mangled & short names
3853 if($SymbolInfo{$Version}{$FuncInfoId}{"ShortName"}=~/\A__(comp|base|deleting)_(c|d)tor\Z/)
3854 { # support for old GCC versions: reconstruct real names for constructors and destructors
3855 $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"}=getNameByInfo(getTypeDeclId($SymbolInfo{$Version}{$FuncInfoId}{"Class"}));
3856 $SymbolInfo{$Version}{$FuncInfoId}{"ShortName"}=~s/<.+>\Z//;
3857 }
3858 if(not $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"})
3859 { # try to mangle symbol (link with libraries)
3860 if(my $Mangled = linkSymbol($FuncInfoId)) {
3861 $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"} = $Mangled;
3862 }
3863 }
3864 if($OStarget eq "windows")
3865 { # link MS C++ symbols from library with GCC symbols from headers
3866 if(my $Mangled = $mangled_name{$Version}{modelUnmangled($FuncInfoId, "MSVC")})
3867 { # exported symbols
3868 $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"} = $Mangled;
3869 }
3870 elsif(my $Mangled = mangle_symbol($FuncInfoId, $Version, "MSVC"))
3871 { # pure virtual symbols
3872 $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"} = $Mangled;
3873 }
3874 }
3875 }
3876 if(not $SymbolInfo{$Version}{$FuncInfoId}{"MnglName"})
3877 { # can't detect symbol name
3878 delete($SymbolInfo{$Version}{$FuncInfoId});
3879 return;
3880 }
3881 if(getFuncSpec($FuncInfoId) eq "Virt")
3882 { # virtual methods
3883 $SymbolInfo{$Version}{$FuncInfoId}{"Virt"} = 1;
3884 }
3885 if(getFuncSpec($FuncInfoId) eq "PureVirt")
3886 { # pure virtual methods
3887 $SymbolInfo{$Version}{$FuncInfoId}{"PureVirt"} = 1;
3888 }
3889 if(isInline($FuncInfoId)) {
3890 $SymbolInfo{$Version}{$FuncInfoId}{"InLine"} = 1;
3891 }
3892 if($SymbolInfo{$Version}{$FuncInfoId}{"Constructor"}
3893 and my $ClassId = $SymbolInfo{$Version}{$FuncInfoId}{"Class"})
3894 {
3895 if(not $SymbolInfo{$Version}{$FuncInfoId}{"InLine"}
3896 and $LibInfo{$Version}{"info"}{$FuncInfoId}!~/ artificial /i)
3897 { # inline or auto-generated constructor
3898 delete($TypeInfo{$Version}{$Tid_TDid{$Version}{$ClassId}}{$ClassId}{"Copied"});
3899 }
3900 }
3901 if(not link_symbol($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}, $Version, "-Deps")
3902 and not $SymbolInfo{$Version}{$FuncInfoId}{"Virt"}
3903 and not $SymbolInfo{$Version}{$FuncInfoId}{"PureVirt"})
3904 { # removing src only and external non-virtual functions
3905 # non-virtual template instances going here
3906 if(not $CheckHeadersOnly) {
3907 delete($SymbolInfo{$Version}{$FuncInfoId});
3908 return;
3909 }
3910 }
3911 if($SymbolInfo{$Version}{$FuncInfoId}{"Type"} eq "Method"
3912 or $SymbolInfo{$Version}{$FuncInfoId}{"Constructor"}
3913 or $SymbolInfo{$Version}{$FuncInfoId}{"Destructor"}
3914 or $SymbolInfo{$Version}{$FuncInfoId}{"Class"})
3915 {
3916 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}!~/\A(_Z|\?)/) {
3917 delete($SymbolInfo{$Version}{$FuncInfoId});
3918 return;
3919 }
3920 }
3921 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"})
3922 {
3923 if($MangledNames{$Version}{$SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}})
3924 { # one instance for one mangled name only
3925 delete($SymbolInfo{$Version}{$FuncInfoId});
3926 return;
3927 }
3928 else {
3929 $MangledNames{$Version}{$SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}} = 1;
3930 }
3931 }
3932 if($SymbolInfo{$Version}{$FuncInfoId}{"Constructor"}
3933 or $SymbolInfo{$Version}{$FuncInfoId}{"Destructor"}) {
3934 delete($SymbolInfo{$Version}{$FuncInfoId}{"Return"});
3935 }
3936 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}=~/\A(_Z|\?)/
3937 and $SymbolInfo{$Version}{$FuncInfoId}{"Class"})
3938 {
3939 if($SymbolInfo{$Version}{$FuncInfoId}{"Type"} eq "Function")
3940 { # static methods
3941 $SymbolInfo{$Version}{$FuncInfoId}{"Static"} = 1;
3942 }
3943 }
3944 if(getFuncLink($FuncInfoId) eq "Static") {
3945 $SymbolInfo{$Version}{$FuncInfoId}{"Static"} = 1;
3946 }
3947 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}=~/\A(_Z|\?)/
3948 and $tr_name{$SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}}=~/\.\_\d/) {
3949 delete($SymbolInfo{$Version}{$FuncInfoId});
3950 return;
3951 }
3952 delete($SymbolInfo{$Version}{$FuncInfoId}{"Type"});
3953 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}=~/\A_ZN(V|)K/) {
3954 $SymbolInfo{$Version}{$FuncInfoId}{"Const"} = 1;
3955 }
3956 if($SymbolInfo{$Version}{$FuncInfoId}{"MnglName"}=~/\A_ZN(K|)V/) {
3957 $SymbolInfo{$Version}{$FuncInfoId}{"Volatile"} = 1;
3958 }
3959}
3960
3961sub isInline($)
3962{ # "body: undefined" in the tree
3963 # -fkeep-inline-functions GCC option should be specified
3964 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
3965 if($FuncInfo=~/ undefined /i) {
3966 return 0;
3967 }
3968 return 1;
3969}
3970
3971sub getTypeId($)
3972{
3973 if($LibInfo{$Version}{"info"}{$_[0]}=~/type[ ]*:[ ]*@(\d+) /) {
3974 return $1;
3975 }
3976 else {
3977 return "";
3978 }
3979}
3980
3981sub setTypeMemb($$)
3982{
3983 my ($TypeId, $TypeAttr) = @_;
3984 my $TypeType = $TypeAttr->{"Type"};
3985 my ($Position, $UnnamedPos) = (0, 0);
3986 if($TypeType eq "Enum")
3987 {
3988 my $TypeMembInfoId = getEnumMembInfoId($TypeId);
3989 while($TypeMembInfoId)
3990 {
3991 $TypeAttr->{"Memb"}{$Position}{"value"} = getEnumMembVal($TypeMembInfoId);
3992 my $MembName = getEnumMembName($TypeMembInfoId);
3993 $TypeAttr->{"Memb"}{$Position}{"name"} = getEnumMembName($TypeMembInfoId);
3994 $EnumMembName_Id{$Version}{getTreeAttr($TypeMembInfoId, "valu")} = ($TypeAttr->{"NameSpace"})?$TypeAttr->{"NameSpace"}."::".$MembName:$MembName;
3995 $TypeMembInfoId = getNextMembInfoId($TypeMembInfoId);
3996 $Position += 1;
3997 }
3998 }
3999 elsif($TypeType=~/\A(Struct|Class|Union)\Z/)
4000 {
4001 my $TypeMembInfoId = getStructMembInfoId($TypeId);
4002 while($TypeMembInfoId)
4003 {
4004 if($LibInfo{$Version}{"info_type"}{$TypeMembInfoId} ne "field_decl") {
4005 $TypeMembInfoId = getNextStructMembInfoId($TypeMembInfoId);
4006 next;
4007 }
4008 my $StructMembName = getStructMembName($TypeMembInfoId);
4009 if($StructMembName=~/_vptr\./)
4010 {# virtual tables
4011 $TypeMembInfoId = getNextStructMembInfoId($TypeMembInfoId);
4012 next;
4013 }
4014 if(not $StructMembName)
4015 { # unnamed fields
4016 if($TypeAttr->{"Name"}!~/_type_info_pseudo/)
4017 {
4018 my $UnnamedTid = getTreeAttr($TypeMembInfoId, "type");
4019 my $UnnamedTName = getNameByInfo(getTypeDeclId($UnnamedTid));
4020 if(isAnon($UnnamedTName))
4021 { # rename unnamed fields to unnamed0, unnamed1, ...
4022 $StructMembName = "unnamed".($UnnamedPos++);
4023 }
4024 }
4025 }
4026 if(not $StructMembName)
4027 { # unnamed fields and base classes
4028 $TypeMembInfoId = getNextStructMembInfoId($TypeMembInfoId);
4029 next;
4030 }
4031 my $MembTypeId = getTreeAttr($TypeMembInfoId, "type");
4032 if(my $AddedTid = $MissedTypedef{$Version}{$MembTypeId}{"Tid"}) {
4033 $MembTypeId = $AddedTid;
4034 }
4035 $TypeAttr->{"Memb"}{$Position}{"type"} = $MembTypeId;
4036 $TypeAttr->{"Memb"}{$Position}{"name"} = $StructMembName;
4037 if((my $Access = getTreeAccess($TypeMembInfoId)) ne "public")
4038 {# marked only protected and private, public by default
4039 $TypeAttr->{"Memb"}{$Position}{"access"} = $Access;
4040 }
4041 if(my $BFSize = getStructMembBitFieldSize($TypeMembInfoId)) {
4042 $TypeAttr->{"Memb"}{$Position}{"bitfield"} = $BFSize;
4043 }
4044 else
4045 { # set alignment for non-bit fields
4046 # alignment for bitfields is always equal to 1 bit
4047 $TypeAttr->{"Memb"}{$Position}{"algn"} = getAlgn($TypeMembInfoId)/$BYTE_SIZE;
4048 }
4049 $TypeMembInfoId = getNextStructMembInfoId($TypeMembInfoId);
4050 $Position += 1;
4051 }
4052 }
4053}
4054
4055sub setFuncParams($)
4056{
4057 my $FuncInfoId = $_[0];
4058 my $ParamInfoId = getFuncParamInfoId($FuncInfoId);
4059 my $FunctionType = getFuncType($FuncInfoId);
4060 if($FunctionType eq "Method")
4061 { # check type of "this" pointer
4062 my $ObjectTypeId = getFuncParamType($ParamInfoId);
4063 if(get_TypeName($ObjectTypeId, $Version)=~/(\A|\W)const(| volatile)\*const(\W|\Z)/) {
4064 $SymbolInfo{$Version}{$FuncInfoId}{"Const"} = 1;
4065 }
4066 if(get_TypeName($ObjectTypeId, $Version)=~/(\A|\W)volatile(\W|\Z)/) {
4067 $SymbolInfo{$Version}{$FuncInfoId}{"Volatile"} = 1;
4068 }
4069 $ParamInfoId = getNextElem($ParamInfoId);
4070 }
4071 my ($Position, $Vtt_Pos) = (0, -1);
4072 while($ParamInfoId)
4073 {
4074 my $ParamTypeId = getFuncParamType($ParamInfoId);
4075 my $ParamName = getFuncParamName($ParamInfoId);
4076 if($TypeInfo{$Version}{getTypeDeclId($ParamTypeId)}{$ParamTypeId}{"Name"} eq "void") {
4077 last;
4078 }
4079 if(my $AddedTid = $MissedTypedef{$Version}{$ParamTypeId}{"Tid"}) {
4080 $ParamTypeId = $AddedTid;
4081 }
4082 my $PType = get_TypeAttr($ParamTypeId, $Version, "Type");
4083 if(not $PType or $PType eq "Unknown") {
4084 return 1;
4085 }
4086 if($ParamName eq "__vtt_parm"
4087 and get_TypeName($ParamTypeId, $Version) eq "void const**")
4088 {
4089 $Vtt_Pos = $Position;
4090 $ParamInfoId = getNextElem($ParamInfoId);
4091 next;
4092 }
4093 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"type"} = $ParamTypeId;
4094 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"name"} = $ParamName;
4095 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"algn"} = getAlgn($ParamInfoId)/$BYTE_SIZE;
4096 if(not $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"name"}) {
4097 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"name"} = "p".($Position+1);
4098 }
4099 if($LibInfo{$Version}{"info"}{$ParamInfoId}=~/spec:\s*register /)
4100 { # foo(register type arg)
4101 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"reg"} = 1;
4102 }
4103 $ParamInfoId = getNextElem($ParamInfoId);
4104 $Position += 1;
4105 }
4106 if(detect_nolimit_args($FuncInfoId, $Vtt_Pos)) {
4107 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"type"} = -1;
4108 }
4109 return 0;
4110}
4111
4112sub detect_nolimit_args($$)
4113{
4114 my ($FuncInfoId, $Vtt_Pos) = @_;
4115 my $FuncTypeId = getFuncTypeId($FuncInfoId);
4116 my $ParamListElemId = getTreeAttr($FuncTypeId, "prms");
4117 if(getFuncType($FuncInfoId) eq "Method") {
4118 $ParamListElemId = getNextElem($ParamListElemId);
4119 }
4120 return 1 if(not $ParamListElemId);# foo(...)
4121 my $HaveVoid = 0;
4122 my $Position = 0;
4123 while($ParamListElemId)
4124 {
4125 if($Vtt_Pos!=-1 and $Position==$Vtt_Pos)
4126 {
4127 $Vtt_Pos=-1;
4128 $ParamListElemId = getNextElem($ParamListElemId);
4129 next;
4130 }
4131 my $ParamTypeId = getTreeAttr($ParamListElemId, "valu");
4132 if(my $PurpId = getTreeAttr($ParamListElemId, "purp"))
4133 {
4134 if($LibInfo{$Version}{"info_type"}{$PurpId} eq "integer_cst") {
4135 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"default"} = getTreeValue($PurpId);
4136 }
4137 elsif($LibInfo{$Version}{"info_type"}{$PurpId} eq "string_cst") {
4138 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"default"} = getNodeStrCst($PurpId);
4139 }
4140 }
4141 if($TypeInfo{$Version}{getTypeDeclId($ParamTypeId)}{$ParamTypeId}{"Name"} eq "void") {
4142 $HaveVoid = 1;
4143 last;
4144 }
4145 elsif(not defined $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"type"})
4146 {
4147 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"type"} = $ParamTypeId;
4148 if(not $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"name"}) {
4149 $SymbolInfo{$Version}{$FuncInfoId}{"Param"}{$Position}{"name"} = "p".($Position+1);
4150 }
4151 }
4152 $ParamListElemId = getNextElem($ParamListElemId);
4153 $Position += 1;
4154 }
4155 return ($Position>=1 and not $HaveVoid);
4156}
4157
4158sub getTreeAttr($$)
4159{
4160 my $Attr = $_[1];
4161 if($LibInfo{$Version}{"info"}{$_[0]}=~/\Q$Attr\E\s*:\s*\@(\d+) /) {
4162 return $1;
4163 }
4164 return "";
4165}
4166
4167sub getTreeValue($)
4168{
4169 if($LibInfo{$Version}{"info"}{$_[0]}=~/low[ ]*:[ ]*([^ ]+) /) {
4170 return $1;
4171 }
4172 return "";
4173}
4174
4175sub getTreeAccess($)
4176{
4177 my $InfoId = $_[0];
4178 if($LibInfo{$Version}{"info"}{$InfoId}=~/accs[ ]*:[ ]*([a-zA-Z]+) /)
4179 {
4180 my $Access = $1;
4181 if($Access eq "prot") {
4182 return "protected";
4183 }
4184 elsif($Access eq "priv") {
4185 return "private";
4186 }
4187 }
4188 elsif($LibInfo{$Version}{"info"}{$InfoId}=~/ protected /)
4189 {# support for old GCC versions
4190 return "protected";
4191 }
4192 elsif($LibInfo{$Version}{"info"}{$InfoId}=~/ private /)
4193 {# support for old GCC versions
4194 return "private";
4195 }
4196 return "public";
4197}
4198
4199sub setFuncAccess($)
4200{
4201 my $FuncInfoId = $_[0];
4202 my $Access = getTreeAccess($FuncInfoId);
4203 if($Access eq "protected") {
4204 $SymbolInfo{$Version}{$FuncInfoId}{"Protected"} = 1;
4205 }
4206 elsif($Access eq "private") {
4207 $SymbolInfo{$Version}{$FuncInfoId}{"Private"} = 1;
4208 }
4209}
4210
4211sub setTypeAccess($$)
4212{
4213 my ($TypeId, $TypeAttr) = @_;
4214 my $Access = getTreeAccess($TypeId);
4215 if($Access eq "protected") {
4216 $TypeAttr->{"Protected"} = 1;
4217 }
4218 elsif($Access eq "private") {
4219 $TypeAttr->{"Private"} = 1;
4220 }
4221}
4222
4223sub setFuncKind($)
4224{
4225 my $FuncInfoId = $_[0];
4226 if($LibInfo{$Version}{"info"}{$FuncInfoId}=~/pseudo tmpl/) {
4227 $SymbolInfo{$Version}{$FuncInfoId}{"PseudoTemplate"} = 1;
4228 }
4229 elsif($LibInfo{$Version}{"info"}{$FuncInfoId}=~/ constructor /) {
4230 $SymbolInfo{$Version}{$FuncInfoId}{"Constructor"} = 1;
4231 }
4232 elsif($LibInfo{$Version}{"info"}{$FuncInfoId}=~/ destructor /) {
4233 $SymbolInfo{$Version}{$FuncInfoId}{"Destructor"} = 1;
4234 }
4235}
4236
4237sub getFuncSpec($)
4238{
4239 my $FuncInfoId = $_[0];
4240 my $FuncInfo = $LibInfo{$Version}{"info"}{$FuncInfoId};
4241 if($FuncInfo=~/spec[ ]*:[ ]*pure /) {
4242 return "PureVirt";
4243 }
4244 elsif($FuncInfo=~/spec[ ]*:[ ]*virt /) {
4245 return "Virt";
4246 }
4247 elsif($FuncInfo=~/ pure\s+virtual /)
4248 {# support for old GCC versions
4249 return "PureVirt";
4250 }
4251 elsif($FuncInfo=~/ virtual /)
4252 {# support for old GCC versions
4253 return "Virt";
4254 }
4255 return "";
4256}
4257
4258sub getFuncClass($)
4259{
4260 my $FuncInfoId = $_[0];
4261 my $FuncInfo = $LibInfo{$Version}{"info"}{$FuncInfoId};
4262 if($FuncInfo=~/scpe[ ]*:[ ]*@(\d+) /) {
4263 return $1;
4264 }
4265 else {
4266 return "";
4267 }
4268}
4269
4270sub getFuncLink($)
4271{
4272 my $FuncInfoId = $_[0];
4273 my $FuncInfo = $LibInfo{$Version}{"info"}{$FuncInfoId};
4274 if($FuncInfo=~/link[ ]*:[ ]*static /) {
4275 return "Static";
4276 }
4277 else {
4278 if($FuncInfo=~/link[ ]*:[ ]*([a-zA-Z]+) /) {
4279 return $1;
4280 }
4281 else {
4282 return "";
4283 }
4284 }
4285}
4286
4287sub getNextElem($)
4288{
4289 my $FuncInfoId = $_[0];
4290 my $FuncInfo = $LibInfo{$Version}{"info"}{$FuncInfoId};
4291 if($FuncInfo=~/(chan|chain)[ ]*:[ ]*@(\d+) /) {
4292 return $2;
4293 }
4294 else {
4295 return "";
4296 }
4297}
4298
4299sub getFuncParamInfoId($)
4300{
4301 my $FuncInfoId = $_[0];
4302 my $FuncInfo = $LibInfo{$Version}{"info"}{$FuncInfoId};
4303 if($FuncInfo=~/args[ ]*:[ ]*@(\d+) /) {
4304 return $1;
4305 }
4306 else {
4307 return "";
4308 }
4309}
4310
4311sub getFuncParamType($)
4312{
4313 my $ParamInfoId = $_[0];
4314 my $ParamInfo = $LibInfo{$Version}{"info"}{$ParamInfoId};
4315 if($ParamInfo=~/type[ ]*:[ ]*@(\d+) /) {
4316 return $1;
4317 }
4318 else {
4319 return "";
4320 }
4321}
4322
4323sub getFuncParamName($)
4324{
4325 my $ParamInfoId = $_[0];
4326 my $ParamInfo = $LibInfo{$Version}{"info"}{$ParamInfoId};
4327 if($ParamInfo=~/name[ ]*:[ ]*@(\d+) /) {
4328 return getTreeStr($1);
4329 }
4330 return "";
4331}
4332
4333sub getEnumMembInfoId($)
4334{
4335 if($LibInfo{$Version}{"info"}{$_[0]}=~/csts[ ]*:[ ]*@(\d+) /) {
4336 return $1;
4337 }
4338 else {
4339 return "";
4340 }
4341}
4342
4343sub getStructMembInfoId($)
4344{
4345 if($LibInfo{$Version}{"info"}{$_[0]}=~/flds[ ]*:[ ]*@(\d+) /) {
4346 return $1;
4347 }
4348 else {
4349 return "";
4350 }
4351}
4352
4353sub get_IntNameSpace($$)
4354{
4355 my ($Interface, $LibVersion) = @_;
4356 return "" if(not $Interface or not $LibVersion);
4357 if(defined $Cache{"get_IntNameSpace"}{$Interface}{$LibVersion}) {
4358 return $Cache{"get_IntNameSpace"}{$Interface}{$LibVersion};
4359 }
4360 my $Signature = get_Signature($Interface, $LibVersion);
4361 if($Signature=~/\:\:/)
4362 {
4363 my $FounNameSpace = 0;
4364 foreach my $NameSpace (sort {get_depth($b)<=>get_depth($a)} keys(%{$NestedNameSpaces{$LibVersion}}))
4365 {
4366 if($Signature=~/(\A|\s+for\s+)\Q$NameSpace\E\:\:/) {
4367 return ($Cache{"get_IntNameSpace"}{$Interface}{$LibVersion} = $NameSpace);
4368 }
4369 }
4370 }
4371 else {
4372 return ($Cache{"get_IntNameSpace"}{$Interface}{$LibVersion} = "");
4373 }
4374}
4375
4376sub parse_TypeNameSpace($$)
4377{
4378 my ($TypeName, $LibVersion) = @_;
4379 return "" if(not $TypeName or not $LibVersion);
4380 if(defined $Cache{"parse_TypeNameSpace"}{$TypeName}{$LibVersion}) {
4381 return $Cache{"parse_TypeNameSpace"}{$TypeName}{$LibVersion};
4382 }
4383 if($TypeName=~/\:\:/)
4384 {
4385 my $FounNameSpace = 0;
4386 foreach my $NameSpace (sort {get_depth($b)<=>get_depth($a)} keys(%{$NestedNameSpaces{$LibVersion}}))
4387 {
4388 if($TypeName=~/\A\Q$NameSpace\E\:\:/) {
4389 return ($Cache{"parse_TypeNameSpace"}{$TypeName}{$LibVersion} = $NameSpace);
4390 }
4391 }
4392 }
4393 else {
4394 return ($Cache{"parse_TypeNameSpace"}{$TypeName}{$LibVersion} = "");
4395 }
4396}
4397
4398sub getNameSpace($)
4399{
4400 my $TypeInfoId = $_[0];
4401 my $NameSpaceInfoId = getTreeAttr($TypeInfoId, "scpe");
4402 return "" if(not $NameSpaceInfoId);
4403 if($LibInfo{$Version}{"info_type"}{$NameSpaceInfoId} eq "namespace_decl")
4404 {
4405 my $NameSpaceInfo = $LibInfo{$Version}{"info"}{$NameSpaceInfoId};
4406 if($NameSpaceInfo=~/name[ ]*:[ ]*@(\d+) /)
4407 {
4408 my $NameSpace = getTreeStr($1);
4409 return "" if($NameSpace eq "::");
4410 if(my $BaseNameSpace = getNameSpace($NameSpaceInfoId)) {
4411 $NameSpace = $BaseNameSpace."::".$NameSpace;
4412 }
4413 $NestedNameSpaces{$Version}{$NameSpace} = 1;
4414 return $NameSpace;
4415 }
4416 else {
4417 return "";
4418 }
4419 }
4420 elsif($LibInfo{$Version}{"info_type"}{$NameSpaceInfoId} eq "record_type")
4421 {# inside data type
4422 my ($Name, $NameNS) = getTrivialName(getTypeDeclId($NameSpaceInfoId), $NameSpaceInfoId);
4423 return $Name;
4424 }
4425 else {
4426 return "";
4427 }
4428}
4429
4430sub getNameSpaceId($)
4431{
4432 if($LibInfo{$Version}{"info"}{$_[0]}=~/scpe[ ]*:[ ]*\@(\d+)/) {
4433 return $1;
4434 }
4435 else {
4436 return "";
4437 }
4438}
4439
4440sub getEnumMembName($)
4441{
4442 if($LibInfo{$Version}{"info"}{$_[0]}=~/purp[ ]*:[ ]*\@(\d+)/) {
4443 return getTreeStr($1);
4444 }
4445 else {
4446 return "";
4447 }
4448}
4449
4450sub getStructMembName($)
4451{
4452 if($LibInfo{$Version}{"info"}{$_[0]}=~/name[ ]*:[ ]*\@(\d+)/) {
4453 return getTreeStr($1);
4454 }
4455 else {
4456 return "";
4457 }
4458}
4459
4460sub getEnumMembVal($)
4461{
4462 if($LibInfo{$Version}{"info"}{$_[0]}=~/valu[ ]*:[ ]*\@(\d+)/)
4463 {
4464 if($LibInfo{$Version}{"info"}{$1}=~/cnst[ ]*:[ ]*\@(\d+)/)
4465 {# in newer versions of GCC the value is in the "const_decl->cnst" node
4466 return getTreeValue($1);
4467 }
4468 else
4469 {# some old versions of GCC (3.3) have the value in the "integer_cst" node
4470 return getTreeValue($1);
4471 }
4472 }
4473 return "";
4474}
4475
4476sub getSize($)
4477{
4478 if($LibInfo{$Version}{"info"}{$_[0]}=~/size[ ]*:[ ]*\@(\d+)/) {
4479 return getTreeValue($1);
4480 }
4481 else {
4482 return 0;
4483 }
4484}
4485
4486sub getAlgn($)
4487{
4488 if($LibInfo{$Version}{"info"}{$_[0]}=~/algn[ ]*:[ ]*(\d+) /) {
4489 return $1;
4490 }
4491 else {
4492 return "";
4493 }
4494}
4495
4496sub getStructMembBitFieldSize($)
4497{
4498 if($LibInfo{$Version}{"info"}{$_[0]}=~/ bitfield /) {
4499 return getSize($_[0]);
4500 }
4501 else {
4502 return 0;
4503 }
4504}
4505
4506sub getNextMembInfoId($)
4507{
4508 if($LibInfo{$Version}{"info"}{$_[0]}=~/(chan|chain)[ ]*:[ ]*@(\d+) /) {
4509 return $2;
4510 }
4511 else {
4512 return "";
4513 }
4514}
4515
4516sub getNextStructMembInfoId($)
4517{
4518 if($LibInfo{$Version}{"info"}{$_[0]}=~/(chan|chain)[ ]*:[ ]*@(\d+) /) {
4519 return $2;
4520 }
4521 else {
4522 return "";
4523 }
4524}
4525
4526sub fieldHasName($)
4527{
4528 my $TypeMembInfoId = $_[0];
4529 if($LibInfo{$Version}{"info_type"}{$TypeMembInfoId} eq "field_decl")
4530 {
4531 if($LibInfo{$Version}{"info"}{$TypeMembInfoId}=~/name[ ]*:[ ]*@(\d+) /) {
4532 return $1;
4533 }
4534 else {
4535 return "";
4536 }
4537 }
4538 else {
4539 return 0;
4540 }
4541}
4542
4543sub register_header($$)
4544{ # input: header absolute path, relative path or name
4545 my ($Header, $LibVersion) = @_;
4546 return "" if(not $Header);
4547 if(is_abs($Header) and not -f $Header) {
4548 exitStatus("Access_Error", "can't access \'$Header\'");
4549 }
4550 return "" if(skip_header($Header, $LibVersion));
4551 my $Header_Path = identify_header($Header, $LibVersion);
4552 return "" if(not $Header_Path);
4553 detect_header_includes($Header_Path, $LibVersion);
4554 if(my $RHeader_Path = $Header_ErrorRedirect{$LibVersion}{$Header_Path})
4555 {
4556 return "" if(skip_header($RHeader_Path, $LibVersion));
4557 $Header_Path = $RHeader_Path;
4558 return "" if($Registered_Headers{$LibVersion}{$Header_Path}{"Identity"});
4559 }
4560 elsif($Header_ShouldNotBeUsed{$LibVersion}{$Header_Path}) {
4561 return "";
4562 }
4563 $Registered_Headers{$LibVersion}{$Header_Path}{"Identity"} = get_filename($Header_Path);
4564 $HeaderName_Paths{$LibVersion}{get_filename($Header_Path)}{$Header_Path} = 1;
4565 if(($Header=~/\.(\w+)\Z/ and $1 ne "h")
4566 or $Header!~/\.(\w+)\Z/)
4567 { # hpp, hh
4568 setLanguage($LibVersion, "C++");
4569 }
4570 if($CheckHeadersOnly
4571 and $Header=~/(\A|\/)c\+\+(\/|\Z)/)
4572 { # /usr/include/c++/4.6.1/...
4573 $STDCXX_TESTING = 1;
4574 }
4575 return $Header_Path;
4576}
4577
4578sub register_directory($$$)
4579{
4580 my ($Dir, $WithDeps, $LibVersion) = @_;
4581 $Dir=~s/[\/\\]+\Z//g;
4582 return if(not $LibVersion or not $Dir or not -d $Dir);
4583 return if(skip_header($Dir, $LibVersion));
4584 $Dir = get_abs_path($Dir);
4585 my $Mode = "All";
4586 if($WithDeps) {
4587 if($RegisteredDirs{$LibVersion}{$Dir}{1}) {
4588 return;
4589 }
4590 elsif($RegisteredDirs{$LibVersion}{$Dir}{0}) {
4591 $Mode = "DepsOnly";
4592 }
4593 }
4594 else {
4595 if($RegisteredDirs{$LibVersion}{$Dir}{1}
4596 or $RegisteredDirs{$LibVersion}{$Dir}{0}) {
4597 return;
4598 }
4599 }
4600 $Header_Dependency{$LibVersion}{$Dir} = 1;
4601 $RegisteredDirs{$LibVersion}{$Dir}{$WithDeps} = 1;
4602 if($Mode eq "DepsOnly")
4603 {
4604 foreach my $Path (cmd_find($Dir,"d","","")) {
4605 $Header_Dependency{$LibVersion}{$Path} = 1;
4606 }
4607 return;
4608 }
4609 foreach my $Path (sort {length($b)<=>length($a)} cmd_find($Dir,"f","",""))
4610 {
4611 if($WithDeps)
4612 {
4613 my $SubDir = $Path;
4614 while(($SubDir = get_dirname($SubDir)) ne $Dir)
4615 { # register all sub directories
4616 $Header_Dependency{$LibVersion}{$SubDir} = 1;
4617 }
4618 }
4619 next if(is_not_header($Path));
4620 next if(ignore_path($Path));
4621 next if(skip_header($Path, $LibVersion));
4622 # Neighbors
4623 foreach my $Part (get_path_prefixes($Path)) {
4624 $Include_Neighbors{$LibVersion}{$Part} = $Path;
4625 }
4626 }
4627 if(get_filename($Dir) eq "include")
4628 { # search for "lib/include/" directory
4629 my $LibDir = $Dir;
4630 if($LibDir=~s/([\/\\])include\Z/$1lib/g and -d $LibDir) {
4631 register_directory($LibDir, $WithDeps, $LibVersion);
4632 }
4633 }
4634}
4635
4636sub parse_redirect($$$)
4637{
4638 my ($Content, $Path, $LibVersion) = @_;
4639 if(defined $Cache{"parse_redirect"}{$LibVersion}{$Path}) {
4640 return $Cache{"parse_redirect"}{$LibVersion}{$Path};
4641 }
4642 return "" if(not $Content);
4643 my @Errors = ();
4644 while($Content=~s/#\s*error\s+([^\n]+?)\s*(\n|\Z)//) {
4645 push(@Errors, $1);
4646 }
4647 my $Redirect = "";
4648 foreach (@Errors)
4649 {
4650 s/\s{2,}/ /g;
4651 if(/(only|must\ include
4652 |update\ to\ include
4653 |replaced\ with
4654 |replaced\ by|renamed\ to
4655 |is\ in|use)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))/ix)
4656 {
4657 $Redirect = $2;
4658 last;
4659 }
4660 elsif(/(include|use|is\ in)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))\ instead/i)
4661 {
4662 $Redirect = $2;
4663 last;
4664 }
4665 elsif(/this\ header\ should\ not\ be\ used
4666 |programs\ should\ not\ directly\ include
4667 |you\ should\ not\ (include|be\ (including|using)\ this\ (file|header))
4668 |is\ not\ supported\ API\ for\ general\ use
4669 |do\ not\ use
4670 |should\ not\ be\ used
4671 |cannot\ be\ included\ directly/ix and not /\ from\ /i) {
4672 $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1;
4673 }
4674 }
4675 $Redirect=~s/\A<//g;
4676 $Redirect=~s/>\Z//g;
4677 return ($Cache{"parse_redirect"}{$LibVersion}{$Path} = $Redirect);
4678}
4679
4680sub parse_includes($$)
4681{
4682 my ($Content, $Path) = @_;
4683 my %Includes = ();
4684 while($Content=~s/#([ \t]*)(include|include_next|import)([ \t]*)(<|")([^<>"]+)(>|")//)
4685 {# C/C++: include, Objective C/C++: import directive
4686 my ($Header, $Method) = ($5, $4);
4687 $Header = path_format($Header, $OSgroup);
4688 if(($Method eq "\"" and -e joinPath(get_dirname($Path), $Header))
4689 or is_abs($Header)) {
4690 # include "path/header.h" that doesn't exist is equal to include <path/header.h>
4691 $Includes{$Header} = -1;
4692 }
4693 else {
4694 $Includes{$Header} = 1;
4695 }
4696 }
4697 return \%Includes;
4698}
4699
4700sub ignore_path($)
4701{
4702 my $Path = $_[0];
4703 if($Path=~/\~\Z/)
4704 {# skipping system backup files
4705 return 1;
4706 }
4707 if($Path=~/(\A|[\/\\]+)(\.(svn|git|bzr|hg)|CVS)([\/\\]+|\Z)/)
4708 {# skipping hidden .svn, .git, .bzr, .hg and CVS directories
4709 return 1;
4710 }
4711 return 0;
4712}
4713
4714sub sort_by_word($$)
4715{
4716 my ($ArrRef, $W) = @_;
4717 return if(length($W)<2);
4718 @{$ArrRef} = sort {get_filename($b)=~/\Q$W\E/i<=>get_filename($a)=~/\Q$W\E/i} @{$ArrRef};
4719}
4720
4721sub natural_sorting($$)
4722{
4723 my ($H1, $H2) = @_;
4724 $H1=~s/\.[a-z]+\Z//ig;
4725 $H2=~s/\.[a-z]+\Z//ig;
4726 my ($HDir1, $Hname1) = separate_path($H1);
4727 my ($HDir2, $Hname2) = separate_path($H2);
4728 my $Dirname1 = get_filename($HDir1);
4729 my $Dirname2 = get_filename($HDir2);
4730 if($H1 eq $H2) {
4731 return 0;
4732 }
4733 elsif($H1=~/\A\Q$H2\E/) {
4734 return 1;
4735 }
4736 elsif($H2=~/\A\Q$H1\E/) {
4737 return -1;
4738 }
4739 elsif($HDir1=~/\Q$Hname1\E/i
4740 and $HDir2!~/\Q$Hname2\E/i)
4741 {# include/glib-2.0/glib.h
4742 return -1;
4743 }
4744 elsif($HDir2=~/\Q$Hname2\E/i
4745 and $HDir1!~/\Q$Hname1\E/i)
4746 {# include/glib-2.0/glib.h
4747 return 1;
4748 }
4749 elsif($Hname1=~/\Q$Dirname1\E/i
4750 and $Hname2!~/\Q$Dirname2\E/i)
4751 {# include/hildon-thumbnail/hildon-thumbnail-factory.h
4752 return -1;
4753 }
4754 elsif($Hname2=~/\Q$Dirname2\E/i
4755 and $Hname1!~/\Q$Dirname1\E/i)
4756 {# include/hildon-thumbnail/hildon-thumbnail-factory.h
4757 return 1;
4758 }
4759 elsif($Hname1=~/(config|lib)/i
4760 and $Hname2!~/(config|lib)/i)
4761 {# include/alsa/asoundlib.h
4762 return -1;
4763 }
4764 elsif($Hname2=~/(config|lib)/i
4765 and $Hname1!~/(config|lib)/i)
4766 {# include/alsa/asoundlib.h
4767 return 1;
4768 }
4769 elsif(checkRelevance($H1)
4770 and not checkRelevance($H2))
4771 {# libebook/e-book.h
4772 return -1;
4773 }
4774 elsif(checkRelevance($H2)
4775 and not checkRelevance($H1))
4776 {# libebook/e-book.h
4777 return 1;
4778 }
4779 else {
4780 return (lc($H1) cmp lc($H2));
4781 }
4782}
4783
4784sub searchForHeaders($)
4785{
4786 my $LibVersion = $_[0];
4787 # gcc standard include paths
4788 find_gcc_cxx_headers($LibVersion);
4789 # processing header paths
4790 foreach my $Path (keys(%{$Descriptor{$LibVersion}{"IncludePaths"}}),
4791 keys(%{$Descriptor{$LibVersion}{"AddIncludePaths"}}))
4792 {
4793 my $IPath = $Path;
4794 if(not -e $Path) {
4795 exitStatus("Access_Error", "can't access \'$Path\'");
4796 }
4797 elsif(-f $Path) {
4798 exitStatus("Access_Error", "\'$Path\' - not a directory");
4799 }
4800 elsif(-d $Path)
4801 {
4802 $Path = get_abs_path($Path);
4803 register_directory($Path, 0, $LibVersion);
4804 if($Descriptor{$LibVersion}{"AddIncludePaths"}{$IPath}) {
4805 $Add_Include_Paths{$LibVersion}{$Path} = 1;
4806 }
4807 else {
4808 $Include_Paths{$LibVersion}{$Path} = 1;
4809 }
4810 }
4811 }
4812 if(keys(%{$Include_Paths{$LibVersion}})) {
4813 $INC_PATH_AUTODETECT{$LibVersion} = 0;
4814 }
4815 # registering directories
4816 foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
4817 {
4818 next if(not -e $Path);
4819 $Path = get_abs_path($Path);
4820 $Path = path_format($Path, $OSgroup);
4821 if(-d $Path) {
4822 register_directory($Path, 1, $LibVersion);
4823 }
4824 elsif(-f $Path)
4825 {
4826 my $Dir = get_dirname($Path);
4827 if(not $SystemPaths{"include"}{$Dir}
4828 and not $LocalIncludes{$Dir})
4829 {
4830 register_directory($Dir, 1, $LibVersion);
4831 if(my $OutDir = get_dirname($Dir))
4832 { # registering the outer directory
4833 if(not $SystemPaths{"include"}{$OutDir}
4834 and not $LocalIncludes{$OutDir}) {
4835 register_directory($OutDir, 0, $LibVersion);
4836 }
4837 }
4838 }
4839 }
4840 }
4841 # registering headers
4842 my $Position = 0;
4843 foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
4844 {
4845 if(is_abs($Dest) and not -e $Dest) {
4846 exitStatus("Access_Error", "can't access \'$Dest\'");
4847 }
4848 $Dest = path_format($Dest, $OSgroup);
4849 if(is_header($Dest, 1, $LibVersion))
4850 {
4851 if(my $HPath = register_header($Dest, $LibVersion)) {
4852 $Registered_Headers{$LibVersion}{$HPath}{"Pos"} = $Position++;
4853 }
4854 }
4855 elsif(-d $Dest)
4856 {
4857 my @Registered = ();
4858 foreach my $Path (cmd_find($Dest,"f","",""))
4859 {
4860 next if(ignore_path($Path));
4861 next if(not is_header($Path, 0, $LibVersion));
4862 if(my $HPath = register_header($Path, $LibVersion)) {
4863 push(@Registered, $HPath);
4864 }
4865 }
4866 @Registered = sort {natural_sorting($a, $b)} @Registered;
4867 sort_by_word(\@Registered, $TargetLibraryShortName);
4868 foreach my $Path (@Registered) {
4869 $Registered_Headers{$LibVersion}{$Path}{"Pos"} = $Position++;
4870 }
4871 }
4872 else {
4873 exitStatus("Access_Error", "can't identify \'$Dest\' as a header file");
4874 }
4875 }
4876 # preparing preamble headers
4877 my $Preamble_Position=0;
4878 foreach my $Header (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"IncludePreamble"}))
4879 {
4880 if(is_abs($Header) and not -f $Header) {
4881 exitStatus("Access_Error", "can't access file \'$Header\'");
4882 }
4883 $Header = path_format($Header, $OSgroup);
4884 if(my $Header_Path = is_header($Header, 1, $LibVersion))
4885 {
4886 next if(skip_header($Header_Path, $LibVersion));
4887 $Include_Preamble{$LibVersion}{$Header_Path}{"Position"} = $Preamble_Position++;
4888 }
4889 else {
4890 exitStatus("Access_Error", "can't identify \'$Header\' as a header file");
4891 }
4892 }
4893 foreach my $Header_Name (keys(%{$HeaderName_Paths{$LibVersion}}))
4894 { # set relative paths (for duplicates)
4895 if(keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})>=2)
4896 { # search for duplicates
4897 my $FirstPath = (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))[0];
4898 my $Prefix = get_dirname($FirstPath);
4899 while($Prefix=~/\A(.+)[\/\\]+[^\/\\]+\Z/)
4900 { # detect a shortest distinguishing prefix
4901 my $NewPrefix = $1;
4902 my %Identity = ();
4903 foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
4904 {
4905 if($Path=~/\A\Q$Prefix\E[\/\\]+(.*)\Z/) {
4906 $Identity{$Path} = $1;
4907 }
4908 }
4909 if(keys(%Identity)==keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
4910 { # all names are differend with current prefix
4911 foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})) {
4912 $Registered_Headers{$LibVersion}{$Path}{"Identity"} = $Identity{$Path};
4913 }
4914 last;
4915 }
4916 $Prefix = $NewPrefix; # increase prefix
4917 }
4918 }
4919 }
4920 foreach my $HeaderName (keys(%{$Include_Order{$LibVersion}}))
4921 { # ordering headers according to descriptor
4922 my $PairName=$Include_Order{$LibVersion}{$HeaderName};
4923 my ($Pos, $PairPos) = (-1, -1);
4924 my ($Path, $PairPath) = ();
4925 my @Paths = keys(%{$Registered_Headers{$LibVersion}});
4926 @Paths = sort {int($Registered_Headers{$LibVersion}{$a}{"Pos"})<=>int($Registered_Headers{$LibVersion}{$b}{"Pos"})} @Paths;
4927 foreach my $Header_Path (@Paths)
4928 {
4929 if(get_filename($Header_Path) eq $PairName)
4930 {
4931 $PairPos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
4932 $PairPath = $Header_Path;
4933 }
4934 if(get_filename($Header_Path) eq $HeaderName)
4935 {
4936 $Pos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
4937 $Path = $Header_Path;
4938 }
4939 }
4940 if($PairPos!=-1 and $Pos!=-1
4941 and int($PairPos)<int($Pos))
4942 {
4943 my %Tmp = %{$Registered_Headers{$LibVersion}{$Path}};
4944 %{$Registered_Headers{$LibVersion}{$Path}} = %{$Registered_Headers{$LibVersion}{$PairPath}};
4945 %{$Registered_Headers{$LibVersion}{$PairPath}} = %Tmp;
4946 }
4947 }
4948 if(not keys(%{$Registered_Headers{$LibVersion}})) {
4949 exitStatus("Error", "header files are not found in the ".$Descriptor{$LibVersion}{"Version"});
4950 }
4951}
4952
4953sub detect_real_includes($$)
4954{
4955 my ($AbsPath, $LibVersion) = @_;
4956 return () if(not $LibVersion or not $AbsPath or not -e $AbsPath);
4957 if($Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}
4958 or keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
4959 return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
4960 }
4961 my $Path = callPreprocessor($AbsPath, "", $LibVersion);
4962 return () if(not $Path);
4963 open(PREPROC, $Path);
4964 while(<PREPROC>)
4965 {
4966 if(/#\s+\d+\s+"([^"]+)"[\s\d]*\n/)
4967 {
4968 my $Include = path_format($1, $OSgroup);
4969 if($Include=~/\<(built\-in|internal|command(\-|\s)line)\>|\A\./) {
4970 next;
4971 }
4972 if($Include eq $AbsPath) {
4973 next;
4974 }
4975 $RecursiveIncludes{$LibVersion}{$AbsPath}{$Include} = 1;
4976 }
4977 }
4978 close(PREPROC);
4979 $Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}=1;
4980 return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
4981}
4982
4983sub detect_header_includes($$)
4984{
4985 my ($Path, $LibVersion) = @_;
4986 return if(not $LibVersion or not $Path or not -e $Path);
4987 return if($Cache{"detect_header_includes"}{$LibVersion}{$Path});
4988 my $Content = readFile($Path);
4989 if($Content=~/#[ \t]*error[ \t]+/ and (my $Redirect = parse_redirect($Content, $Path, $LibVersion)))
4990 {# detecting error directive in the headers
4991 if(my $RedirectPath = identify_header($Redirect, $LibVersion))
4992 {
4993 if($RedirectPath=~/\/usr\/include\// and $Path!~/\/usr\/include\//) {
4994 $RedirectPath = identify_header(get_filename($Redirect), $LibVersion);
4995 }
4996 if($RedirectPath ne $Path) {
4997 $Header_ErrorRedirect{$LibVersion}{$Path} = $RedirectPath;
4998 }
4999 }
5000 }
5001 my $Inc = parse_includes($Content, $Path);
5002 foreach my $Include (keys(%{$Inc}))
5003 {# detecting includes
5004 #if(is_not_header($Include))
5005 #{ #include<*.c> and others
5006 # next;
5007 #}
5008 $Header_Includes{$LibVersion}{$Path}{$Include} = $Inc->{$Include};
5009 }
5010 $Cache{"detect_header_includes"}{$LibVersion}{$Path} = 1;
5011}
5012
5013sub simplify_path($)
5014{
5015 my $Path = $_[0];
5016 while($Path=~s&([\/\\])[^\/\\]+[\/\\]\.\.[\/\\]&$1&){};
5017 return $Path;
5018}
5019
5020sub fromLibc($)
5021{ # GLIBC header
5022 my $Path = $_[0];
5023 my ($Dir, $Name) = separate_path($Path);
5024 if(get_filename($Dir)=~/\A(include|libc)\Z/ and $GlibcHeader{$Name})
5025 { # /usr/include/{stdio.h, ...}
5026 # epoc32/include/libc/{stdio.h, ...}
5027 return 1;
5028 }
5029 if(isLibcDir($Dir)) {
5030 return 1;
5031 }
5032 return 0;
5033}
5034
5035sub isLibcDir($)
5036{ # GLIBC directory
5037 my $Dir = $_[0];
5038 my ($OutDir, $Name) = separate_path($Dir);
5039 if(get_filename($OutDir)=~/\A(include|libc)\Z/
5040 and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
5041 { # /usr/include/{sys,bits,asm,asm-*}/*.h
5042 return 1;
5043 }
5044 return 0;
5045}
5046
5047sub detect_recursive_includes($$)
5048{
5049 my ($AbsPath, $LibVersion) = @_;
5050 return () if(not $AbsPath);
5051 if(isCyclical(\@RecurInclude, $AbsPath)) {
5052 return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
5053 }
5054 my ($AbsDir, $Name) = separate_path($AbsPath);
5055 if(isLibcDir($AbsDir))
5056 { # GLIBC internals
5057 return ();
5058 }
5059 if(keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
5060 return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
5061 }
5062 return () if($OSgroup ne "windows" and $Name=~/windows|win32|win64/i);
5063 return () if($MAIN_CPP_DIR and $AbsPath=~/\A\Q$MAIN_CPP_DIR\E/ and not $STDCXX_TESTING);
5064 push(@RecurInclude, $AbsPath);
5065 if($DefaultGccPaths{$AbsDir}
5066 or fromLibc($AbsPath))
5067 { # check "real" (non-"model") include paths
5068 my @Paths = detect_real_includes($AbsPath, $LibVersion);
5069 pop(@RecurInclude);
5070 return @Paths;
5071 }
5072 if(not keys(%{$Header_Includes{$LibVersion}{$AbsPath}})) {
5073 detect_header_includes($AbsPath, $LibVersion);
5074 }
5075 foreach my $Include (keys(%{$Header_Includes{$LibVersion}{$AbsPath}}))
5076 {
5077 my $HPath = "";
5078 if($Header_Includes{$LibVersion}{$AbsPath}{$Include}==-1)
5079 { # for #include "..."
5080 my $Candidate = joinPath($AbsDir, $Include);
5081 if(-f $Candidate) {
5082 $HPath = simplify_path($Candidate);
5083 }
5084 }
5085 elsif($Header_Includes{$LibVersion}{$AbsPath}{$Include}==1
5086 and $Include=~/[\/\\]/ and not find_in_defaults($Include))
5087 { # search for the nearest header
5088 # QtCore/qabstractanimation.h includes <QtCore/qobject.h>
5089 my $Candidate = joinPath(get_dirname($AbsDir), $Include);
5090 if(-f $Candidate) {
5091 $HPath = $Candidate;
5092 }
5093 }
5094 if(not $HPath) {
5095 $HPath = identify_header($Include, $LibVersion);
5096 }
5097 next if(not $HPath);
5098 if($HPath eq $AbsPath) {
5099 next;
5100 }
5101 $RecursiveIncludes{$LibVersion}{$AbsPath}{$HPath} = 1;
5102 if($Header_Includes{$LibVersion}{$AbsPath}{$Include}==1)
5103 { # only include <...>, skip include "..." prefixes
5104 $Header_Include_Prefix{$LibVersion}{$AbsPath}{$HPath}{get_dirname($Include)} = 1;
5105 }
5106 foreach my $IncPath (detect_recursive_includes($HPath, $LibVersion))
5107 {
5108 if($IncPath eq $AbsPath) {
5109 next;
5110 }
5111 $RecursiveIncludes{$LibVersion}{$AbsPath}{$IncPath} = 1;
5112 foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$HPath}{$IncPath}})) {
5113 $Header_Include_Prefix{$LibVersion}{$AbsPath}{$IncPath}{$Prefix} = 1;
5114 }
5115 }
5116 foreach my $Dep (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}}))
5117 {
5118 if($GlibcHeader{get_filename($Dep)} and keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}})>=2
5119 and defined $Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""})
5120 { # distinguish math.h from glibc and math.h from the tested library
5121 delete($Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""});
5122 last;
5123 }
5124 }
5125 }
5126 pop(@RecurInclude);
5127 return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
5128}
5129
5130sub find_in_framework($$$)
5131{
5132 my ($Header, $Framework, $LibVersion) = @_;
5133 return "" if(not $Header or not $Framework or not $LibVersion);
5134 if(defined $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header}) {
5135 return $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header};
5136 }
5137 foreach my $Dependency (sort {get_depth($a)<=>get_depth($b)} keys(%{$Header_Dependency{$LibVersion}}))
5138 {
5139 if(get_filename($Dependency) eq $Framework
5140 and -f get_dirname($Dependency)."/".$Header) {
5141 return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = get_dirname($Dependency));
5142 }
5143 }
5144 return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = "");
5145}
5146
5147sub find_in_defaults($)
5148{
5149 my $Header = $_[0];
5150 return "" if(not $Header);
5151 if(defined $Cache{"find_in_defaults"}{$Header}) {
5152 return $Cache{"find_in_defaults"}{$Header};
5153 }
5154 foreach my $Dir (sort {get_depth($a)<=>get_depth($b)}
5155 (keys(%DefaultIncPaths), keys(%DefaultGccPaths), keys(%DefaultCppPaths), keys(%UserIncPath)))
5156 {
5157 next if(not $Dir);
5158 if(-f $Dir."/".$Header) {
5159 return ($Cache{"find_in_defaults"}{$Header}=$Dir);
5160 }
5161 }
5162 return ($Cache{"find_in_defaults"}{$Header}="");
5163}
5164
5165sub cmp_paths($$)
5166{
5167 my ($Path1, $Path2) = @_;
5168 my @Parts1 = split(/[\/\\]/, $Path1);
5169 my @Parts2 = split(/[\/\\]/, $Path2);
5170 foreach my $Num (0 .. $#Parts1)
5171 {
5172 my $Part1 = $Parts1[$Num];
5173 my $Part2 = $Parts2[$Num];
5174 if($GlibcDir{$Part1}
5175 and not $GlibcDir{$Part2}) {
5176 return 1;
5177 }
5178 elsif($GlibcDir{$Part2}
5179 and not $GlibcDir{$Part1}) {
5180 return -1;
5181 }
5182 elsif($Part1=~/glib/
5183 and $Part2!~/glib/) {
5184 return 1;
5185 }
5186 elsif($Part1!~/glib/
5187 and $Part2=~/glib/) {
5188 return -1;
5189 }
5190 elsif(my $CmpRes = ($Part1 cmp $Part2)) {
5191 return $CmpRes;
5192 }
5193 }
5194 return 0;
5195}
5196
5197sub checkRelevance($)
5198{
5199 my ($Path) = @_;
5200 return 0 if(not $Path);
5201 if($SystemRoot) {
5202 $Path=~s/\A\Q$SystemRoot\E//g;
5203 }
5204 my ($Dir, $Name) = separate_path($Path);
5205 $Name=~s/\.\w+\Z//g;# remove extension (.h)
5206 my @Tokens = split(/[_\d\W]+/, $Name);
5207 foreach (@Tokens)
5208 {
5209 next if(not $_);
5210 if($Dir=~/(\A|lib|[_\d\W])\Q$_\E([_\d\W]|lib|\Z)/i
5211 or length($_)>=4 and $Dir=~/\Q$_\E/i)
5212 { # include/gupnp-1.0/libgupnp/gupnp-context.h
5213 # include/evolution-data-server-1.4/libebook/e-book.h
5214 return 1;
5215 }
5216 }
5217 return 0;
5218}
5219
5220sub checkFamily(@)
5221{
5222 my @Paths = @_;
5223 return 1 if($#Paths<=0);
5224 my %Prefix = ();
5225 foreach my $Path (@Paths)
5226 {
5227 if($SystemRoot) {
5228 $Path = cut_path_prefix($Path, $SystemRoot);
5229 }
5230 if(my $Dir = get_dirname($Path))
5231 {
5232 $Dir=~s/(\/[^\/]+?)[\d\.\-\_]+\Z/$1/g; # remove version suffix
5233 $Prefix{$Dir} += 1;
5234 $Prefix{get_dirname($Dir)} += 1;
5235 }
5236 }
5237 foreach (sort keys(%Prefix))
5238 {
5239 if(get_depth($_)>=3
5240 and $Prefix{$_}==$#Paths+1) {
5241 return 1;
5242 }
5243 }
5244 return 0;
5245}
5246
5247sub isAcceptable($$$)
5248{
5249 my ($Header, $Candidate, $LibVersion) = @_;
5250 my $HName = get_filename($Header);
5251 if(get_dirname($Header))
5252 { # with prefix
5253 return 1;
5254 }
5255 if($HName=~/config|setup/i and $Candidate=~/[\/\\]lib\d*[\/\\]/)
5256 { # allow to search for glibconfig.h in /usr/lib/glib-2.0/include/
5257 return 1;
5258 }
5259 if(checkRelevance($Candidate))
5260 { # allow to search for atk.h in /usr/include/atk-1.0/atk/
5261 return 1;
5262 }
5263 if(checkFamily(getSystemHeaders($HName, $LibVersion)))
5264 { # /usr/include/qt4/QtNetwork/qsslconfiguration.h
5265 # /usr/include/qt4/Qt/qsslconfiguration.h
5266 return 1;
5267 }
5268 if($OStarget eq "symbian")
5269 {
5270 if($Candidate=~/[\/\\]stdapis[\/\\]/) {
5271 return 1;
5272 }
5273 }
5274 return 0;
5275}
5276
5277sub isRelevant($$$)
5278{ # disallow to search for "abstract" headers in too deep directories
5279 my ($Header, $Candidate, $LibVersion) = @_;
5280 my $HName = get_filename($Header);
5281 if($OStarget eq "symbian")
5282 {
5283 if($Candidate=~/[\/\\](tools|stlportv5)[\/\\]/) {
5284 return 0;
5285 }
5286 }
5287 if($OStarget ne "bsd") {
5288 if($Candidate=~/[\/\\]include[\/\\]bsd[\/\\]/)
5289 { # openssh: skip /usr/lib/bcc/include/bsd/signal.h
5290 return 0;
5291 }
5292 }
5293 if(not get_dirname($Header)
5294 and $Candidate=~/[\/\\]wx[\/\\]/)
5295 { # do NOT search in system /wx/ directory
5296 # for headers without a prefix: sstream.h
5297 return 0;
5298 }
5299 if($Candidate=~/c\+\+[\/\\]\d+/ and $MAIN_CPP_DIR
5300 and $Candidate!~/\A\Q$MAIN_CPP_DIR\E/)
5301 { # skip ../c++/3.3.3/ if using ../c++/4.5/
5302 return 0;
5303 }
5304 if($Candidate=~/[\/\\]asm-/
5305 and (my $Arch = getArch($LibVersion)) ne "unknown")
5306 { # arch-specific header files
5307 if($Candidate!~/[\/\\]asm-\Q$Arch\E/)
5308 {# skip ../asm-arm/ if using x86 architecture
5309 return 0;
5310 }
5311 }
5312 my @Candidates = getSystemHeaders($HName, $LibVersion);
5313 if($#Candidates==1)
5314 { # unique header
5315 return 1;
5316 }
5317 my @SCandidates = getSystemHeaders($Header, $LibVersion);
5318 if($#SCandidates==1)
5319 { # unique name
5320 return 1;
5321 }
5322 my $SystemDepth = $SystemRoot?get_depth($SystemRoot):0;
5323 if(get_depth($Candidate)-$SystemDepth>=5)
5324 { # abstract headers in too deep directories
5325 # sstream.h or typeinfo.h in /usr/include/wx-2.9/wx/
5326 if(not isAcceptable($Header, $Candidate, $LibVersion)) {
5327 return 0;
5328 }
5329 }
5330 if($Header eq "parser.h"
5331 and $Candidate!~/\/libxml2\//)
5332 { # select parser.h from xml2 library
5333 return 0;
5334 }
5335 if(not get_dirname($Header)
5336 and keys(%{$SystemHeaders{$HName}})>=3)
5337 { # many headers with the same name
5338 # like thread.h included without a prefix
5339 if(not checkFamily(@Candidates)) {
5340 return 0;
5341 }
5342 }
5343 return 1;
5344}
5345
5346sub selectSystemHeader($$)
5347{
5348 my ($Header, $LibVersion) = @_;
5349 return $Header if(-f $Header);
5350 return "" if(is_abs($Header) and not -f $Header);
5351 return "" if($Header=~/\A(atomic|config|configure|build|conf|setup)\.h\Z/i);
5352 if($OSgroup ne "windows")
5353 {
5354 if(get_filename($Header)=~/windows|win32|win64|\A(dos|process|winsock|config-win)\.h\Z/i) {
5355 return "";
5356 }
5357 elsif($Header=~/\A(mem)\.h\Z/)
5358 { # pngconf.h include mem.h for __MSDOS__
5359 return "";
5360 }
5361 }
5362 if($OSgroup ne "solaris")
5363 {
5364 if($Header=~/\A(thread)\.h\Z/)
5365 { # thread.h in Solaris
5366 return "";
5367 }
5368 }
5369 if(defined $Cache{"selectSystemHeader"}{$LibVersion}{$Header}) {
5370 return $Cache{"selectSystemHeader"}{$LibVersion}{$Header};
5371 }
5372 foreach my $Path (keys(%{$SystemPaths{"include"}}))
5373 { # search in default paths
5374 if(-f $Path."/".$Header) {
5375 return ($Cache{"selectSystemHeader"}{$LibVersion}{$Header} = joinPath($Path,$Header));
5376 }
5377 }
5378 if(not keys(%SystemHeaders)) {
5379 detectSystemHeaders();
5380 }
5381 foreach my $Candidate (sort {get_depth($a)<=>get_depth($b)}
5382 sort {cmp_paths($b, $a)} getSystemHeaders($Header, $LibVersion))
5383 {
5384 if(isRelevant($Header, $Candidate, $LibVersion)) {
5385 return ($Cache{"selectSystemHeader"}{$LibVersion}{$Header} = $Candidate);
5386 }
5387 }
5388 return ($Cache{"selectSystemHeader"}{$LibVersion}{$Header} = ""); # error
5389}
5390
5391sub getSystemHeaders($$)
5392{
5393 my ($Header, $LibVersion) = @_;
5394 my @Candidates = ();
5395 foreach my $Candidate (sort keys(%{$SystemHeaders{$Header}}))
5396 {
5397 if(skip_header($Candidate, $LibVersion)) {
5398 next;
5399 }
5400 push(@Candidates, $Candidate);
5401 }
5402 return @Candidates;
5403}
5404
5405sub cut_path_prefix($$)
5406{
5407 my ($Path, $Prefix) = @_;
5408 return $Path if(not $Prefix);
5409 $Prefix=~s/[\/\\]+\Z//;
5410 $Path=~s/\A\Q$Prefix\E([\/\\]+|\Z)//;
5411 return $Path;
5412}
5413
5414sub is_default_include_dir($)
5415{
5416 my $Dir = $_[0];
5417 $Dir=~s/[\/\\]+\Z//;
5418 return ($DefaultGccPaths{$Dir} or $DefaultCppPaths{$Dir} or $DefaultIncPaths{$Dir});
5419}
5420
5421sub identify_header($$)
5422{
5423 my ($Header, $LibVersion) = @_;
5424 $Header=~s/\A(\.\.[\\\/])+//g;
5425 if(defined $Cache{"identify_header"}{$Header}{$LibVersion}) {
5426 return $Cache{"identify_header"}{$Header}{$LibVersion};
5427 }
5428 my $Path = identify_header_internal($Header, $LibVersion);
5429 if(not $Path and $OSgroup eq "macos" and my $Dir = get_dirname($Header))
5430 { # search in frameworks: "OpenGL/gl.h" is "OpenGL.framework/Headers/gl.h"
5431 my $RelPath = "Headers\/".get_filename($Header);
5432 if(my $HeaderDir = find_in_framework($RelPath, $Dir.".framework", $LibVersion)) {
5433 $Path = joinPath($HeaderDir, $RelPath);
5434 }
5435 }
5436 return ($Cache{"identify_header"}{$Header}{$LibVersion} = $Path);
5437}
5438
5439sub identify_header_internal($$)
5440{ # search for header by absolute path, relative path or name
5441 my ($Header, $LibVersion) = @_;
5442 return "" if(not $Header);
5443 if(-f $Header)
5444 { # it's relative or absolute path
5445 return get_abs_path($Header);
5446 }
5447 elsif($GlibcHeader{$Header} and not $GLIBC_TESTING
5448 and my $HeaderDir = find_in_defaults($Header))
5449 { # search for libc headers in the /usr/include
5450 # for non-libc target library before searching
5451 # in the library paths
5452 return joinPath($HeaderDir,$Header);
5453 }
5454 elsif(my $Path = $Include_Neighbors{$LibVersion}{$Header})
5455 { # search in the target library paths
5456 return $Path;
5457 }
5458 elsif($DefaultGccHeader{$Header})
5459 { # search in the internal GCC include paths
5460 return $DefaultGccHeader{$Header};
5461 }
5462 elsif(my $HeaderDir = find_in_defaults($Header))
5463 { # search in the default GCC include paths
5464 return joinPath($HeaderDir,$Header);
5465 }
5466 elsif($DefaultCppHeader{$Header})
5467 { # search in the default G++ include paths
5468 return $DefaultCppHeader{$Header};
5469 }
5470 elsif(my $AnyPath = selectSystemHeader($Header, $LibVersion))
5471 { # search everywhere in the system
5472 return $AnyPath;
5473 }
5474 else
5475 { # cannot find anything
5476 return "";
5477 }
5478}
5479
5480sub getLocation($)
5481{
5482 if($LibInfo{$Version}{"info"}{$_[0]}=~/srcp[ ]*:[ ]*([\w\-\<\>\.\+\/\\]+):(\d+) /) {
5483 return ($1, $2);
5484 }
5485 else {
5486 return ();
5487 }
5488}
5489
5490sub getTypeTypeByTypeId($)
5491{
5492 my $TypeId = $_[0];
5493 my $TypeType = $LibInfo{$Version}{"info_type"}{$TypeId};
5494 if($TypeType=~/integer_type|real_type|boolean_type|void_type|complex_type/)
5495 {
5496 return "Intrinsic";
5497 }
5498 elsif(isFuncPtr($TypeId)) {
5499 return "FuncPtr";
5500 }
5501 elsif(isMethodPtr($TypeId)) {
5502 return "MethodPtr";
5503 }
5504 elsif(isFieldPtr($TypeId)) {
5505 return "FieldPtr";
5506 }
5507 elsif($TypeType eq "pointer_type") {
5508 return "Pointer";
5509 }
5510 elsif($TypeType eq "reference_type") {
5511 return "Ref";
5512 }
5513 elsif($TypeType eq "union_type") {
5514 return "Union";
5515 }
5516 elsif($TypeType eq "enumeral_type") {
5517 return "Enum";
5518 }
5519 elsif($TypeType eq "record_type") {
5520 return "Struct";
5521 }
5522 elsif($TypeType eq "array_type") {
5523 return "Array";
5524 }
5525 elsif($TypeType eq "complex_type") {
5526 return "Intrinsic";
5527 }
5528 elsif($TypeType eq "function_type") {
5529 return "FunctionType";
5530 }
5531 elsif($TypeType eq "method_type") {
5532 return "MethodType";
5533 }
5534 else {
5535 return "Unknown";
5536 }
5537}
5538
5539sub getNameByInfo($)
5540{
5541 return "" if($LibInfo{$Version}{"info"}{$_[0]}!~/name[ ]*:[ ]*@(\d+) /);
5542 if($LibInfo{$Version}{"info"}{$1}=~/strg[ ]*:[ ]*(.*?)[ ]+lngt/)
5543 {# short unsigned int (may include spaces)
5544 return $1;
5545 }
5546 else {
5547 return "";
5548 }
5549}
5550
5551sub getTreeStr($)
5552{
5553 my $Info = $LibInfo{$Version}{"info"}{$_[0]};
5554 if($Info=~/strg[ ]*:[ ]*([^ ]*)/)
5555 {
5556 my $Str = $1;
5557 if($C99Mode{$Version}
5558 and $Str=~/\Ac99_(.+)\Z/) {
5559 if($CppKeywords_A{$1}) {
5560 $Str=$1;
5561 }
5562 }
5563 return $Str;
5564 }
5565 else {
5566 return "";
5567 }
5568}
5569
5570sub getVarShortName($)
5571{
5572 my $VarInfo = $LibInfo{$Version}{"info"}{$_[0]};
5573 return "" if($VarInfo!~/name[ ]*:[ ]*@(\d+) /);
5574 return getTreeStr($1);
5575}
5576
5577sub getFuncShortName($)
5578{
5579 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
5580 if($FuncInfo=~/ operator /)
5581 {
5582 if($FuncInfo=~/ conversion /) {
5583 return "operator ".get_TypeName($SymbolInfo{$Version}{$_[0]}{"Return"}, $Version);
5584 }
5585 else
5586 {
5587 return "" if($FuncInfo!~/ operator[ ]+([a-zA-Z]+) /);
5588 return "operator".$Operator_Indication{$1};
5589 }
5590 }
5591 else
5592 {
5593 return "" if($FuncInfo!~/name[ ]*:[ ]*@(\d+) /);
5594 return getTreeStr($1);
5595 }
5596}
5597
5598sub getFuncMnglName($)
5599{
5600 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
5601 return "" if($FuncInfo!~/mngl[ ]*:[ ]*@(\d+) /);
5602 return getTreeStr($1);
5603}
5604
5605sub getFuncReturn($)
5606{
5607 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
5608 if($FuncInfo=~/type[ ]*:[ ]*@(\d+) /) {
5609 if($LibInfo{$Version}{"info"}{$1}=~/retn[ ]*:[ ]*@(\d+) /) {
5610 return $1;
5611 }
5612 }
5613 return "";
5614}
5615
5616sub getFuncOrig($)
5617{
5618 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
5619 if($FuncInfo=~/orig[ ]*:[ ]*@(\d+) /) {
5620 return $1;
5621 }
5622 else {
5623 return $_[0];
5624 }
5625}
5626
5627sub unmangleSymbol($)
5628{
5629 my $Symbol = $_[0];
5630 my @Unmngl = unmangleArray($Symbol);
5631 return $Unmngl[0];
5632}
5633
5634sub unmangleArray(@)
5635{
5636 if(@_[0]=~/\A\?/)
5637 { # MSVC mangling
5638 my $UndNameCmd = get_CmdPath("undname");
5639 if(not $UndNameCmd) {
5640 exitStatus("Not_Found", "can't find \"undname\"");
5641 }
5642 writeFile("$TMP_DIR/unmangle", join("\n", @_));
5643 return split(/\n/, `$UndNameCmd 0x8386 $TMP_DIR/unmangle`);
5644 }
5645 else
5646 { # GCC mangling
5647 my $CppFiltCmd = get_CmdPath("c++filt");
5648 if(not $CppFiltCmd) {
5649 exitStatus("Not_Found", "can't find c++filt in PATH");
5650 }
5651 my $Info = `$CppFiltCmd -h 2>&1`;
5652 if($Info=~/\@<file>/)
5653 {# new version of c++filt can take a file
5654 my $NoStrip = "";
5655 if($OSgroup eq "macos"
5656 or $OSgroup eq "windows") {
5657 $NoStrip = "-n";
5658 }
5659 writeFile("$TMP_DIR/unmangle", join("\n", @_));
5660 return split(/\n/, `$CppFiltCmd $NoStrip \@\"$TMP_DIR/unmangle\"`);
5661 }
5662 else
5663 { # old-style unmangling
5664 if($#_>$MAX_COMMAND_LINE_ARGUMENTS) {
5665 my @Half = splice(@_, 0, ($#_+1)/2);
5666 return (unmangleArray(@Half), unmangleArray(@_))
5667 }
5668 else
5669 {
5670 my $NoStrip = "";
5671 if($OSgroup eq "macos"
5672 or $OSgroup eq "windows") {
5673 $NoStrip = "-n";
5674 }
5675 my $Strings = join(" ", @_);
5676 return split(/\n/, `$CppFiltCmd $NoStrip $Strings`);
5677 }
5678 }
5679 }
5680}
5681
5682sub get_SignatureNoInfo($$)
5683{
5684 my ($Interface, $LibVersion) = @_;
5685 if($Cache{"get_SignatureNoInfo"}{$LibVersion}{$Interface}) {
5686 return $Cache{"get_SignatureNoInfo"}{$LibVersion}{$Interface};
5687 }
5688 my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Interface);
5689 my $Signature = $tr_name{$MnglName}?$tr_name{$MnglName}:$MnglName;
5690 if($Interface=~/\A(_Z|\?)/)
5691 { # C++
5692 $Signature=~s/\Qstd::basic_string<char, std::char_traits<char>, std::allocator<char> >\E/std::string/g;
5693 $Signature=~s/\Qstd::map<std::string, std::string, std::less<std::string >, std::allocator<std::pair<std::string const, std::string > > >\E/std::map<std::string, std::string>/g;
5694 }
5695 if(not $CheckObjectsOnly or $OSgroup=~/linux|bsd|beos/)
5696 { # ELF format marks data as OBJECT
5697 if($CompleteSignature{$LibVersion}{$Interface}{"Object"}) {
5698 $Signature .= " [data]";
5699 }
5700 elsif($Interface!~/\A(_Z|\?)/) {
5701 $Signature .= " (...)";
5702 }
5703 }
5704 if(my $ChargeLevel = get_ChargeLevel($Interface, $LibVersion))
5705 {
5706 my $ShortName = substr($Signature, 0, detect_center($Signature, "("));
5707 $Signature=~s/\A\Q$ShortName\E/$ShortName $ChargeLevel/g;
5708 }
5709 if($SymbolVersion) {
5710 $Signature .= $VersionSpec.$SymbolVersion;
5711 }
5712 return ($Cache{"get_SignatureNoInfo"}{$LibVersion}{$Interface} = $Signature);
5713}
5714
5715sub get_ChargeLevel($$)
5716{
5717 my ($Interface, $LibVersion) = @_;
5718 return "" if($Interface!~/\A(_Z|\?)/);
5719 if(defined $CompleteSignature{$LibVersion}{$Interface}
5720 and $CompleteSignature{$LibVersion}{$Interface}{"Header"})
5721 {
5722 if($CompleteSignature{$LibVersion}{$Interface}{"Constructor"})
5723 {
5724 if($Interface=~/C1E/) {
5725 return "[in-charge]";
5726 }
5727 elsif($Interface=~/C2E/) {
5728 return "[not-in-charge]";
5729 }
5730 }
5731 elsif($CompleteSignature{$LibVersion}{$Interface}{"Destructor"})
5732 {
5733 if($Interface=~/D1E/) {
5734 return "[in-charge]";
5735 }
5736 elsif($Interface=~/D2E/) {
5737 return "[not-in-charge]";
5738 }
5739 elsif($Interface=~/D0E/) {
5740 return "[in-charge-deleting]";
5741 }
5742 }
5743 }
5744 else
5745 {
5746 if($Interface=~/C1E/) {
5747 return "[in-charge]";
5748 }
5749 elsif($Interface=~/C2E/) {
5750 return "[not-in-charge]";
5751 }
5752 elsif($Interface=~/D1E/) {
5753 return "[in-charge]";
5754 }
5755 elsif($Interface=~/D2E/) {
5756 return "[not-in-charge]";
5757 }
5758 elsif($Interface=~/D0E/) {
5759 return "[in-charge-deleting]";
5760 }
5761 }
5762 return "";
5763}
5764
5765sub get_Signature($$)
5766{
5767 my ($Interface, $LibVersion) = @_;
5768 if($Cache{"get_Signature"}{$LibVersion}{$Interface}) {
5769 return $Cache{"get_Signature"}{$LibVersion}{$Interface};
5770 }
5771 my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Interface);
5772 if(skipGlobalData($MnglName) or not $CompleteSignature{$LibVersion}{$Interface}{"Header"}) {
5773 return get_SignatureNoInfo($Interface, $LibVersion);
5774 }
5775 my ($Func_Signature, @Param_Types_FromUnmangledName) = ();
5776 my $ShortName = $CompleteSignature{$LibVersion}{$Interface}{"ShortName"};
5777 if($Interface=~/\A(_Z|\?)/)
5778 {
5779 if(my $ClassId = $CompleteSignature{$LibVersion}{$Interface}{"Class"}) {
5780 $Func_Signature = get_TypeName($ClassId, $LibVersion)."::".(($CompleteSignature{$LibVersion}{$Interface}{"Destructor"})?"~":"").$ShortName;
5781 }
5782 elsif(my $NameSpace = $CompleteSignature{$LibVersion}{$Interface}{"NameSpace"}) {
5783 $Func_Signature = $NameSpace."::".$ShortName;
5784 }
5785 else {
5786 $Func_Signature = $ShortName;
5787 }
5788 @Param_Types_FromUnmangledName = get_s_params($tr_name{$MnglName}, 0);
5789 }
5790 else {
5791 $Func_Signature = $MnglName;
5792 }
5793 my @ParamArray = ();
5794 foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{$LibVersion}{$Interface}{"Param"}}))
5795 {
5796 next if($Pos eq "");
5797 my $ParamTypeId = $CompleteSignature{$LibVersion}{$Interface}{"Param"}{$Pos}{"type"};
5798 next if(not $ParamTypeId);
5799 my $ParamTypeName = get_TypeName($ParamTypeId, $LibVersion);
5800 if(not $ParamTypeName) {
5801 $ParamTypeName = $Param_Types_FromUnmangledName[$Pos];
5802 }
5803 foreach my $Typedef (keys(%ChangedTypedef))
5804 {
5805 my $Base = $Typedef_BaseName{$LibVersion}{$Typedef};
5806 $ParamTypeName=~s/(\A|\W)\Q$Typedef\E(\W|\Z)/$1$Base$2/g;
5807 }
5808 if(my $ParamName = $CompleteSignature{$LibVersion}{$Interface}{"Param"}{$Pos}{"name"}) {
5809 push(@ParamArray, create_member_decl($ParamTypeName, $ParamName));
5810 }
5811 else {
5812 push(@ParamArray, $ParamTypeName);
5813 }
5814 }
5815 if($CompleteSignature{$LibVersion}{$Interface}{"Data"}
5816 or $CompleteSignature{$LibVersion}{$Interface}{"Object"}) {
5817 $Func_Signature .= " [data]";
5818 }
5819 else
5820 {
5821 if(my $ChargeLevel = get_ChargeLevel($Interface, $LibVersion))
5822 { # add [in-charge]
5823 $Func_Signature .= " ".$ChargeLevel;
5824 }
5825 $Func_Signature .= " (".join(", ", @ParamArray).")";
5826 if($CompleteSignature{$LibVersion}{$Interface}{"Const"}
5827 or $Interface=~/\A_ZN(V|)K/) {
5828 $Func_Signature .= " const";
5829 }
5830 if($CompleteSignature{$LibVersion}{$Interface}{"Volatile"}
5831 or $Interface=~/\A_ZN(K|)V/) {
5832 $Func_Signature .= " volatile";
5833 }
5834 if($CompleteSignature{$LibVersion}{$Interface}{"Static"}
5835 and $Interface=~/\A(_Z|\?)/)
5836 {# for static methods
5837 $Func_Signature .= " [static]";
5838 }
5839 }
5840 if(defined $ShowRetVal
5841 and my $ReturnTId = $CompleteSignature{$LibVersion}{$Interface}{"Return"}) {
5842 $Func_Signature .= ":".get_TypeName($ReturnTId, $LibVersion);
5843 }
5844 if($SymbolVersion) {
5845 $Func_Signature .= $VersionSpec.$SymbolVersion;
5846 }
5847 return ($Cache{"get_Signature"}{$LibVersion}{$Interface} = $Func_Signature);
5848}
5849
5850sub create_member_decl($$)
5851{
5852 my ($TName, $Member) = @_;
5853 if($TName=~/\([\*]+\)/) {
5854 $TName=~s/\(([\*]+)\)/\($1$Member\)/;
5855 return $TName;
5856 }
5857 else
5858 {
5859 my @ArraySizes = ();
5860 while($TName=~s/(\[[^\[\]]*\])\Z//) {
5861 push(@ArraySizes, $1);
5862 }
5863 return $TName." ".$Member.join("", @ArraySizes);
5864 }
5865}
5866
5867sub getFuncType($)
5868{
5869 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
5870 return "" if($FuncInfo!~/type[ ]*:[ ]*@(\d+) /);
5871 my $FuncTypeInfoId = $1;
5872 my $FunctionType = $LibInfo{$Version}{"info_type"}{$FuncTypeInfoId};
5873 if($FunctionType eq "method_type") {
5874 return "Method";
5875 }
5876 elsif($FunctionType eq "function_type") {
5877 return "Function";
5878 }
5879 else {
5880 return $FunctionType;
5881 }
5882}
5883
5884sub getFuncTypeId($)
5885{
5886 my $FuncInfo = $LibInfo{$Version}{"info"}{$_[0]};
5887 if($FuncInfo=~/type[ ]*:[ ]*@(\d+)( |\Z)/) {
5888 return $1;
5889 }
5890 else {
5891 return 0;
5892 }
5893}
5894
5895sub isNotAnon($) {
5896 return (not isAnon($_[0]));
5897}
5898
5899sub isAnon($)
5900{# "._N" or "$_N" in older GCC versions
5901 return ($_[0]=~/(\.|\$)\_\d+|anon\-/);
5902}
5903
5904sub unmangled_Compact($)
5905{ # Removes all non-essential (for C++ language) whitespace from a string. If
5906 # the whitespace is essential it will be replaced with exactly one ' '
5907 # character. Works correctly only for unmangled names.
5908 my $Name = $_[0];
5909 if(defined $Cache{"unmangled_Compact"}{$Name}) {
5910 return $Cache{"unmangled_Compact"}{$Name};
5911 }
5912 # First, we reduce all spaces that we can
5913 my $coms='[-()<>:*&~!|+=%@~"?.,/[^'."']";
5914 my $coms_nobr='[-()<:*&~!|+=%@~"?.,'."']";
5915 my $clos='[),;:\]]';
5916 $_ = $Name;
5917 s/^\s+//gm;
5918 s/\s+$//gm;
5919 s/((?!\n)\s)+/ /g;
5920 s/(\w+)\s+($coms+)/$1$2/gm;
5921 s/($coms+)\s+(\w+)/$1$2/gm;
5922 s/(\w)\s+($clos)/$1$2/gm;
5923 s/($coms+)\s+($coms+)/$1 $2/gm;
5924 s/($coms_nobr+)\s+($coms+)/$1$2/gm;
5925 s/($coms+)\s+($coms_nobr+)/$1$2/gm;
5926 # don't forget about >> and <:. In unmangled names global-scope modifier
5927 # is not used, so <: will always be a digraph and requires no special treatment.
5928 # We also try to remove other parts that are better to be removed here than in other places
5929 # double-cv
5930 s/\bconst\s+const\b/const/gm;
5931 s/\bvolatile\s+volatile\b/volatile/gm;
5932 s/\bconst\s+volatile\b\s+const\b/const volatile/gm;
5933 s/\bvolatile\s+const\b\s+volatile\b/const volatile/gm;
5934 # Place cv in proper order
5935 s/\bvolatile\s+const\b/const volatile/gm;
5936 return ($Cache{"unmangled_Compact"}{$Name} = $_);
5937}
5938
5939sub unmangled_PostProcess($)
5940{
5941 my $Name = $_[0];
5942 $_ = $Name;
5943 #s/\bunsigned int\b/unsigned/g;
5944 s/\bshort unsigned int\b/unsigned short/g;
5945 s/\bshort int\b/short/g;
5946 s/\blong long unsigned int\b/unsigned long long/g;
5947 s/\blong unsigned int\b/unsigned long/g;
5948 s/\blong long int\b/long long/g;
5949 s/\blong int\b/long/g;
5950 s/\)const\b/\) const/g;
5951 s/\blong long unsigned\b/unsigned long long/g;
5952 s/\blong unsigned\b/unsigned long/g;
5953 return $_;
5954}
5955
5956sub formatName($)
5957{# type name correction
5958 my $Name = $_[0];
5959 $Name=unmangled_Compact($Name);
5960 $Name=unmangled_PostProcess($Name);
5961 $Name=~s/>>/> >/g; # double templates
5962 $Name=~s/(operator\s*)> >/$1>>/;
5963 return $Name;
5964}
5965
5966sub get_HeaderDeps($$)
5967{
5968 my ($AbsPath, $LibVersion) = @_;
5969 return () if(not $AbsPath or not $LibVersion);
5970 if(defined $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}) {
5971 return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
5972 }
5973 my %IncDir = ();
5974 detect_recursive_includes($AbsPath, $LibVersion);
5975 foreach my $HeaderPath (keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}))
5976 {
5977 next if(not $HeaderPath);
5978 next if($MAIN_CPP_DIR and $HeaderPath=~/\A\Q$MAIN_CPP_DIR\E([\/\\]|\Z)/);
5979 my $Dir = get_dirname($HeaderPath);
5980 foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$HeaderPath}}))
5981 {
5982 my $Dep = $Dir;
5983 if($Prefix)
5984 {
5985 if($OSgroup eq "windows")
5986 { # case insensitive seach on windows
5987 if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//ig) {
5988 next;
5989 }
5990 }
5991 elsif($OSgroup eq "macos")
5992 { # seach in frameworks
5993 if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
5994 {
5995 if($HeaderPath=~/(.+\.framework)\/Headers\/([^\/]+)/)
5996 {# frameworks
5997 my ($HFramework, $HName) = ($1, $2);
5998 $Dep = $HFramework;
5999 }
6000 else
6001 {# mismatch
6002 next;
6003 }
6004 }
6005 }
6006 elsif(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
6007 { # Linux, FreeBSD
6008 next;
6009 }
6010 }
6011 if(not $Dep)
6012 { # nothing to include
6013 next;
6014 }
6015 if(is_default_include_dir($Dep))
6016 { # included by the compiler
6017 next;
6018 }
6019 if(get_depth($Dep)==1)
6020 { # too short
6021 next;
6022 }
6023 if(isLibcDir($Dep))
6024 { # do NOT include /usr/include/{sys,bits}
6025 next;
6026 }
6027 $IncDir{$Dep}=1;
6028 }
6029 }
6030 $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath} = sortIncPaths([keys(%IncDir)], $LibVersion);
6031 return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
6032}
6033
6034sub sortIncPaths($$)
6035{
6036 my ($ArrRef, $LibVersion) = @_;
6037 @{$ArrRef} = sort {$b cmp $a} @{$ArrRef};
6038 @{$ArrRef} = sort {get_depth($a)<=>get_depth($b)} @{$ArrRef};
6039 @{$ArrRef} = sort {$Header_Dependency{$LibVersion}{$b}<=>$Header_Dependency{$LibVersion}{$a}} @{$ArrRef};
6040 return $ArrRef;
6041}
6042
6043sub joinPath($$) {
6044 return join($SLASH, @_);
6045}
6046
6047sub get_namespace_additions($)
6048{
6049 my $NameSpaces = $_[0];
6050 my ($Additions, $AddNameSpaceId) = ("", 1);
6051 foreach my $NS (sort {$a=~/_/ <=> $b=~/_/} sort {lc($a) cmp lc($b)} keys(%{$NameSpaces}))
6052 {
6053 next if($SkipNameSpaces{$Version}{$NS});
6054 next if(not $NS or $NameSpaces->{$NS}==-1);
6055 next if($NS=~/(\A|::)iterator(::|\Z)/i);
6056 next if($NS=~/\A__/i);
6057 next if(($NS=~/\Astd::/ or $NS=~/\A(std|tr1|rel_ops|fcntl)\Z/) and not $STDCXX_TESTING);
6058 $NestedNameSpaces{$Version}{$NS} = 1;# for future use in reports
6059 my ($TypeDecl_Prefix, $TypeDecl_Suffix) = ();
6060 my @NS_Parts = split(/::/, $NS);
6061 next if($#NS_Parts==-1);
6062 next if($NS_Parts[0]=~/\A(random|or)\Z/);
6063 foreach my $NS_Part (@NS_Parts)
6064 {
6065 $TypeDecl_Prefix .= "namespace $NS_Part\{";
6066 $TypeDecl_Suffix .= "}";
6067 }
6068 my $TypeDecl = $TypeDecl_Prefix."typedef int tmp_add_type_$AddNameSpaceId;".$TypeDecl_Suffix;
6069 my $FuncDecl = "$NS\:\:tmp_add_type_$AddNameSpaceId tmp_add_func_$AddNameSpaceId(){return 0;};";
6070 $Additions.=" $TypeDecl\n $FuncDecl\n";
6071 $AddNameSpaceId+=1;
6072 }
6073 return $Additions;
6074}
6075
6076sub path_format($$)
6077{# forward slash to pass into MinGW GCC
6078 my ($Path, $Fmt) = @_;
6079 if($Fmt eq "windows") {
6080 $Path=~s/\//\\/g;
6081 $Path=lc($Path);
6082 }
6083 else {
6084 $Path=~s/\\/\//g;
6085 }
6086 return $Path;
6087}
6088
6089sub inc_opt($$)
6090{
6091 my ($Path, $Style) = @_;
6092 if($Style eq "GCC")
6093 {# GCC options
6094 if($OSgroup eq "windows")
6095 {# to MinGW GCC
6096 return "-I\"".path_format($Path, "unix")."\"";
6097 }
6098 elsif($OSgroup eq "macos"
6099 and $Path=~/\.framework\Z/)
6100 {# to Apple's GCC
6101 return "-F".esc(get_dirname($Path));
6102 }
6103 else {
6104 return "-I".esc($Path);
6105 }
6106 }
6107 elsif($Style eq "CL") {
6108 return "/I \"$Path\"";
6109 }
6110 return "";
6111}
6112
6113sub platformSpecs($)
6114{
6115 my $LibVersion = $_[0];
6116 my $Arch = getArch($LibVersion);
6117 if($OStarget eq "symbian")
6118 { # options for GCCE compiler
6119 my %Symbian_Opts = map {$_=>1} (
6120 "-D__GCCE__",
6121 "-DUNICODE",
6122 "-fexceptions",
6123 "-D__SYMBIAN32__",
6124 "-D__MARM_INTERWORK__",
6125 "-D_UNICODE",
6126 "-D__S60_50__",
6127 "-D__S60_3X__",
6128 "-D__SERIES60_3X__",
6129 "-D__EPOC32__",
6130 "-D__MARM__",
6131 "-D__EABI__",
6132 "-D__MARM_ARMV5__",
6133 "-D__SUPPORT_CPP_EXCEPTIONS__",
6134 "-march=armv5t",
6135 "-mapcs",
6136 "-mthumb-interwork",
6137 "-DEKA2",
6138 "-DSYMBIAN_ENABLE_SPLIT_HEADERS"
6139 );
6140 return join(" ", keys(%Symbian_Opts));
6141 }
6142 elsif($OSgroup eq "windows"
6143 and get_dumpmachine($GCC_PATH)=~/mingw/i)
6144 { # add options to MinGW compiler
6145 # to simulate the MSVC compiler
6146 my %MinGW_Opts = map {$_=>1} (
6147 "-D_WIN32",
6148 "-D_STDCALL_SUPPORTED",
6149 "-D__int64=\"long long\"",
6150 "-D__int32=int",
6151 "-D__int16=short",
6152 "-D__int8=char",
6153 "-D__possibly_notnullterminated=\" \"",
6154 "-D__nullterminated=\" \"",
6155 "-D__nullnullterminated=\" \"",
6156 "-D__w64=\" \"",
6157 "-D__ptr32=\" \"",
6158 "-D__ptr64=\" \"",
6159 "-D__forceinline=inline",
6160 "-D__inline=inline",
6161 "-D__uuidof(x)=IID()",
6162 "-D__try=",
6163 "-D__except(x)=",
6164 "-D__declspec(x)=__attribute__((x))",
6165 "-D__pragma(x)=",
6166 "-D_inline=inline",
6167 "-D__forceinline=__inline",
6168 "-D__stdcall=__attribute__((__stdcall__))",
6169 "-D__cdecl=__attribute__((__cdecl__))",
6170 "-D__fastcall=__attribute__((__fastcall__))",
6171 "-D__thiscall=__attribute__((__thiscall__))",
6172 "-D_stdcall=__attribute__((__stdcall__))",
6173 "-D_cdecl=__attribute__((__cdecl__))",
6174 "-D_fastcall=__attribute__((__fastcall__))",
6175 "-D_thiscall=__attribute__((__thiscall__))",
6176 "-DSHSTDAPI_(x)=x",
6177 "-D_MSC_EXTENSIONS",
6178 "-DSECURITY_WIN32",
6179 "-D_MSC_VER=1500",
6180 "-D_USE_DECLSPECS_FOR_SAL",
6181 "-D__noop=\" \"",
6182 "-DDECLSPEC_DEPRECATED=\" \"",
6183 "-D__builtin_alignof(x)=__alignof__(x)",
6184 "-DSORTPP_PASS");
6185 if($Arch eq "x86") {
6186 $MinGW_Opts{"-D_M_IX86=300"}=1;
6187 }
6188 elsif($Arch eq "x86_64") {
6189 $MinGW_Opts{"-D_M_AMD64=300"}=1;
6190 }
6191 elsif($Arch eq "ia64") {
6192 $MinGW_Opts{"-D_M_IA64=300"}=1;
6193 }
6194 return join(" ", keys(%MinGW_Opts));
6195 }
6196 return "";
6197}
6198
6199my %C_Structure = map {$_=>1} (
6200# FIXME: Can't separate union and struct data types before dumping,
6201# so it sometimes cause compilation errors for unknown reason
6202# when trying to declare TYPE* tmp_add_class_N
6203# This is a list of such structures + list of other C structures
6204 "sigval",
6205 "sigevent",
6206 "sigaction",
6207 "sigvec",
6208 "sigstack",
6209 "timeval",
6210 "timezone",
6211 "rusage",
6212 "rlimit",
6213 "wait",
6214 "flock",
6215 "stat",
6216 "_stat",
6217 "stat32",
6218 "_stat32",
6219 "stat64",
6220 "_stat64",
6221 "_stati64",
6222 "if_nameindex",
6223 "usb_device",
6224 "sigaltstack",
6225 "sysinfo",
6226 "timeLocale",
6227 "tcp_debug",
6228 "rpc_createerr",
6229# Other C structures appearing in every dump
6230 "timespec",
6231 "random_data",
6232 "drand48_data",
6233 "_IO_marker",
6234 "_IO_FILE",
6235 "lconv",
6236 "sched_param",
6237 "tm",
6238 "itimerspec",
6239 "_pthread_cleanup_buffer",
6240 "fd_set",
6241 "siginfo"
6242);
6243
6244sub getCompileCmd($$$)
6245{
6246 my ($Path, $Opt, $Inc) = @_;
6247 my $GccCall = $GCC_PATH;
6248 if($Opt) {
6249 $GccCall .= " ".$Opt;
6250 }
6251 $GccCall .= " -x ";
6252 if($OSgroup eq "macos") {
6253 $GccCall .= "objective-";
6254 }
6255 if(check_gcc_version($GCC_PATH, "4"))
6256 { # compile as "C++" header
6257 # to obtain complete dump using GCC 4.0
6258 $GccCall .= "c++-header";
6259 }
6260 else
6261 { # compile as "C++" source
6262 # GCC 3.3 cannot compile headers
6263 $GccCall .= "c++";
6264 }
6265 if(my $Opts = platformSpecs($Version))
6266 {# platform-specific options
6267 $GccCall .= " ".$Opts;
6268 }
6269 # allow extra qualifications
6270 # and other nonconformant code
6271 $GccCall .= " -fpermissive -w";
6272 if($NoStdInc)
6273 {
6274 $GccCall .= " -nostdinc";
6275 $GccCall .= " -nostdinc++";
6276 }
6277 if($CompilerOptions{$Version})
6278 { # user-defined options
6279 $GccCall .= " ".$CompilerOptions{$Version};
6280 }
6281 $GccCall .= " \"$Path\"";
6282 if($Inc)
6283 { # include paths
6284 $GccCall .= " ".$Inc;
6285 }
6286 return $GccCall;
6287}
6288
6289sub getDump()
6290{
6291 if(not $GCC_PATH) {
6292 exitStatus("Error", "internal error - GCC path is not set");
6293 }
6294 my %HeaderElems = (
6295 # Types
6296 "stdio.h" => ["FILE", "va_list"],
6297 "stddef.h" => ["NULL"],
6298 "stdint.h" => ["uint32_t", "int32_t", "uint64_t"],
6299 "time.h" => ["time_t"],
6300 "sys/types.h" => ["ssize_t", "u_int32_t", "u_short", "u_char",
6301 "u_int", "off_t", "u_quad_t", "u_long", "size_t", "mode_t"],
6302 "unistd.h" => ["gid_t", "uid_t"],
6303 "stdbool.h" => ["_Bool"],
6304 "rpc/xdr.h" => ["bool_t"],
6305 "in_systm.h" => ["n_long", "n_short"],
6306 # Fields
6307 "arpa/inet.h" => ["fw_src", "ip_src"]
6308 );
6309 my %AutoPreamble = ();
6310 foreach (keys(%HeaderElems)) {
6311 foreach my $Elem (@{$HeaderElems{$_}}) {
6312 $AutoPreamble{$Elem}=$_;
6313 }
6314 }
6315 my $TmpHeaderPath = "$TMP_DIR/dump$Version.h";
6316 my $MHeaderPath = $TmpHeaderPath;
6317 open(LIB_HEADER, ">$TmpHeaderPath") || die ("can't open file \'$TmpHeaderPath\': $!\n");
6318 if(my $AddDefines = $Descriptor{$Version}{"Defines"})
6319 {
6320 $AddDefines=~s/\n\s+/\n /g;
6321 print LIB_HEADER "\n // add defines\n ".$AddDefines."\n";
6322 }
6323 print LIB_HEADER "\n // add includes\n";
6324 my @PreambleHeaders = keys(%{$Include_Preamble{$Version}});
6325 @PreambleHeaders = sort {int($Include_Preamble{$Version}{$a}{"Position"})<=>int($Include_Preamble{$Version}{$b}{"Position"})} @PreambleHeaders;
6326 foreach my $Header_Path (@PreambleHeaders) {
6327 print LIB_HEADER " #include \"".path_format($Header_Path, "unix")."\"\n";
6328 }
6329 my @Headers = keys(%{$Registered_Headers{$Version}});
6330 @Headers = sort {int($Registered_Headers{$Version}{$a}{"Pos"})<=>int($Registered_Headers{$Version}{$b}{"Pos"})} @Headers;
6331 foreach my $Header_Path (@Headers)
6332 {
6333 next if($Include_Preamble{$Version}{$Header_Path});
6334 print LIB_HEADER " #include \"".path_format($Header_Path, "unix")."\"\n";
6335 }
6336 close(LIB_HEADER);
6337 my $IncludeString = getIncString(getIncPaths(@PreambleHeaders, @Headers), "GCC");
6338 if($Debug)
6339 { # debug mode
6340 writeFile($DEBUG_PATH{$Version}."/recursive-includes.txt", Dumper(\%RecursiveIncludes));
6341 writeFile($DEBUG_PATH{$Version}."/include-paths.txt", Dumper($Cache{"get_HeaderDeps"}));
6342 writeFile($DEBUG_PATH{$Version}."/includes.txt", Dumper(\%Header_Includes));
6343 writeFile($DEBUG_PATH{$Version}."/default-includes.txt", Dumper(\%DefaultIncPaths));
6344 }
6345 # preprocessing stage
6346 checkPreprocessedUnit(callPreprocessor($TmpHeaderPath, $IncludeString, $Version));
6347 my $MContent = "";
6348 my $PreprocessCmd = getCompileCmd($TmpHeaderPath, "-E", $IncludeString);
6349 if($OStarget eq "windows"
6350 and get_dumpmachine($GCC_PATH)=~/mingw/i
6351 and $MinGWMode{$Version}!=-1)
6352 { # modify headers to compile by MinGW
6353 if(not $MContent)
6354 { # preprocessing
6355 $MContent = `$PreprocessCmd 2>$TMP_DIR/null`;
6356 }
6357 if($MContent=~s/__asm\s*(\{[^{}]*?\}|[^{};]*)//g)
6358 { # __asm { ... }
6359 $MinGWMode{$Version}=1;
6360 }
6361 if($MContent=~s/\s+(\/ \/.*?)\n/\n/g)
6362 { # comments after preprocessing
6363 $MinGWMode{$Version}=1;
6364 }
6365 if($MContent=~s/(\W)(0x[a-f]+|\d+)(i|ui)(8|16|32|64)(\W)/$1$2$5/g)
6366 { # 0xffui8
6367 $MinGWMode{$Version}=1;
6368 }
6369 if($MinGWMode{$Version}) {
6370 printMsg("INFO", "Using MinGW compatibility mode");
6371 $MHeaderPath = "$TMP_DIR/dump$Version.i";
6372 }
6373 }
6374 if(($COMMON_LANGUAGE{$Version} eq "C" or $CheckHeadersOnly)
6375 and $C99Mode{$Version}!=-1 and not $Cpp2003)
6376 { # rename "C++" keywords in "C" code
6377 if(not $MContent)
6378 { # preprocessing
6379 $MContent = `$PreprocessCmd 2>$TMP_DIR/null`;
6380 }
6381 my $RegExp_C = join("|", keys(%CppKeywords_C));
6382 my $RegExp_F = join("|", keys(%CppKeywords_F));
6383 my $RegExp_O = join("|", keys(%CppKeywords_O));
6384 while($MContent=~s/(\A|\n[^\#\/\n][^\n]*?|\n)(\*\s*|\s+|\@|\,|\()($RegExp_C|$RegExp_F)(\s*(\,|\)|\;|\-\>|\.|\:\s*\d))/$1$2c99_$3$4/g)
6385 { # MATCH:
6386 # int foo(int new, int class, int (*new)(int));
6387 # unsigned private: 8;
6388 # DO NOT MATCH:
6389 # #pragma GCC visibility push(default)
6390 $C99Mode{$Version} = 1;
6391 }
6392 if($MContent=~s/([^\w\s]|\w\s+)(?<!operator )(delete)(\s*(\())/$1c99_$2$3/g)
6393 { # MATCH:
6394 # int delete(...);
6395 # int explicit(...);
6396 # DO NOT MATCH:
6397 # void operator delete(...)
6398 $C99Mode{$Version} = 1;
6399 }
6400 if($MContent=~s/(\s+)($RegExp_O)(\s*(\;|\:))/$1c99_$2$3/g)
6401 { # MATCH:
6402 # int bool;
6403 # DO NOT MATCH:
6404 # bool X;
6405 # return *this;
6406 # throw;
6407 $C99Mode{$Version} = 1;
6408 }
6409 if($MContent=~s/(\s+)(operator)(\s*(\(\s*\)\s*[^\(\s]|\(\s*[^\)\s]))/$1c99_$2$3/g)
6410 { # MATCH:
6411 # int operator(...);
6412 # DO NOT MATCH:
6413 # int operator()(...);
6414 $C99Mode{$Version} = 1;
6415 }
6416 if($MContent=~s/([^\w\(\,\s]\s*|\s+)(operator)(\s*(\,\s*[^\(\s]|\)))/$1c99_$2$3/g)
6417 { # MATCH:
6418 # int foo(int operator);
6419 # int foo(int operator, int other);
6420 # DO NOT MATCH:
6421 # int operator,(...);
6422 $C99Mode{$Version} = 1;
6423 }
6424 if($MContent=~s/(\*\s*|\w\s+)(bool)(\s*(\,|\)))/$1c99_$2$3/g)
6425 { # MATCH:
6426 # int foo(gboolean *bool);
6427 # DO NOT MATCH:
6428 # void setTabEnabled(int index, bool);
6429 $C99Mode{$Version} = 1;
6430 }
6431 if($MContent=~s/(\w)([^\w\(\,\s]\s*|\s+)(this)(\s*(\,|\)))/$1$2c99_$3$4/g)
6432 { # MATCH:
6433 # int foo(int* this);
6434 # int bar(int this);
6435 # DO NOT MATCH:
6436 # baz(X, this);
6437 $C99Mode{$Version} = 1;
6438 }
6439 if($C99Mode{$Version}==1)
6440 { # try to change C++ "keyword" to "c99_keyword"
6441 printMsg("INFO", "Using C99 compatibility mode");
6442 $MHeaderPath = "$TMP_DIR/dump$Version.i";
6443 }
6444 }
6445 if($C99Mode{$Version}==1
6446 or $MinGWMode{$Version}==1)
6447 { # compile the corrected preprocessor output
6448 writeFile($MHeaderPath, $MContent);
6449 }
6450 if($COMMON_LANGUAGE{$Version} eq "C++")
6451 { # add classes and namespaces to the dump
6452 my $CHdump = "-fdump-class-hierarchy -c";
6453 if($C99Mode{$Version}==1
6454 or $MinGWMode{$Version}==1) {
6455 $CHdump .= " -fpreprocessed";
6456 }
6457 my $ClassHierarchyCmd = getCompileCmd($MHeaderPath, $CHdump, $IncludeString);
6458 chdir($TMP_DIR);
6459 system("$ClassHierarchyCmd >null 2>&1");
6460 chdir($ORIG_DIR);
6461 if(my $ClassDump = (cmd_find($TMP_DIR,"f","*.class",1))[0])
6462 {
6463 my %AddClasses = ();
6464 my $Content = readFile($ClassDump);
6465 foreach my $ClassInfo (split(/\n\n/, $Content))
6466 {
6467 if($ClassInfo=~/\AClass\s+(.+)\s*/i)
6468 {
6469 my $CName = $1;
6470 next if($CName=~/\A(__|_objc_|_opaque_)/);
6471 $TUnit_NameSpaces{$Version}{$CName} = -1;
6472 if($CName=~/\A[\w:]+\Z/)
6473 { # classes
6474 $AddClasses{$CName} = 1;
6475 }
6476 if($CName=~/(\w[\w:]*)::/)
6477 { # namespaces
6478 my $NS = $1;
6479 if(not defined $TUnit_NameSpaces{$Version}{$NS}) {
6480 $TUnit_NameSpaces{$Version}{$NS} = 1;
6481 }
6482 }
6483 }
6484 elsif($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i)
6485 { # read v-tables (advanced approach)
6486 my ($CName, $VTable) = ($1, $2);
6487 $ClassVTable_Content{$Version}{$CName} = $VTable;
6488 }
6489 }
6490 if($Debug)
6491 { # debug mode
6492 mkpath($DEBUG_PATH{$Version});
6493 copy($ClassDump, $DEBUG_PATH{$Version}."/class-hierarchy.txt");
6494 }
6495 unlink($ClassDump);
6496 if(my $NS_Add = get_namespace_additions($TUnit_NameSpaces{$Version}))
6497 { # GCC on all supported platforms does not include namespaces to the dump by default
6498 appendFile($MHeaderPath, "\n // add namespaces\n".$NS_Add);
6499 }
6500 # some GCC versions don't include class methods to the TU dump by default
6501 my ($AddClass, $ClassNum) = ("", 0);
6502 foreach my $CName (sort keys(%AddClasses))
6503 {
6504 next if($C_Structure{$CName});
6505 next if(not $STDCXX_TESTING and $CName=~/\Astd::/);
6506 next if(($CName=~tr![:]!!)>2);
6507 next if($SkipTypes{$Version}{$CName});
6508 if($CName=~/\A(.+)::[^:]+\Z/
6509 and $AddClasses{$1}) {
6510 next;
6511 }
6512 $AddClass .= " $CName* tmp_add_class_".($ClassNum++).";\n";
6513 }
6514 appendFile($MHeaderPath, "\n // add classes\n".$AddClass);
6515 }
6516 }
6517 writeLog($Version, "Temporary header file \'$TmpHeaderPath\' with the following content will be compiled to create GCC translation unit dump:\n".readFile($TmpHeaderPath)."\n");
6518 # create TU dump
6519 my $TUdump = "-fdump-translation-unit -fkeep-inline-functions -c";
6520 if($C99Mode{$Version}==1
6521 or $MinGWMode{$Version}==1) {
6522 $TUdump .= " -fpreprocessed";
6523 }
6524 my $SyntaxTreeCmd = getCompileCmd($MHeaderPath, $TUdump, $IncludeString);
6525 writeLog($Version, "The GCC parameters:\n $SyntaxTreeCmd\n\n");
6526 chdir($TMP_DIR);
6527 system($SyntaxTreeCmd." >$TMP_DIR/tu_errors 2>&1");
6528 if($?)
6529 { # failed to compile, but the TU dump still can be created
6530 my $Errors = readFile("$TMP_DIR/tu_errors");
6531 if($Errors=~/c99_/)
6532 { # disable c99 mode
6533 $C99Mode{$Version}=-1;
6534 printMsg("INFO", "Disabling C99 compatibility mode");
6535 resetLogging($Version);
6536 $TMP_DIR = tempdir(CLEANUP=>1);
6537 return getDump();
6538 }
6539 elsif($AutoPreambleMode{$Version}!=-1
6540 and my $TErrors = $Errors)
6541 {
6542 my %Types = ();
6543 while($TErrors=~s/error\:\s*(field\s*|)\W+(.+?)\W+//)
6544 { # error: 'FILE' has not been declared
6545 $Types{$2}=1;
6546 }
6547 my %AddHeaders = ();
6548 foreach my $Type (keys(%Types))
6549 {
6550 if(my $Header = $AutoPreamble{$Type}) {
6551 $AddHeaders{path_format($Header, $OSgroup)}=$Type;
6552 }
6553 }
6554 if(my @Headers = sort {$b cmp $a} keys(%AddHeaders))
6555 { # sys/types.h should be the first
6556 foreach my $Num (0 .. $#Headers)
6557 {
6558 my $Name = $Headers[$Num];
6559 if(my $Path = identify_header($Name, $Version))
6560 { # add automatic preamble headers
6561 if(defined $Include_Preamble{$Version}{$Path})
6562 { # already added
6563 next;
6564 }
6565 $Include_Preamble{$Version}{$Path}{"Position"} = keys(%{$Include_Preamble{$Version}});
6566 my $Type = $AddHeaders{$Name};
6567 printMsg("INFO", "Add \'$Name\' preamble header for \'$Type\'");
6568 }
6569 }
6570 $AutoPreambleMode{$Version}=-1;
6571 resetLogging($Version);
6572 $TMP_DIR = tempdir(CLEANUP=>1);
6573 return getDump();
6574 }
6575 }
6576 elsif($MinGWMode{$Version}!=-1)
6577 {
6578 $MinGWMode{$Version}=-1;
6579 resetLogging($Version);
6580 $TMP_DIR = tempdir(CLEANUP=>1);
6581 return getDump();
6582 }
6583 # FIXME: handle other errors and try to recompile
6584 writeLog($Version, $Errors);
6585 printMsg("ERROR", "some errors occurred when compiling headers");
6586 printErrorLog($Version);
6587 $COMPILE_ERRORS = $ERROR_CODE{"Compile_Error"};
6588 writeLog($Version, "\n");# new line
6589 }
6590 chdir($ORIG_DIR);
6591 unlink($TmpHeaderPath, $MHeaderPath);
6592 return (cmd_find($TMP_DIR,"f","*.tu",1))[0];
6593}
6594
6595sub cmd_file($)
6596{
6597 my $Path = $_[0];
6598 return "" if(not $Path or not -e $Path);
6599 if(my $CmdPath = get_CmdPath("file")) {
6600 return `$CmdPath -b \"$Path\"`;
6601 }
6602 return "";
6603}
6604
6605sub getIncString($$)
6606{
6607 my ($ArrRef, $Style) = @_;
6608 return if(not $ArrRef or $#{$ArrRef}<0);
6609 my $String = "";
6610 foreach (@{$ArrRef}) {
6611 $String .= " ".inc_opt($_, $Style);
6612 }
6613 return $String;
6614}
6615
6616sub getIncPaths(@)
6617{
6618 my @HeaderPaths = @_;
6619 my @IncPaths = ();
6620 if($INC_PATH_AUTODETECT{$Version})
6621 { # auto-detecting dependencies
6622 my %Includes = ();
6623 foreach my $HPath (@HeaderPaths)
6624 {
6625 foreach my $Dir (get_HeaderDeps($HPath, $Version))
6626 {
6627 if($Skip_Include_Paths{$Version}{$Dir}) {
6628 next;
6629 }
6630 $Includes{$Dir}=1;
6631 }
6632 }
6633 foreach my $Dir (keys(%{$Add_Include_Paths{$Version}}))
6634 { # added by user
6635 next if($Includes{$Dir});
6636 push(@IncPaths, $Dir);
6637 }
6638 foreach my $Dir (@{sortIncPaths([keys(%Includes)], $Version)}) {
6639 push(@IncPaths, $Dir);
6640 }
6641 }
6642 else
6643 { # user-defined paths
6644 foreach my $Dir (sort {get_depth($a)<=>get_depth($b)}
6645 sort {$b cmp $a} keys(%{$Include_Paths{$Version}})) {
6646 push(@IncPaths, $Dir);
6647 }
6648 }
6649 return \@IncPaths;
6650}
6651
6652sub callPreprocessor($$$)
6653{
6654 my ($Path, $Inc, $LibVersion) = @_;
6655 return "" if(not $Path or not -f $Path);
6656 my $IncludeString=$Inc;
6657 if(not $Inc) {
6658 $IncludeString = getIncString(getIncPaths($Path), "GCC");
6659 }
6660 my $Cmd = getCompileCmd($Path, "-dD -E", $IncludeString);
6661 my $Path = "$TMP_DIR/preprocessed";
6662 system("$Cmd >$Path 2>$TMP_DIR/null");
6663 return $Path;
6664}
6665
6666sub cmd_find($$$$)
6667{ # native "find" is much faster than File::Find (~6x)
6668 # also the File::Find doesn't support --maxdepth N option
6669 # so using the cross-platform wrapper for the native one
6670 my ($Path, $Type, $Name, $MaxDepth) = @_;
6671 return () if(not $Path or not -e $Path);
6672 if($OSgroup eq "windows")
6673 {
6674 my $DirCmd = get_CmdPath("dir");
6675 if(not $DirCmd) {
6676 exitStatus("Not_Found", "can't find \"dir\" command");
6677 }
6678 $Path=~s/[\\]+\Z//;
6679 $Path = get_abs_path($Path);
6680 $Path = path_format($Path, $OSgroup);
6681 my $Cmd = $DirCmd." \"$Path\" /B /O";
6682 if($MaxDepth!=1) {
6683 $Cmd .= " /S";
6684 }
6685 if($Type eq "d") {
6686 $Cmd .= " /AD";
6687 }
6688 my @Files = ();
6689 if($Name)
6690 { # FIXME: how to search file names in MS shell?
6691 $Name=~s/\*/.*/g if($Name!~/\]/);
6692 foreach my $File (split(/\n/, `$Cmd`))
6693 {
6694 if($File=~/$Name\Z/i) {
6695 push(@Files, $File);
6696 }
6697 }
6698 }
6699 else {
6700 @Files = split(/\n/, `$Cmd 2>$TMP_DIR/null`);
6701 }
6702 my @AbsPaths = ();
6703 foreach my $File (@Files)
6704 {
6705 if(not is_abs($File)) {
6706 $File = joinPath($Path, $File);
6707 }
6708 if($Type eq "f" and not -f $File)
6709 { # skip dirs
6710 next;
6711 }
6712 push(@AbsPaths, path_format($File, $OSgroup));
6713 }
6714 if($Type eq "d") {
6715 push(@AbsPaths, $Path);
6716 }
6717 return @AbsPaths;
6718 }
6719 else
6720 {
6721 my $FindCmd = get_CmdPath("find");
6722 if(not $FindCmd) {
6723 exitStatus("Not_Found", "can't find a \"find\" command");
6724 }
6725 $Path = get_abs_path($Path);
6726 if(-d $Path and -l $Path
6727 and $Path!~/\/\Z/)
6728 { # for directories that are symlinks
6729 $Path.="/";
6730 }
6731 my $Cmd = $FindCmd." \"$Path\"";
6732 if($MaxDepth) {
6733 $Cmd .= " -maxdepth $MaxDepth";
6734 }
6735 if($Type) {
6736 $Cmd .= " -type $Type";
6737 }
6738 if($Name)
6739 { # file name
6740 if($Name=~/\]/) {
6741 $Cmd .= " -regex \"$Name\"";
6742 }
6743 else {
6744 $Cmd .= " -name \"$Name\"";
6745 }
6746 }
6747 return split(/\n/, `$Cmd 2>$TMP_DIR/null`);
6748 }
6749}
6750
6751sub unpackDump($)
6752{
6753 my $Path = $_[0];
6754 return "" if(not $Path or not -e $Path);
6755 $Path = get_abs_path($Path);
6756 $Path = path_format($Path, $OSgroup);
6757 my ($Dir, $FileName) = separate_path($Path);
6758 my $UnpackDir = $TMP_DIR."/unpack";
6759 rmtree($UnpackDir);
6760 mkpath($UnpackDir);
6761 if($FileName=~s/\Q.zip\E\Z//g)
6762 { # *.zip
6763 my $UnzipCmd = get_CmdPath("unzip");
6764 if(not $UnzipCmd) {
6765 exitStatus("Not_Found", "can't find \"unzip\" command");
6766 }
6767 chdir($UnpackDir);
6768 system("$UnzipCmd \"$Path\" >$UnpackDir/contents.txt");
6769 if($?) {
6770 exitStatus("Error", "can't extract \'$Path\'");
6771 }
6772 chdir($ORIG_DIR);
6773 my @Contents = ();
6774 foreach (split("\n", readFile("$UnpackDir/contents.txt")))
6775 {
6776 if(/inflating:\s*([^\s]+)/) {
6777 push(@Contents, $1);
6778 }
6779 }
6780 if(not @Contents) {
6781 exitStatus("Error", "can't extract \'$Path\'");
6782 }
6783 return joinPath($UnpackDir, $Contents[0]);
6784 }
6785 elsif($FileName=~s/\Q.tar.gz\E\Z//g)
6786 { # *.tar.gz
6787 if($OSgroup eq "windows")
6788 { # -xvzf option is not implemented in tar.exe (2003)
6789 # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead
6790 my $TarCmd = get_CmdPath("tar");
6791 if(not $TarCmd) {
6792 exitStatus("Not_Found", "can't find \"tar\" command");
6793 }
6794 my $GzipCmd = get_CmdPath("gzip");
6795 if(not $GzipCmd) {
6796 exitStatus("Not_Found", "can't find \"gzip\" command");
6797 }
6798 chdir($UnpackDir);
6799 system("$GzipCmd -k -d -f \"$Path\"");# keep input files (-k)
6800 if($?) {
6801 exitStatus("Error", "can't extract \'$Path\'");
6802 }
6803 system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >$UnpackDir/contents.txt");
6804 if($?) {
6805 exitStatus("Error", "can't extract \'$Path\'");
6806 }
6807 chdir($ORIG_DIR);
6808 unlink($Dir."/".$FileName.".tar");
6809 my @Contents = split("\n", readFile("$UnpackDir/contents.txt"));
6810 if(not @Contents) {
6811 exitStatus("Error", "can't extract \'$Path\'");
6812 }
6813 return joinPath($UnpackDir, $Contents[0]);
6814 }
6815 else
6816 { # Unix
6817 my $TarCmd = get_CmdPath("tar");
6818 if(not $TarCmd) {
6819 exitStatus("Not_Found", "can't find \"tar\" command");
6820 }
6821 chdir($UnpackDir);
6822 system("$TarCmd -xvzf \"$Path\" >$UnpackDir/contents.txt");
6823 if($?) {
6824 exitStatus("Error", "can't extract \'$Path\'");
6825 }
6826 chdir($ORIG_DIR);
6827 # The content file name may be different
6828 # from the package file name
6829 my @Contents = split("\n", readFile("$UnpackDir/contents.txt"));
6830 if(not @Contents) {
6831 exitStatus("Error", "can't extract \'$Path\'");
6832 }
6833 return joinPath($UnpackDir, $Contents[0]);
6834 }
6835 }
6836}
6837
6838sub createArchive($$)
6839{
6840 my ($Path, $To) = @_;
6841 if(not $Path or not -e $Path
6842 or not -d $To) {
6843 return "";
6844 }
6845 my ($From, $Name) = separate_path($Path);
6846 if($OSgroup eq "windows")
6847 { # *.zip
6848 my $ZipCmd = get_CmdPath("zip");
6849 if(not $ZipCmd) {
6850 exitStatus("Not_Found", "can't find \"zip\"");
6851 }
6852 my $Pkg = $To."/".$Name.".zip";
6853 unlink($Pkg);
6854 chdir($To);
6855 system("$ZipCmd -j \"$Name.zip\" \"$Path\" >$TMP_DIR/null");
6856 if($?)
6857 { # cannot allocate memory (or other problems with "zip")
6858 unlink($Path);
6859 exitStatus("Error", "can't pack the ABI dump: ".$!);
6860 }
6861 chdir($ORIG_DIR);
6862 unlink($Path);
6863 return $Pkg;
6864 }
6865 else
6866 { # *.tar.gz
6867 my $TarCmd = get_CmdPath("tar");
6868 if(not $TarCmd) {
6869 exitStatus("Not_Found", "can't find \"tar\"");
6870 }
6871 my $GzipCmd = get_CmdPath("gzip");
6872 if(not $GzipCmd) {
6873 exitStatus("Not_Found", "can't find \"gzip\"");
6874 }
6875 my $Pkg = abs_path($To)."/".$Name.".tar.gz";
6876 unlink($Pkg);
6877 chdir($From);
6878 system($TarCmd, "-czf", $Pkg, $Name);
6879 if($?)
6880 { # cannot allocate memory (or other problems with "tar")
6881 unlink($Path);
6882 exitStatus("Error", "can't pack the ABI dump: ".$!);
6883 }
6884 chdir($ORIG_DIR);
6885 unlink($Path);
6886 return $To."/".$Name.".tar.gz";
6887 }
6888}
6889
6890sub is_header_file($)
6891{
6892 if($_[0]=~/\.($HEADER_EXT)\Z/i) {
6893 return $_[0];
6894 }
6895 return 0;
6896}
6897
6898sub is_not_header($)
6899{
6900 if($_[0]=~/\.\w+\Z/
6901 and $_[0]!~/\.($HEADER_EXT)\Z/i) {
6902 return 1;
6903 }
6904 return 0;
6905}
6906
6907sub is_header($$$)
6908{
6909 my ($Header, $UserDefined, $LibVersion) = @_;
6910 return 0 if(-d $Header);
6911 if(-f $Header) {
6912 $Header = get_abs_path($Header);
6913 }
6914 else
6915 {
6916 if(is_abs($Header))
6917 { # incorrect absolute path
6918 return 0;
6919 }
6920 if(my $HPath = identify_header($Header, $LibVersion)) {
6921 $Header = $HPath;
6922 }
6923 else
6924 { # can't find header
6925 return 0;
6926 }
6927 }
6928 if($Header=~/\.\w+\Z/)
6929 { # have an extension
6930 return is_header_file($Header);
6931 }
6932 else
6933 {
6934 if($UserDefined==2)
6935 { # specified on the command line
6936 if(cmd_file($Header)!~/HTML|XML/i) {
6937 return $Header;
6938 }
6939 }
6940 elsif($UserDefined)
6941 { # specified in the XML-descriptor
6942 # header file without an extension
6943 return $Header;
6944 }
6945 else
6946 {
6947 if(cmd_file($Header)=~/C[\+]*\s+program/i)
6948 { # !~/HTML|XML|shared|dynamic/i
6949 return $Header;
6950 }
6951 }
6952 }
6953 return 0;
6954}
6955
6956sub detectTargetHeaders($)
6957{
6958 my $LibVersion = $_[0];
6959 foreach my $RegHeader (keys(%{$Registered_Headers{$LibVersion}}))
6960 {
6961 my $RegDir = get_dirname($RegHeader);
6962 $TargetHeaders{$LibVersion}{get_filename($RegHeader)}=1;
6963 foreach my $RecInc (keys(%{$RecursiveIncludes{$LibVersion}{$RegHeader}}))
6964 {
6965 my $Dir = get_dirname($RecInc);
6966 if($Dir=~/\A$RegDir([\/\\]|\Z)/)
6967 { # in the same directory
6968 $TargetHeaders{$LibVersion}{get_filename($RecInc)}=1;
6969 }
6970 }
6971 }
6972}
6973
6974sub readHeaders($)
6975{
6976 $Version = $_[0];
6977 printMsg("INFO", "checking header(s) ".$Descriptor{$Version}{"Version"}." ...");
6978 my $DumpPath = getDump();
6979 if(not $DumpPath) {
6980 exitStatus("Cannot_Compile", "can't compile header(s)");
6981 }
6982 if($Debug)
6983 { # debug mode
6984 mkpath($DEBUG_PATH{$Version});
6985 copy($DumpPath, $DEBUG_PATH{$Version}."/translation-unit.txt");
6986 }
6987 getInfo($DumpPath);
6988 if($CheckHeadersOnly)
6989 { # --headers-only mode
6990 detectTargetHeaders($Version);
6991 }
6992}
6993
6994sub prepareTypes($)
6995{
6996 my $LibVersion = $_[0];
6997 if(cmpVersions($UsedDump{$LibVersion}{"V"}, "2.0")<0)
6998 { # support for old ABI dumps
6999 # type names have been corrected in ACC 1.22 (dump 2.0 format)
7000 foreach my $TypeDeclId (keys(%{$TypeInfo{$LibVersion}}))
7001 {
7002 foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}{$TypeDeclId}}))
7003 {
7004 my $TName = $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"Name"};
7005 if($TName=~/\A(\w+)::(\w+)/) {
7006 my ($P1, $P2) = ($1, $2);
7007 if($P1 eq $P2) {
7008 $TName=~s/\A$P1:\:$P1(\W)/$P1$1/;
7009 }
7010 else {
7011 $TName=~s/\A(\w+:\:)$P2:\:$P2(\W)/$1$P2$2/;
7012 }
7013 }
7014 $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"Name"} = $TName;
7015 }
7016 }
7017 }
7018 if(cmpVersions($UsedDump{$LibVersion}{"V"}, "2.5")<0)
7019 { # support for old ABI dumps
7020 # V < 2.5: array size == "number of elements"
7021 # V >= 2.5: array size in bytes
7022 foreach my $TypeDeclId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
7023 {
7024 foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$TypeDeclId}}))
7025 {
7026 my %Type = get_PureType($TypeDeclId, $TypeId, $LibVersion);
7027 if($Type{"Type"} eq "Array")
7028 {
7029 if($Type{"Size"})
7030 { # array[N]
7031 my %Base = get_OneStep_BaseType($Type{"TDid"}, $Type{"Tid"}, $LibVersion);
7032 $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"Size"} = $Type{"Size"}*$Base{"Size"};
7033 }
7034 else
7035 { # array[] is a pointer
7036 $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"Size"} = $WORD_SIZE{$LibVersion};
7037 }
7038 }
7039 }
7040 }
7041 }
7042 my $V2 = ($LibVersion==1)?2:1;
7043 if(cmpVersions($UsedDump{1}{"V"}, "2.7")<0)
7044 { # support for old ABI dumps
7045 # size of "method ptr" corrected in 2.7
7046 foreach my $TypeDeclId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
7047 {
7048 foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$TypeDeclId}}))
7049 {
7050 my %PureType = get_PureType($TypeDeclId, $TypeId, $LibVersion);
7051 if($PureType{"Type"} eq "MethodPtr")
7052 {
7053 my %Type = get_Type($TypeDeclId, $TypeId, $LibVersion);
7054 my $TypeId_2 = getTypeIdByName($PureType{"Name"}, $V2);
7055 my %Type2 = get_Type($Tid_TDid{$V2}{$TypeId_2}, $TypeId_2, $V2);
7056 if($Type{"Size"} ne $Type2{"Size"}) {
7057 $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"Size"} = $Type2{"Size"};
7058 }
7059 }
7060 }
7061 }
7062 }
7063}
7064
7065sub prepareInterfaces($)
7066{
7067 my $LibVersion = $_[0];
7068 my $Remangle = 0;
7069 if(($UsedDump{1}{"V"} and cmpVersions($UsedDump{1}{"V"}, "2.10")<0)
7070 or ($UsedDump{2}{"V"} and cmpVersions($UsedDump{2}{"V"}, "2.10")<0))
7071 { # different formats
7072 $Remangle = 1;
7073 }
7074 if($CheckHeadersOnly)
7075 { # different languages
7076 if($UserLang)
7077 { # --lang=LANG for both versions
7078 if(($UsedDump{1}{"V"} and $UserLang ne $UsedDump{1}{"L"})
7079 or ($UsedDump{2}{"V"} and $UserLang ne $UsedDump{2}{"L"}))
7080 {
7081 if($UserLang eq "C++")
7082 { # remangle symbols
7083 $Remangle = 1;
7084 }
7085 elsif($UserLang eq "C")
7086 { # remove mangling
7087 $Remangle = -1;
7088 }
7089 }
7090 }
7091 }
7092 foreach my $FuncInfoId (sort {int($b)<=>int($a)} keys(%{$SymbolInfo{$LibVersion}}))
7093 { # reverse order: D0, D1, D2, D0 (artificial, GCC < 4.5), C1, C2
7094 if($SymbolInfo{$LibVersion}{$FuncInfoId}{"Destructor"})
7095 {
7096 if(defined $SymbolInfo{$LibVersion}{$FuncInfoId}{"Param"}
7097 and keys(%{$SymbolInfo{$LibVersion}{$FuncInfoId}{"Param"}})
7098 and $SymbolInfo{$LibVersion}{$FuncInfoId}{"Param"}{"0"}{"name"})
7099 { # support for old GCC < 4.5: skip artificial ~dtor(int __in_chrg)
7100 # + support for old ABI dumps
7101 next;
7102 }
7103 }
7104 my $MnglName = $SymbolInfo{$LibVersion}{$FuncInfoId}{"MnglName"};
7105 if($Remangle==1)
7106 { # support for old ABI dumps: some symbols are not mangled in old dumps
7107 # mangle both sets of symbols (old and new)
7108 # NOTE: remangling all symbols by the same mangler
7109 if($MnglName=~/\A_ZN(V|)K/)
7110 { # mangling may be incorrect on old ABI dumps
7111 # because of absent "Const" attribute
7112 $SymbolInfo{$LibVersion}{$FuncInfoId}{"Const"} = 1;
7113 }
7114 if($MnglName=~/\A_ZN(K|)V/)
7115 { # mangling may be incorrect on old ABI dumps
7116 # because of absent "Volatile" attribute
7117 $SymbolInfo{$LibVersion}{$FuncInfoId}{"Volatile"} = 1;
7118 }
7119 if(($SymbolInfo{$LibVersion}{$FuncInfoId}{"Class"} and ($MnglName!~/\A_Z/ or not link_symbol($MnglName, $LibVersion, "-Deps")))
7120 or (not $SymbolInfo{$LibVersion}{$FuncInfoId}{"Class"} and $CheckHeadersOnly))
7121 { # GCC >= 4.0
7122 # remangling C++-functions (not mangled in the TU dump)
7123 # remangling broken C++-methods (without a mangled name)
7124 # remangling all inline virtual C++-methods
7125 if($MnglName = mangle_symbol($FuncInfoId, $LibVersion, "GCC"))
7126 {
7127 $SymbolInfo{$LibVersion}{$FuncInfoId}{"MnglName"} = $MnglName;
7128 $MangledNames{$LibVersion}{$MnglName} = 1;
7129 }
7130 }
7131 }
7132 elsif($Remangle==-1)
7133 { # remove mangling
7134 $MnglName = "";
7135 $SymbolInfo{$LibVersion}{$FuncInfoId}{"MnglName"} = "";
7136 }
7137 if(not $MnglName)
7138 { # ABI dumps don't contain mangled names for C-functions
7139 $MnglName = $SymbolInfo{$LibVersion}{$FuncInfoId}{"ShortName"};
7140 $SymbolInfo{$LibVersion}{$FuncInfoId}{"MnglName"} = $MnglName;
7141 }
7142 if(not $MnglName) {
7143 next;
7144 }
7145 if(not $CompleteSignature{$LibVersion}{$MnglName}{"MnglName"})
7146 { # NOTE: global data may enter here twice
7147 %{$CompleteSignature{$LibVersion}{$MnglName}} = %{$SymbolInfo{$LibVersion}{$FuncInfoId}};
7148 }
7149 if($UsedDump{$LibVersion}{"V"}
7150 and cmpVersions($UsedDump{$LibVersion}{"V"}, "2.6")<0)
7151 { # support for old dumps
7152 # add "Volatile" attribute
7153 if($MnglName=~/_Z(K|)V/) {
7154 $CompleteSignature{$LibVersion}{$MnglName}{"Volatile"}=1;
7155 }
7156 }
7157 # symbol and its symlink have same signatures
7158 if($SymVer{$LibVersion}{$MnglName}) {
7159 %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$MnglName}}} = %{$SymbolInfo{$LibVersion}{$FuncInfoId}};
7160 }
7161 delete($SymbolInfo{$LibVersion}{$FuncInfoId});
7162 }
7163 if($COMMON_LANGUAGE{$LibVersion} eq "C++" or $OSgroup eq "windows") {
7164 translateSymbols(keys(%{$CompleteSignature{$LibVersion}}), $LibVersion);
7165 }
7166 if($ExtendedCheck)
7167 { # --ext option
7168 addExtension($LibVersion);
7169 }
7170 if(not keys(%{$CompleteSignature{$LibVersion}}))
7171 { # check if input is valid
7172 if(not $ExtendedCheck and not $CheckObjectsOnly)
7173 {
7174 if($CheckHeadersOnly) {
7175 exitStatus("Empty_Set", "the set of public symbols is empty (".$Descriptor{$LibVersion}{"Version"}.")");
7176 }
7177 else {
7178 exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection (".$Descriptor{$LibVersion}{"Version"}.")");
7179 }
7180 }
7181 }
7182 foreach my $MnglName (keys(%{$CompleteSignature{$LibVersion}}))
7183 { # detect allocable classes with public exported constructors
7184 # or classes with auto-generated or inline-only constructors
7185 if(my $ClassId = $CompleteSignature{$LibVersion}{$MnglName}{"Class"})
7186 {
7187 my $ClassName = get_TypeName($ClassId, $LibVersion);
7188 if($CompleteSignature{$LibVersion}{$MnglName}{"Constructor"}
7189 and not $CompleteSignature{$LibVersion}{$MnglName}{"InLine"})
7190 { # Class() { ... } will not be exported
7191 if(not $CompleteSignature{$LibVersion}{$MnglName}{"Private"})
7192 {
7193 if(link_symbol($MnglName, $LibVersion, "-Deps")) {
7194 $AllocableClass{$LibVersion}{$ClassName} = 1;
7195 }
7196 }
7197 }
7198 if(not $CompleteSignature{$LibVersion}{$MnglName}{"Private"})
7199 { # all imported class methods
7200 if($CheckHeadersOnly)
7201 {
7202 if(not $CompleteSignature{$LibVersion}{$MnglName}{"InLine"}
7203 or $CompleteSignature{$LibVersion}{$MnglName}{"Virt"})
7204 {# all symbols except non-virtual inline
7205 $ClassMethods{$LibVersion}{$ClassName}{$MnglName} = 1;
7206 }
7207 }
7208 elsif(link_symbol($MnglName, $LibVersion, "-Deps"))
7209 { # all symbols
7210 $ClassMethods{$LibVersion}{$ClassName}{$MnglName} = 1;
7211 }
7212 }
7213 $ClassToId{$LibVersion}{$ClassName} = $ClassId;
7214 }
7215 if(my $RetId = $CompleteSignature{$LibVersion}{$MnglName}{"Return"})
7216 {
7217 my %Base = get_BaseType($Tid_TDid{$LibVersion}{$RetId}, $RetId, $LibVersion);
7218 if($Base{"Type"}=~/Struct|Class/)
7219 {
7220 my $Name = get_TypeName($Base{"Tid"}, $LibVersion);
7221 if($Name=~/<([^<>\s]+)>/)
7222 {
7223 if(my $Tid = getTypeIdByName($1, $LibVersion)) {
7224 $ReturnedClass{$LibVersion}{$Tid} = 1;
7225 }
7226 }
7227 else {
7228 $ReturnedClass{$LibVersion}{$Base{"Tid"}} = 1;
7229 }
7230 }
7231 }
7232 foreach my $Num (keys(%{$CompleteSignature{$LibVersion}{$MnglName}{"Param"}}))
7233 {
7234 my $PId = $CompleteSignature{$LibVersion}{$MnglName}{"Param"}{$Num}{"type"};
7235 if(get_PointerLevel($Tid_TDid{1}{$PId}, $PId, $LibVersion)>=1)
7236 {
7237 my %Base = get_BaseType($Tid_TDid{$LibVersion}{$PId}, $PId, $LibVersion);
7238 if($Base{"Type"}=~/Struct|Class/)
7239 {
7240 $ParamClass{$LibVersion}{$Base{"Tid"}}{$MnglName} = 1;
7241 foreach my $SubId (get_sub_classes($Base{"Tid"}, $LibVersion, 1))
7242 { # mark all derived classes
7243 $ParamClass{$LibVersion}{$SubId}{$MnglName} = 1;
7244 }
7245 }
7246 }
7247 }
7248 }
7249 foreach my $MnglName (keys(%{$VTableClass{$LibVersion}}))
7250 { # reconstruct header name for v-tables
7251 if($MnglName=~/\A_ZTV/)
7252 {
7253 if(my $ClassName = $VTableClass{$LibVersion}{$MnglName})
7254 {
7255 if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName}) {
7256 $CompleteSignature{$LibVersion}{$MnglName}{"Header"} = get_TypeAttr($ClassId, $LibVersion, "Header");
7257 }
7258 }
7259 }
7260 }
7261}
7262
7263sub addExtension($)
7264{
7265 my $LibVersion = $_[0];
7266 foreach my $TDid (keys(%{$TypeInfo{$LibVersion}}))
7267 {
7268 foreach my $Tid (keys(%{$TypeInfo{$LibVersion}{$TDid}}))
7269 {
7270 my $TType = $TypeInfo{$LibVersion}{$TDid}{$Tid}{"Type"};
7271 if($TType=~/Struct|Union|Enum|Class/)
7272 {
7273 my $HName = $TypeInfo{$LibVersion}{$TDid}{$Tid}{"Header"};
7274 if(not $HName or isBuiltIn($HName)) {
7275 next;
7276 }
7277 my $TName = $TypeInfo{$LibVersion}{$TDid}{$Tid}{"Name"};
7278 if(isAnon($TName))
7279 { # anon-struct-header.h-265
7280 next;
7281 }
7282 my $FuncName = "external_func_".$TName;
7283 $ExtendedFuncs{$FuncName}=1;
7284 my %Attrs = (
7285 "Header" => "extended.h",
7286 "ShortName" => $FuncName,
7287 "MnglName" => $FuncName,
7288 "Param" => { "0" => { "type"=>$Tid, "name"=>"p1" } }
7289 );
7290 %{$CompleteSignature{$LibVersion}{$FuncName}} = %Attrs;
7291 register_TypeUsing($TDid, $Tid, $LibVersion);
7292 $GeneratedSymbols{$FuncName}=1;
7293 $CheckedSymbols{$FuncName}=1;
7294 }
7295 }
7296 }
7297 my $ConstFunc = "external_func_0";
7298 $GeneratedSymbols{$ConstFunc}=1;
7299 $CheckedSymbols{$ConstFunc}=1;
7300}
7301
7302sub formatDump($)
7303{ # remove unnecessary data from the ABI dump
7304 my $LibVersion = $_[0];
7305 foreach my $FuncInfoId (keys(%{$SymbolInfo{$LibVersion}}))
7306 {
7307 my $MnglName = $SymbolInfo{$LibVersion}{$FuncInfoId}{"MnglName"};
7308 if(not $MnglName) {
7309 delete($SymbolInfo{$LibVersion}{$FuncInfoId});
7310 next;
7311 }
7312 if($MnglName eq $SymbolInfo{$LibVersion}{$FuncInfoId}{"ShortName"}) {
7313 delete($SymbolInfo{$LibVersion}{$FuncInfoId}{"MnglName"});
7314 }
7315 if(not is_target_header($SymbolInfo{$LibVersion}{$FuncInfoId}{"Header"}))
7316 { # user-defined header
7317 delete($SymbolInfo{$LibVersion}{$FuncInfoId});
7318 next;
7319 }
7320 if(not link_symbol($MnglName, $LibVersion, "-Deps")
7321 and not $SymbolInfo{$LibVersion}{$FuncInfoId}{"Virt"}
7322 and not $SymbolInfo{$LibVersion}{$FuncInfoId}{"PureVirt"})
7323 { # removing src only and all external functions
7324 if(not $CheckHeadersOnly) {
7325 delete($SymbolInfo{$LibVersion}{$FuncInfoId});
7326 next;
7327 }
7328 }
7329 if(not symbolFilter($MnglName, $LibVersion, "Public")) {
7330 delete($SymbolInfo{$LibVersion}{$FuncInfoId});
7331 next;
7332 }
7333 my %FuncInfo = %{$SymbolInfo{$LibVersion}{$FuncInfoId}};
7334 register_TypeUsing($Tid_TDid{$LibVersion}{$FuncInfo{"Return"}}, $FuncInfo{"Return"}, $LibVersion);
7335 register_TypeUsing($Tid_TDid{$LibVersion}{$FuncInfo{"Class"}}, $FuncInfo{"Class"}, $LibVersion);
7336 foreach my $Param_Pos (keys(%{$FuncInfo{"Param"}}))
7337 {
7338 my $Param_TypeId = $FuncInfo{"Param"}{$Param_Pos}{"type"};
7339 register_TypeUsing($Tid_TDid{$LibVersion}{$Param_TypeId}, $Param_TypeId, $LibVersion);
7340 }
7341 if(not keys(%{$SymbolInfo{$LibVersion}{$FuncInfoId}{"Param"}})) {
7342 delete($SymbolInfo{$LibVersion}{$FuncInfoId}{"Param"});
7343 }
7344 }
7345 foreach my $TDid (keys(%{$TypeInfo{$LibVersion}}))
7346 {
7347 if(not keys(%{$TypeInfo{$LibVersion}{$TDid}})) {
7348 delete($TypeInfo{$LibVersion}{$TDid});
7349 }
7350 else
7351 {
7352 foreach my $Tid (keys(%{$TypeInfo{$LibVersion}{$TDid}}))
7353 {
7354 if(not $UsedType{$LibVersion}{$TDid}{$Tid})
7355 {
7356 delete($TypeInfo{$LibVersion}{$TDid}{$Tid});
7357 if(not keys(%{$TypeInfo{$LibVersion}{$TDid}})) {
7358 delete($TypeInfo{$LibVersion}{$TDid});
7359 }
7360 if($Tid_TDid{$LibVersion}{$Tid} eq $TDid) {
7361 delete($Tid_TDid{$LibVersion}{$Tid});
7362 }
7363 }
7364 else
7365 { # clean attributes
7366 if(not $TypeInfo{$LibVersion}{$TDid}{$Tid}{"TDid"}) {
7367 delete($TypeInfo{$LibVersion}{$TDid}{$Tid}{"TDid"});
7368 }
7369 if(not $TypeInfo{$LibVersion}{$TDid}{$Tid}{"NameSpace"}) {
7370 delete($TypeInfo{$LibVersion}{$TDid}{$Tid}{"NameSpace"});
7371 }
7372 if(defined $TypeInfo{$LibVersion}{$TDid}{$Tid}{"BaseType"}
7373 and not $TypeInfo{$LibVersion}{$TDid}{$Tid}{"BaseType"}{"TDid"}) {
7374 delete($TypeInfo{$LibVersion}{$TDid}{$Tid}{"BaseType"}{"TDid"});
7375 }
7376 if(not $TypeInfo{$LibVersion}{$TDid}{$Tid}{"Header"}) {
7377 delete($TypeInfo{$LibVersion}{$TDid}{$Tid}{"Header"});
7378 }
7379 if(not $TypeInfo{$LibVersion}{$TDid}{$Tid}{"Line"}) {
7380 delete($TypeInfo{$LibVersion}{$TDid}{$Tid}{"Line"});
7381 }
7382 if(not $TypeInfo{$LibVersion}{$TDid}{$Tid}{"Size"}) {
7383 delete($TypeInfo{$LibVersion}{$TDid}{$Tid}{"Size"});
7384 }
7385 }
7386 }
7387 }
7388 }
7389}
7390
7391sub register_TypeUsing($$$)
7392{
7393 my ($TypeDeclId, $TypeId, $LibVersion) = @_;
7394 return if($UsedType{$LibVersion}{$TypeDeclId}{$TypeId});
7395 my %Type = get_Type($TypeDeclId, $TypeId, $LibVersion);
7396 if($Type{"Type"}=~/\A(Struct|Union|Class|FuncPtr|MethodPtr|FieldPtr|Enum)\Z/)
7397 {
7398 $UsedType{$LibVersion}{$TypeDeclId}{$TypeId} = 1;
7399 if($Type{"Type"}=~/\A(Struct|Class)\Z/)
7400 {
7401 if(my $ThisPtrId = getTypeIdByName(get_TypeName($TypeId, $LibVersion)."*const", $LibVersion))
7402 {# register "this" pointer
7403 my $ThisPtrDId = $Tid_TDid{$LibVersion}{$ThisPtrId};
7404 my %ThisPtrType = get_Type($ThisPtrDId, $ThisPtrId, $LibVersion);
7405 $UsedType{$LibVersion}{$ThisPtrDId}{$ThisPtrId} = 1;
7406 register_TypeUsing($ThisPtrType{"BaseType"}{"TDid"}, $ThisPtrType{"BaseType"}{"Tid"}, $LibVersion);
7407 }
7408 foreach my $BaseId (keys(%{$Type{"Base"}}))
7409 {# register base classes
7410 register_TypeUsing($Tid_TDid{$LibVersion}{$BaseId}, $BaseId, $LibVersion);
7411 }
7412 }
7413 foreach my $Memb_Pos (keys(%{$Type{"Memb"}}))
7414 {
7415 my $Member_TypeId = $Type{"Memb"}{$Memb_Pos}{"type"};
7416 register_TypeUsing($Tid_TDid{$LibVersion}{$Member_TypeId}, $Member_TypeId, $LibVersion);
7417 }
7418 if($Type{"Type"} eq "FuncPtr"
7419 or $Type{"Type"} eq "MethodPtr") {
7420 my $ReturnType = $Type{"Return"};
7421 register_TypeUsing($Tid_TDid{$LibVersion}{$ReturnType}, $ReturnType, $LibVersion);
7422 foreach my $Memb_Pos (keys(%{$Type{"Param"}}))
7423 {
7424 my $Member_TypeId = $Type{"Param"}{$Memb_Pos}{"type"};
7425 register_TypeUsing($Tid_TDid{$LibVersion}{$Member_TypeId}, $Member_TypeId, $LibVersion);
7426 }
7427 }
7428 }
7429 elsif($Type{"Type"}=~/\A(Const|Pointer|Ref|Volatile|Restrict|Array|Typedef)\Z/)
7430 {
7431 $UsedType{$LibVersion}{$TypeDeclId}{$TypeId} = 1;
7432 register_TypeUsing($Type{"BaseType"}{"TDid"}, $Type{"BaseType"}{"Tid"}, $LibVersion);
7433 }
7434 elsif($Type{"Type"} eq "Intrinsic") {
7435 $UsedType{$LibVersion}{$TypeDeclId}{$TypeId} = 1;
7436 }
7437 else
7438 {
7439 delete($TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId});
7440 if(not keys(%{$TypeInfo{$LibVersion}{$TypeDeclId}})) {
7441 delete($TypeInfo{$LibVersion}{$TypeDeclId});
7442 }
7443 if($Tid_TDid{$LibVersion}{$TypeId} eq $TypeDeclId) {
7444 delete($Tid_TDid{$LibVersion}{$TypeId});
7445 }
7446 }
7447}
7448
7449sub findMethod($$$)
7450{
7451 my ($VirtFunc, $ClassId, $LibVersion) = @_;
7452 foreach my $BaseClass_Id (keys(%{$TypeInfo{$LibVersion}{$Tid_TDid{$LibVersion}{$ClassId}}{$ClassId}{"Base"}}))
7453 {
7454 if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LibVersion)) {
7455 return $VirtMethodInClass;
7456 }
7457 elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LibVersion)) {
7458 return $VirtMethodInBaseClasses;
7459 }
7460 }
7461 return "";
7462}
7463
7464sub findMethod_Class($$$)
7465{
7466 my ($VirtFunc, $ClassId, $LibVersion) = @_;
7467 my $ClassName = get_TypeName($ClassId, $LibVersion);
7468 return "" if(not defined $VirtualTable{$LibVersion}{$ClassName});
7469 my $TargetSuffix = get_symbol_suffix($VirtFunc, 1);
7470 my $TargetShortName = $CompleteSignature{$LibVersion}{$VirtFunc}{"ShortName"};
7471 foreach my $Candidate (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
7472 { # search for interface with the same parameters suffix (overridden)
7473 if($TargetSuffix eq get_symbol_suffix($Candidate, 1))
7474 {
7475 if($CompleteSignature{$LibVersion}{$VirtFunc}{"Destructor"}) {
7476 if($CompleteSignature{$LibVersion}{$Candidate}{"Destructor"}) {
7477 if(($VirtFunc=~/D0E/ and $Candidate=~/D0E/)
7478 or ($VirtFunc=~/D1E/ and $Candidate=~/D1E/)
7479 or ($VirtFunc=~/D2E/ and $Candidate=~/D2E/)) {
7480 return $Candidate;
7481 }
7482 }
7483 }
7484 else {
7485 if($TargetShortName eq $CompleteSignature{$LibVersion}{$Candidate}{"ShortName"}) {
7486 return $Candidate;
7487 }
7488 }
7489 }
7490 }
7491 return "";
7492}
7493
7494sub registerVirtualTable($)
7495{
7496 my $LibVersion = $_[0];
7497 foreach my $Interface (keys(%{$CompleteSignature{$LibVersion}}))
7498 {
7499 if($CompleteSignature{$LibVersion}{$Interface}{"Virt"}
7500 or $CompleteSignature{$LibVersion}{$Interface}{"PureVirt"})
7501 {
7502 my $ClassName = get_TypeName($CompleteSignature{$LibVersion}{$Interface}{"Class"}, $LibVersion);
7503 next if(not $STDCXX_TESTING and $ClassName=~/\A(std::|__cxxabi)/);
7504 if($CompleteSignature{$LibVersion}{$Interface}{"Destructor"}
7505 and $Interface=~/D2E/)
7506 { # pure virtual D2-destructors are marked as "virt" in the dump
7507 # virtual D2-destructors are NOT marked as "virt" in the dump
7508 # both destructors are not presented in the v-table
7509 next;
7510 }
7511 my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Interface);
7512 $VirtualTable{$LibVersion}{$ClassName}{$MnglName} = 1;
7513 }
7514 if($CheckHeadersOnly
7515 and $CompleteSignature{$LibVersion}{$Interface}{"Virt"})
7516 { # Register added and removed virtual symbols
7517 # This is necessary for --headers-only mode
7518 # Virtual function cannot be inline, so:
7519 # presence in headers <=> presence in shared libs
7520 if($LibVersion==2 and not $CompleteSignature{1}{$Interface}{"Header"})
7521 { # not presented in old-version headers
7522 $AddedInt{$Interface} = 1;
7523 }
7524 if($LibVersion==1 and not $CompleteSignature{2}{$Interface}{"Header"})
7525 { # not presented in new-version headers
7526 $RemovedInt{$Interface} = 1;
7527 }
7528 }
7529 }
7530}
7531
7532sub registerOverriding($)
7533{
7534 my $LibVersion = $_[0];
7535 my @Classes = keys(%{$VirtualTable{$LibVersion}});
7536 @Classes = sort {int($ClassToId{$LibVersion}{$a})<=>int($ClassToId{$LibVersion}{$b})} @Classes;
7537 foreach my $ClassName (@Classes)
7538 {
7539 foreach my $VirtFunc (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
7540 {
7541 next if($CompleteSignature{$LibVersion}{$VirtFunc}{"PureVirt"});
7542 if(my $OverriddenMethod = findMethod($VirtFunc, $TName_Tid{$LibVersion}{$ClassName}, $LibVersion))
7543 { # both overridden virtual and implemented pure virtual functions
7544 $CompleteSignature{$LibVersion}{$VirtFunc}{"Override"} = $OverriddenMethod;
7545 $OverriddenMethods{$LibVersion}{$OverriddenMethod}{$VirtFunc} = 1;
7546 delete($VirtualTable{$LibVersion}{$ClassName}{$VirtFunc});
7547 }
7548 }
7549 if(not keys(%{$VirtualTable{$LibVersion}{$ClassName}})) {
7550 delete($VirtualTable{$LibVersion}{$ClassName});
7551 }
7552 }
7553}
7554
7555sub setVirtFuncPositions($)
7556{
7557 my $LibVersion = $_[0];
7558 foreach my $ClassName (keys(%{$VirtualTable{$LibVersion}}))
7559 {
7560 my ($Num, $RelPos, $AbsNum) = (1, 0, 1);
7561 foreach my $VirtFunc (sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})}
7562 sort keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
7563 {
7564 if(not $CompleteSignature{1}{$VirtFunc}{"Override"}
7565 and not $CompleteSignature{2}{$VirtFunc}{"Override"})
7566 {
7567 if(defined $VirtualTable{1}{$ClassName} and defined $VirtualTable{1}{$ClassName}{$VirtFunc}
7568 and defined $VirtualTable{2}{$ClassName} and defined $VirtualTable{2}{$ClassName}{$VirtFunc})
7569 { # relative position excluding added and removed virtual functions
7570 $CompleteSignature{$LibVersion}{$VirtFunc}{"RelPos"} = $RelPos++;
7571 }
7572 $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc}=$Num++;
7573 }
7574
7575 }
7576 }
7577 foreach my $ClassName (keys(%{$ClassToId{$LibVersion}}))
7578 {
7579 my $AbsNum = 1;
7580 foreach my $VirtFunc (getVTable($ClassToId{$LibVersion}{$ClassName}, $LibVersion)) {
7581 $VirtualTable_Full{$LibVersion}{$ClassName}{$VirtFunc}=$AbsNum++;
7582 }
7583 }
7584}
7585
7586sub get_sub_classes($$$)
7587{
7588 my ($ClassId, $LibVersion, $Recursive) = @_;
7589 return () if(not defined $Class_SubClasses{$LibVersion}{$ClassId});
7590 my @Subs = ();
7591 foreach my $SubId (keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
7592 {
7593 if($Recursive) {
7594 foreach my $SubSubId (get_sub_classes($SubId, $LibVersion, $Recursive)) {
7595 push(@Subs, $SubSubId);
7596 }
7597 }
7598 push(@Subs, $SubId);
7599 }
7600 return @Subs;
7601}
7602
7603sub get_base_classes($$$)
7604{
7605 my ($ClassId, $LibVersion, $Recursive) = @_;
7606 my %ClassType = get_Type($Tid_TDid{$LibVersion}{$ClassId}, $ClassId, $LibVersion);
7607 return () if(not defined $ClassType{"Base"});
7608 my @Bases = ();
7609 foreach my $BaseId (sort {int($ClassType{"Base"}{$a}{"pos"})<=>int($ClassType{"Base"}{$b}{"pos"})}
7610 keys(%{$ClassType{"Base"}}))
7611 {
7612 if($Recursive) {
7613 foreach my $SubBaseId (get_base_classes($BaseId, $LibVersion, $Recursive)) {
7614 push(@Bases, $SubBaseId);
7615 }
7616 }
7617 push(@Bases, $BaseId);
7618 }
7619 return @Bases;
7620}
7621
7622sub getVTable($$)
7623{# return list of v-table elements
7624 my ($ClassId, $LibVersion) = @_;
7625 my @Bases = get_base_classes($ClassId, $LibVersion, 1);
7626 my @Elements = ();
7627 foreach my $BaseId (@Bases, $ClassId)
7628 {
7629 my $BName = get_TypeName($BaseId, $LibVersion);
7630 my @VFunctions = keys(%{$VirtualTable{$LibVersion}{$BName}});
7631 @VFunctions = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @VFunctions;
7632 foreach my $VFunc (@VFunctions)
7633 {
7634 push(@Elements, $VFunc);
7635 }
7636 }
7637 return @Elements;
7638}
7639
7640sub getVShift($$)
7641{
7642 my ($ClassId, $LibVersion) = @_;
7643 my @Bases = get_base_classes($ClassId, $LibVersion, 1);
7644 my $VShift = 0;
7645 foreach my $BaseId (@Bases)
7646 {
7647 my $BName = get_TypeName($BaseId, $LibVersion);
7648 if(defined $VirtualTable{$LibVersion}{$BName}) {
7649 $VShift+=keys(%{$VirtualTable{$LibVersion}{$BName}});
7650 }
7651 }
7652 return $VShift;
7653}
7654
7655sub getShift($$)
7656{
7657 my ($ClassId, $LibVersion) = @_;
7658 my @Bases = get_base_classes($ClassId, $LibVersion, 0);
7659 my $Shift = 0;
7660 foreach my $BaseId (@Bases)
7661 {
7662 my $Size = get_TypeSize($BaseId, $LibVersion);
7663 if($Size!=1) {
7664 # empty base class
7665 $Shift+=get_TypeSize($BaseId, $LibVersion);
7666 }
7667 }
7668 return $Shift;
7669}
7670
7671sub getVSize($$)
7672{
7673 my ($ClassName, $LibVersion) = @_;
7674 if(defined $VirtualTable{$LibVersion}{$ClassName}) {
7675 return keys(%{$VirtualTable{$LibVersion}{$ClassName}});
7676 }
7677 else {
7678 return 0;
7679 }
7680}
7681
7682sub isCopyingClass($$)
7683{
7684 my ($TypeId, $LibVersion) = @_;
7685 return $TypeInfo{$LibVersion}{$Tid_TDid{$LibVersion}{$TypeId}}{$TypeId}{"Copied"};
7686}
7687
7688sub isLeafClass($$)
7689{
7690 my ($ClassId, $LibVersion) = @_;
7691 return (not keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}));
7692}
7693
7694sub havePubFields($)
7695{# check structured type for public fields
7696 return isAccessible($_[0], (), 0, -1);
7697}
7698
7699sub isAccessible($$$$)
7700{# check interval in structured type for public fields
7701 my ($TypePtr, $Skip, $Start, $End) = @_;
7702 return 0 if(not $TypePtr);
7703 if($End==-1) {
7704 $End = keys(%{$TypePtr->{"Memb"}})-1;
7705 }
7706 foreach my $MemPos (keys(%{$TypePtr->{"Memb"}}))
7707 {
7708 if($Skip and $Skip->{$MemPos})
7709 { # skip removed/added fields
7710 next;
7711 }
7712 if(int($MemPos)>=$Start and int($MemPos)<=$End)
7713 {
7714 if(isPublic($TypePtr, $MemPos)) {
7715 return ($MemPos+1);
7716 }
7717 }
7718 }
7719 return 0;
7720}
7721
7722sub getAlignment($$$)
7723{
7724 my ($Pos, $TypePtr, $LibVersion) = @_;
7725 my $Tid = $TypePtr->{"Memb"}{$Pos}{"type"};
7726 my %Type = get_PureType($Tid_TDid{$LibVersion}{$Tid}, $Tid, $LibVersion);
7727 my $TSize = $Type{"Size"}*$BYTE_SIZE;
7728 my $MSize = $Type{"Size"}*$BYTE_SIZE;
7729 if(my $BSize = $TypePtr->{"Memb"}{$Pos}{"bitfield"})
7730 { # bitfields
7731 ($TSize, $MSize) = ($WORD_SIZE{$LibVersion}*$BYTE_SIZE, $BSize);
7732 }
7733 elsif($Type{"Type"} eq "Array")
7734 { # in the context of function parameter
7735 # it's passed through the pointer
7736 }
7737 # alignment
7738 my $Alignment = $WORD_SIZE{$LibVersion}*$BYTE_SIZE; # default
7739 if(my $Computed = $TypePtr->{"Memb"}{$Pos}{"algn"})
7740 { # computed by GCC
7741 $Alignment = $Computed*$BYTE_SIZE;
7742 }
7743 elsif($TypePtr->{"Memb"}{$Pos}{"bitfield"})
7744 { # bitfields are 1 bit aligned
7745 $Alignment = 1;
7746 }
7747 elsif($TSize and $TSize<$WORD_SIZE{$LibVersion}*$BYTE_SIZE)
7748 { # model
7749 $Alignment = $TSize;
7750 }
7751 return ($Alignment, $MSize);
7752}
7753
7754sub getOffset($$$)
7755{ # offset of the field including padding
7756 my ($FieldPos, $TypePtr, $LibVersion) = @_;
7757 my $Offset = 0;
7758 foreach my $Pos (0 .. keys(%{$TypePtr->{"Memb"}})-1)
7759 {
7760 my ($Alignment, $MSize) = getAlignment($Pos, $TypePtr, $LibVersion);
7761 # padding
7762 my $Padding = 0;
7763 if($Offset % $Alignment!=0)
7764 { # not aligned, add padding
7765 $Padding = $Alignment - $Offset % $Alignment;
7766 }
7767 $Offset += $Padding;
7768 if($Pos==$FieldPos)
7769 { # after the padding
7770 # before the field
7771 return $Offset;
7772 }
7773 $Offset += $MSize;
7774 }
7775 return $FieldPos;# if something is going wrong
7776}
7777
7778sub isMemPadded($$$$$)
7779{ # check if the target field can be added/removed/changed
7780 # without shifting other fields because of padding bits
7781 my ($FieldPos, $Size, $TypePtr, $Skip, $LibVersion) = @_;
7782 return 0 if($FieldPos==0);
7783 if(defined $TypePtr->{"Memb"}{""})
7784 {
7785 delete($TypePtr->{"Memb"}{""});
7786 if($Debug) {
7787 printMsg("WARNING", "internal error detected");
7788 }
7789 }
7790 my $Offset = 0;
7791 my (%Alignment, %MSize) = ();
7792 my $MaxAlgn = 0;
7793 my $End = keys(%{$TypePtr->{"Memb"}})-1;
7794 my $NextField = $FieldPos+1;
7795 foreach my $Pos (0 .. $End)
7796 {
7797 if($Skip and $Skip->{$Pos})
7798 { # skip removed/added fields
7799 if($Pos > $FieldPos)
7800 { # after the target
7801 $NextField += 1;
7802 next;
7803 }
7804 }
7805 ($Alignment{$Pos}, $MSize{$Pos}) = getAlignment($Pos, $TypePtr, $LibVersion);
7806 if($Alignment{$Pos}>$MaxAlgn) {
7807 $MaxAlgn = $Alignment{$Pos};
7808 }
7809 if($Pos==$FieldPos)
7810 {
7811 if($Size==-1)
7812 { # added/removed fields
7813 if($Pos!=$End)
7814 { # skip target field and see
7815 # if enough padding will be
7816 # created on the next step
7817 # to include this field
7818 next;
7819 }
7820 }
7821 }
7822 # padding
7823 my $Padding = 0;
7824 if($Offset % $Alignment{$Pos}!=0)
7825 { # not aligned, add padding
7826 $Padding = $Alignment{$Pos} - $Offset % $Alignment{$Pos};
7827 }
7828 if($Pos==$NextField)
7829 { # try to place target field in the padding
7830 if($Size==-1)
7831 { # added/removed fields
7832 my $TPadding = 0;
7833 if($Offset % $Alignment{$FieldPos}!=0)
7834 {# padding of the target field
7835 $TPadding = $Alignment{$FieldPos} - $Offset % $Alignment{$FieldPos};
7836 }
7837 if($TPadding+$MSize{$FieldPos}<=$Padding)
7838 { # enough padding to place target field
7839 return 1;
7840 }
7841 else {
7842 return 0;
7843 }
7844 }
7845 else
7846 { # changed fields
7847 my $Delta = $Size-$MSize{$FieldPos};
7848 if($Delta>=0)
7849 { # increased
7850 if($Size-$MSize{$FieldPos}<=$Padding)
7851 { # enough padding to change target field
7852 return 1;
7853 }
7854 else {
7855 return 0;
7856 }
7857 }
7858 else
7859 { # decreased
7860 $Delta = abs($Delta);
7861 if($Delta+$Padding>=$MSize{$Pos})
7862 { # try to place the next field
7863 if(($Offset-$Delta) % $Alignment{$Pos} != 0)
7864 { # padding of the next field in new place
7865 my $NPadding = $Alignment{$Pos} - ($Offset-$Delta) % $Alignment{$Pos};
7866 if($NPadding+$MSize{$Pos}<=$Delta+$Padding)
7867 { # enough delta+padding to store next field
7868 return 0;
7869 }
7870 }
7871 else
7872 {
7873 return 0;
7874 }
7875 }
7876 return 1;
7877 }
7878 }
7879 }
7880 elsif($Pos==$End)
7881 { # target field is the last field
7882 if($Size==-1)
7883 { # added/removed fields
7884 if($Offset % $MaxAlgn!=0)
7885 { # tail padding
7886 my $TailPadding = $MaxAlgn - $Offset % $MaxAlgn;
7887 if($Padding+$MSize{$Pos}<=$TailPadding)
7888 { # enough tail padding to place the last field
7889 return 1;
7890 }
7891 }
7892 return 0;
7893 }
7894 else
7895 { # changed fields
7896 # scenario #1
7897 my $Offset1 = $Offset+$Padding+$MSize{$Pos};
7898 if($Offset1 % $MaxAlgn != 0)
7899 { # tail padding
7900 $Offset1 += $MaxAlgn - $Offset1 % $MaxAlgn;
7901 }
7902 # scenario #2
7903 my $Offset2 = $Offset+$Padding+$Size;
7904 if($Offset2 % $MaxAlgn != 0)
7905 { # tail padding
7906 $Offset2 += $MaxAlgn - $Offset2 % $MaxAlgn;
7907 }
7908 if($Offset1!=$Offset2)
7909 { # different sizes of structure
7910 return 0;
7911 }
7912 return 1;
7913 }
7914 }
7915 $Offset += $Padding+$MSize{$Pos};
7916 }
7917 return 0;
7918}
7919
7920sub isReserved($)
7921{ # reserved fields == private
7922 my $MName = $_[0];
7923 if($MName=~/reserved|padding|f_spare/i) {
7924 return 1;
7925 }
7926 if($MName=~/\A[_]*(spare|pad|unused)[_]*\Z/i) {
7927 return 1;
7928 }
7929 if($MName=~/(pad\d+)/i) {
7930 return 1;
7931 }
7932 return 0;
7933}
7934
7935sub isPublic($$)
7936{
7937 my ($TypePtr, $FieldPos) = @_;
7938 return 0 if(not $TypePtr);
7939 return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos});
7940 return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}{"name"});
7941 if(not $TypePtr->{"Memb"}{$FieldPos}{"access"})
7942 { # by name in C language
7943 # FIXME: add other methods to detect private members
7944 my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"};
7945 if($MName=~/priv|abidata|parent_object/i)
7946 { # C-styled private data
7947 return 0;
7948 }
7949 if(lc($MName) eq "abi")
7950 { # ABI information/reserved field
7951 return 0;
7952 }
7953 if(isReserved($MName))
7954 { # reserved fields
7955 return 0;
7956 }
7957 return 1;
7958 }
7959 elsif($TypePtr->{"Memb"}{$FieldPos}{"access"} ne "private")
7960 { # by access in C++ language
7961 return 1;
7962 }
7963 return 0;
7964}
7965
7966sub cmpVTables_Model($)
7967{
7968 my $ClassName = $_[0];
7969 foreach my $Symbol (keys(%{$VirtualTable_Full{1}{$ClassName}}))
7970 {
7971 if(not defined $VirtualTable_Full{2}{$ClassName}{$Symbol}) {
7972 return 1;
7973 }
7974 }
7975 return 0;
7976}
7977
7978sub cmpVTables($$)
7979{
7980 my ($ClassName, $Strong) = @_;
7981 my $ClassId1 = $ClassToId{1}{$ClassName};
7982 my $ClassId2 = $ClassToId{2}{$ClassName};
7983 my %Type1 = get_Type($Tid_TDid{1}{$ClassId1}, $ClassId1, 1);
7984 my %Type2 = get_Type($Tid_TDid{2}{$ClassId2}, $ClassId2, 2);
7985 if(not defined $Type1{"VTable"}
7986 or not defined $Type2{"VTable"})
7987 { # old ABI dumps
7988 return 0;
7989 }
7990 my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
7991 foreach my $Offset (sort {int($a)<=>int($b)} keys(%Indexes))
7992 {
7993 if(not defined $Type1{"VTable"}{$Offset})
7994 { # v-table v.1 < v-table v.2
7995 return $Strong;
7996 }
7997 my $Entry1 = $Type1{"VTable"}{$Offset};
7998 if(not defined $Type2{"VTable"}{$Offset})
7999 { # v-table v.1 > v-table v.2
8000 return $Strong;
8001 }
8002 my $Entry2 = $Type2{"VTable"}{$Offset};
8003 $Entry1 = simpleVEntry($Entry1);
8004 $Entry2 = simpleVEntry($Entry2);
8005 if($Entry1 ne $Entry2)
8006 { # register as changed
8007 if($Entry1=~/::([^:]+)\Z/)
8008 {
8009 my $M1 = $1;
8010 if($Entry2=~/::([^:]+)\Z/)
8011 {
8012 my $M2 = $1;
8013 if($M1 eq $M2)
8014 { # overridden
8015 next;
8016 }
8017 }
8018 }
8019 return 1;
8020 }
8021 }
8022 return 0;
8023}
8024
8025sub mergeVTables()
8026{ # merging v-tables without diagnostics
8027 foreach my $ClassName (keys(%{$VirtualTable{1}}))
8028 {
8029 if($VTableChanged{$ClassName})
8030 { # already registered
8031 next;
8032 }
8033 if(cmpVTables($ClassName, 0))
8034 {
8035 my @Affected = (keys(%{$ClassMethods{1}{$ClassName}}));
8036 foreach my $Symbol (@Affected)
8037 {
8038 %{$CompatProblems{$Symbol}{"Virtual_Table_Changed_Unknown"}{$ClassName}}=(
8039 "Type_Name"=>$ClassName,
8040 "Type_Type"=>"Class",
8041 "Target"=>$ClassName);
8042 }
8043 }
8044 }
8045}
8046
8047sub mergeBases()
8048{
8049 foreach my $ClassName (keys(%{$ClassToId{1}}))
8050 { # detect added and removed virtual functions
8051 my $ClassId = $ClassToId{1}{$ClassName};
8052 next if(not $ClassId);
8053 foreach my $VirtFunc (keys(%{$VirtualTable{2}{$ClassName}}))
8054 {
8055 if($ClassToId{1}{$ClassName}
8056 and not defined $VirtualTable{1}{$ClassName}{$VirtFunc})
8057 { # added to v-table
8058 if(not $CompleteSignature{2}{$VirtFunc}{"Override"}) {
8059 $AddedInt_Virt{$ClassName}{$VirtFunc} = 1;
8060 }
8061 }
8062 }
8063 foreach my $VirtFunc (keys(%{$VirtualTable{1}{$ClassName}}))
8064 {
8065 if($ClassToId{2}{$ClassName}
8066 and not defined $VirtualTable{2}{$ClassName}{$VirtFunc})
8067 { # removed from v-table
8068 if(not $CompleteSignature{1}{$VirtFunc}{"Override"}) {
8069 $RemovedInt_Virt{$ClassName}{$VirtFunc} = 1;
8070 }
8071 }
8072 }
8073 my %Class_Type = get_Type($Tid_TDid{1}{$ClassId}, $ClassId, 1);
8074 foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$ClassName}}))
8075 { # check replacements, including pure virtual methods
8076 my $AddedPos = $VirtualTable{2}{$ClassName}{$AddedVFunc};
8077 foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$ClassName}}))
8078 {
8079 my $RemovedPos = $VirtualTable{1}{$ClassName}{$RemovedVFunc};
8080 if($AddedPos==$RemovedPos)
8081 {
8082 $VirtualReplacement{$AddedVFunc} = $RemovedVFunc;
8083 $VirtualReplacement{$RemovedVFunc} = $AddedVFunc;
8084 last;# other methods will be reported as "added" or "removed"
8085 }
8086 }
8087 if(my $RemovedVFunc = $VirtualReplacement{$AddedVFunc})
8088 {
8089 if(lc($AddedVFunc) eq lc($RemovedVFunc))
8090 { # skip: DomUi => DomUI parameter (qt 4.2.3 to 4.3.0)
8091 next;
8092 }
8093 my $ProblemType = "Virtual_Replacement";
8094 my @Affected = ($RemovedVFunc);
8095 if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
8096 { # pure methods
8097 if(not isUsedClass($ClassId, 1))
8098 { # not a parameter of some exported method
8099 next;
8100 }
8101 $ProblemType = "Pure_Virtual_Replacement";
8102 @Affected = (keys(%{$ClassMethods{1}{$ClassName}}))
8103 }
8104 foreach my $AffectedInt (@Affected)
8105 {
8106 if($CompleteSignature{1}{$AffectedInt}{"PureVirt"})
8107 { # affected exported methods only
8108 next;
8109 }
8110 %{$CompatProblems{$AffectedInt}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
8111 "Type_Name"=>$Class_Type{"Name"},
8112 "Type_Type"=>"Class",
8113 "Target"=>get_Signature($AddedVFunc, 2),
8114 "Old_Value"=>get_Signature($RemovedVFunc, 1));
8115 }
8116 }
8117 }
8118 }
8119 if($UsedDump{1}{"V"}
8120 and cmpVersions($UsedDump{1}{"V"}, "2.0")<0)
8121 { # support for old dumps
8122 # "Base" attribute introduced in ACC 1.22 (dump 2.0 format)
8123 return;
8124 }
8125 if($UsedDump{2}{"V"}
8126 and cmpVersions($UsedDump{2}{"V"}, "2.0")<0)
8127 { # support for old dumps
8128 # "Base" attribute introduced in ACC 1.22 (dump 2.0 format)
8129 return;
8130 }
8131 foreach my $ClassName (sort keys(%{$ClassToId{1}}))
8132 {
8133 my $ClassId_Old = $ClassToId{1}{$ClassName};
8134 next if(not $ClassId_Old);
8135 if(not isCreatable($ClassId_Old, 1))
8136 { # skip classes without public constructors (including auto-generated)
8137 # example: class has only a private exported or private inline constructor
8138 next;
8139 }
8140 if($ClassName=~/>/)
8141 { # skip affected template instances
8142 next;
8143 }
8144 my %Class_Old = get_Type($Tid_TDid{1}{$ClassId_Old}, $ClassId_Old, 1);
8145 my $ClassId_New = $ClassToId{2}{$ClassName};
8146 next if(not $ClassId_New);
8147 my %Class_New = get_Type($Tid_TDid{2}{$ClassId_New}, $ClassId_New, 2);
8148 my @Bases_Old = sort {$Class_Old{"Base"}{$a}{"pos"}<=>$Class_Old{"Base"}{$b}{"pos"}} keys(%{$Class_Old{"Base"}});
8149 my @Bases_New = sort {$Class_New{"Base"}{$a}{"pos"}<=>$Class_New{"Base"}{$b}{"pos"}} keys(%{$Class_New{"Base"}});
8150 my ($BNum1, $BNum2) = (1, 1);
8151 my %BasePos_Old = map {get_TypeName($_, 1) => $BNum1++} @Bases_Old;
8152 my %BasePos_New = map {get_TypeName($_, 2) => $BNum2++} @Bases_New;
8153 my %ShortBase_Old = map {get_TypeShort($_, 1) => 1} @Bases_Old;
8154 my %ShortBase_New = map {get_TypeShort($_, 2) => 1} @Bases_New;
8155 my $Shift_Old = getShift($ClassId_Old, 1);
8156 my $Shift_New = getShift($ClassId_New, 2);
8157 my %BaseId_New = map {get_TypeName($_, 2) => $_} @Bases_New;
8158 my ($Added, $Removed) = (0, 0);
8159 my @StableBases_Old = ();
8160 foreach my $BaseId (@Bases_Old)
8161 {
8162 my $BaseName = get_TypeName($BaseId, 1);
8163 if($BasePos_New{$BaseName}) {
8164 push(@StableBases_Old, $BaseId);
8165 }
8166 elsif(not $ShortBase_New{$BaseName}
8167 and not $ShortBase_New{get_TypeShort($BaseId, 1)})
8168 { # removed base
8169 # excluding namespace::SomeClass to SomeClass renaming
8170 my $ProblemKind = "Removed_Base_Class";
8171 if($Shift_Old ne $Shift_New)
8172 { # affected fields
8173 if(havePubFields(\%Class_Old)) {
8174 $ProblemKind .= "_And_Shift";
8175 }
8176 elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
8177 $ProblemKind .= "_And_Size";
8178 }
8179 }
8180 if(keys(%{$VirtualTable_Full{1}{$BaseName}})
8181 and (cmpVTables($ClassName, 1) or cmpVTables_Model($ClassName)))
8182 { # affected v-table
8183 $ProblemKind .= "_And_VTable";
8184 $VTableChanged{$ClassName}=1;
8185 }
8186 my @Affected = keys(%{$ClassMethods{1}{$ClassName}});
8187 foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
8188 {
8189 my $SubName = get_TypeName($SubId, 1);
8190 push(@Affected, keys(%{$ClassMethods{1}{$SubName}}));
8191 if($ProblemKind=~/VTable/) {
8192 $VTableChanged{$SubName}=1;
8193 }
8194 }
8195 foreach my $Interface (@Affected)
8196 {
8197 %{$CompatProblems{$Interface}{$ProblemKind}{"this"}}=(
8198 "Type_Name"=>$ClassName,
8199 "Type_Type"=>"Class",
8200 "Target"=>$BaseName,
8201 "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
8202 "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
8203 "Shift"=>abs($Shift_New-$Shift_Old) );
8204 }
8205 $Removed+=1;
8206 }
8207 }
8208 my @StableBases_New = ();
8209 foreach my $BaseId (@Bases_New)
8210 {
8211 my $BaseName = get_TypeName($BaseId, 2);
8212 if($BasePos_Old{$BaseName}) {
8213 push(@StableBases_New, $BaseId);
8214 }
8215 elsif(not $ShortBase_Old{$BaseName}
8216 and not $ShortBase_Old{get_TypeShort($BaseId, 2)})
8217 { # added base
8218 # excluding namespace::SomeClass to SomeClass renaming
8219 my $ProblemKind = "Added_Base_Class";
8220 if($Shift_Old ne $Shift_New)
8221 { # affected fields
8222 if(havePubFields(\%Class_Old)) {
8223 $ProblemKind .= "_And_Shift";
8224 }
8225 elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
8226 $ProblemKind .= "_And_Size";
8227 }
8228 }
8229 if(keys(%{$VirtualTable_Full{2}{$BaseName}})
8230 and (cmpVTables($ClassName, 1) or cmpVTables_Model($ClassName)))
8231 { # affected v-table
8232 $ProblemKind .= "_And_VTable";
8233 $VTableChanged{$ClassName}=1;
8234 }
8235 my @Affected = keys(%{$ClassMethods{1}{$ClassName}});
8236 foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
8237 {
8238 my $SubName = get_TypeName($SubId, 1);
8239 push(@Affected, keys(%{$ClassMethods{1}{$SubName}}));
8240 if($ProblemKind=~/VTable/) {
8241 $VTableChanged{$SubName}=1;
8242 }
8243 }
8244 foreach my $Interface (@Affected)
8245 {
8246 %{$CompatProblems{$Interface}{$ProblemKind}{"this"}}=(
8247 "Type_Name"=>$ClassName,
8248 "Type_Type"=>"Class",
8249 "Target"=>$BaseName,
8250 "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
8251 "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
8252 "Shift"=>abs($Shift_New-$Shift_Old) );
8253 }
8254 $Added+=1;
8255 }
8256 }
8257 ($BNum1, $BNum2) = (1, 1);
8258 my %BaseRelPos_Old = map {get_TypeName($_, 1) => $BNum1++} @StableBases_Old;
8259 my %BaseRelPos_New = map {get_TypeName($_, 2) => $BNum2++} @StableBases_New;
8260 foreach my $BaseId (@Bases_Old)
8261 {
8262 my $BaseName = get_TypeName($BaseId, 1);
8263 if(my $NewPos = $BaseRelPos_New{$BaseName})
8264 {
8265 my $BaseNewId = $BaseId_New{$BaseName};
8266 my $OldPos = $BaseRelPos_Old{$BaseName};
8267 if($NewPos!=$OldPos)
8268 { # changed position of the base class
8269 foreach my $Interface (keys(%{$ClassMethods{1}{$ClassName}}))
8270 {
8271 %{$CompatProblems{$Interface}{"Base_Class_Position"}{"this"}}=(
8272 "Type_Name"=>$ClassName,
8273 "Type_Type"=>"Class",
8274 "Target"=>$BaseName,
8275 "Old_Value"=>$OldPos-1,
8276 "New_Value"=>$NewPos-1 );
8277 }
8278 }
8279 if($Class_Old{"Base"}{$BaseId}{"virtual"}
8280 and not $Class_New{"Base"}{$BaseNewId}{"virtual"})
8281 { # became non-virtual base
8282 foreach my $Interface (keys(%{$ClassMethods{1}{$ClassName}}))
8283 {
8284 %{$CompatProblems{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=(
8285 "Type_Name"=>$ClassName,
8286 "Type_Type"=>"Class",
8287 "Target"=>$BaseName );
8288 }
8289 }
8290 elsif(not $Class_Old{"Base"}{$BaseId}{"virtual"}
8291 and $Class_New{"Base"}{$BaseNewId}{"virtual"})
8292 { # became virtual base
8293 foreach my $Interface (keys(%{$ClassMethods{1}{$ClassName}}))
8294 {
8295 %{$CompatProblems{$Interface}{"Base_Class_Became_Virtually_Inherited"}{"this->".$BaseName}}=(
8296 "Type_Name"=>$ClassName,
8297 "Type_Type"=>"Class",
8298 "Target"=>$BaseName );
8299 }
8300 }
8301 }
8302 }
8303 # detect size changes in base classes
8304 if($Shift_Old!=$Shift_New)
8305 { # size of allocable class
8306 foreach my $BaseId (@StableBases_Old)
8307 { # search for changed base
8308 my %BaseType = get_Type($Tid_TDid{1}{$BaseId}, $BaseId, 1);
8309 my $Size_Old = get_TypeSize($BaseId, 1);
8310 my $Size_New = get_TypeSize($BaseId_New{$BaseType{"Name"}}, 2);
8311 if($Size_Old ne $Size_New
8312 and $Size_Old and $Size_New)
8313 {
8314 my $ProblemType = "";
8315 if(isCopyingClass($BaseId, 1)) {
8316 $ProblemType = "Size_Of_Copying_Class";
8317 }
8318 elsif($AllocableClass{1}{$BaseType{"Name"}})
8319 {
8320 if($Size_New>$Size_Old)
8321 { # increased size
8322 $ProblemType = "Size_Of_Allocable_Class_Increased";
8323 }
8324 else
8325 { # decreased size
8326 $ProblemType = "Size_Of_Allocable_Class_Decreased";
8327 if(not havePubFields(\%Class_Old))
8328 { # affected class has no public members
8329 next;
8330 }
8331 }
8332 }
8333 next if(not $ProblemType);
8334 foreach my $Interface (keys(%{$ClassMethods{1}{$ClassName}}))
8335 { # base class size changes affecting current class
8336 %{$CompatProblems{$Interface}{$ProblemType}{"this->".$BaseType{"Name"}}}=(
8337 "Type_Name"=>$BaseType{"Name"},
8338 "Type_Type"=>"Class",
8339 "Target"=>$BaseType{"Name"},
8340 "Old_Size"=>$Size_Old*$BYTE_SIZE,
8341 "New_Size"=>$Size_New*$BYTE_SIZE );
8342 }
8343 }
8344 }
8345 }
8346 if(my @VFunctions = keys(%{$VirtualTable{1}{$ClassName}}))
8347 { # compare virtual tables size in base classes
8348 my $VShift_Old = getVShift($ClassId_Old, 1);
8349 my $VShift_New = getVShift($ClassId_New, 2);
8350 if($VShift_Old ne $VShift_New)
8351 { # changes in the base class or changes in the list of base classes
8352 my @AllBases_Old = get_base_classes($ClassId_Old, 1, 1);
8353 my @AllBases_New = get_base_classes($ClassId_New, 2, 1);
8354 ($BNum1, $BNum2) = (1, 1);
8355 my %StableBase = map {get_TypeName($_, 2) => $_} @AllBases_New;
8356 foreach my $BaseId (@AllBases_Old)
8357 {
8358 my %BaseType = get_Type($Tid_TDid{1}{$BaseId}, $BaseId, 1);
8359 if(not $StableBase{$BaseType{"Name"}})
8360 { # lost base
8361 next;
8362 }
8363 my $VSize_Old = getVSize($BaseType{"Name"}, 1);
8364 my $VSize_New = getVSize($BaseType{"Name"}, 2);
8365 if($VSize_Old!=$VSize_New)
8366 {
8367 my $VRealSize_Old = get_VTableSymbolSize($BaseType{"Name"}, 1);
8368 my $VRealSize_New = get_VTableSymbolSize($BaseType{"Name"}, 2);
8369 if(not $VRealSize_Old or not $VRealSize_New)
8370 { # try to compute a model v-table size
8371 $VRealSize_Old = ($VSize_Old+2+getVShift($BaseId, 1))*$WORD_SIZE{1};
8372 $VRealSize_New = ($VSize_New+2+getVShift($StableBase{$BaseType{"Name"}}, 2))*$WORD_SIZE{2};
8373 }
8374 foreach my $Interface (@VFunctions)
8375 {
8376 if(not defined $VirtualTable{2}{$ClassName}{$Interface})
8377 { # Removed_Virtual_Method, will be registered in mergeVirtualTables()
8378 next;
8379 }
8380 if($VirtualTable{2}{$ClassName}{$Interface}-$VirtualTable{1}{$ClassName}{$Interface}+$VSize_New-$VSize_Old==0)
8381 { # skip interfaces that have not changed the absolute virtual position
8382 next;
8383 }
8384 if(not link_symbol($Interface, 1, "-Deps")
8385 and not $CheckHeadersOnly)
8386 { # affected symbols in shared library
8387 next;
8388 }
8389 if($LIB_ARCH{1} eq $LIB_ARCH{2}
8390 or not $LIB_ARCH{1} or not $LIB_ARCH{2})
8391 {
8392 %{$CompatProblems{$Interface}{"Virtual_Table_Size"}{$BaseType{"Name"}}}=(
8393 "Type_Name"=>$BaseType{"Name"},
8394 "Type_Type"=>"Class",
8395 "Target"=>get_Signature($Interface, 1),
8396 "Old_Size"=>$VRealSize_Old*$BYTE_SIZE,
8397 "New_Size"=>$VRealSize_New*$BYTE_SIZE );
8398 }
8399 $VTableChanged{$BaseType{"Name"}} = 1;
8400 $VTableChanged{$ClassName} = 1;
8401 foreach my $VirtFunc (keys(%{$AddedInt_Virt{$BaseType{"Name"}}}))
8402 { # the reason of the layout change: added virtual functions
8403 next if($VirtualReplacement{$VirtFunc});
8404 my $ProblemType = "Added_Virtual_Method";
8405 if($CompleteSignature{2}{$VirtFunc}{"PureVirt"}) {
8406 $ProblemType = "Added_Pure_Virtual_Method";
8407 }
8408 %{$CompatProblems{$Interface}{$ProblemType}{get_Signature($VirtFunc, 2)}}=(
8409 "Type_Name"=>$BaseType{"Name"},
8410 "Type_Type"=>"Class",
8411 "Target"=>get_Signature($VirtFunc, 2) );
8412 }
8413 foreach my $VirtFunc (keys(%{$RemovedInt_Virt{$BaseType{"Name"}}}))
8414 { # the reason of the layout change: removed virtual functions
8415 next if($VirtualReplacement{$VirtFunc});
8416 my $ProblemType = "Removed_Virtual_Method";
8417 if($CompleteSignature{1}{$VirtFunc}{"PureVirt"}) {
8418 $ProblemType = "Removed_Pure_Virtual_Method";
8419 }
8420 %{$CompatProblems{$Interface}{$ProblemType}{get_Signature($VirtFunc, 1)}}=(
8421 "Type_Name"=>$BaseType{"Name"},
8422 "Type_Type"=>"Class",
8423 "Target"=>get_Signature($VirtFunc, 1) );
8424 }
8425 }
8426 }
8427 }
8428 }
8429 }
8430 }
8431}
8432
8433sub isCreatable($$)
8434{
8435 my ($ClassId, $LibVersion) = @_;
8436 if($AllocableClass{$LibVersion}{get_TypeName($ClassId, $LibVersion)}
8437 or isCopyingClass($ClassId, $LibVersion)) {
8438 return 1;
8439 }
8440 if(keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
8441 { # Fix for incomplete data: if this class has
8442 # a base class then it should also has a constructor
8443 return 1;
8444 }
8445 if($ReturnedClass{$LibVersion}{$ClassId})
8446 { # returned by some method of this class
8447 # or any other class
8448 return 1;
8449 }
8450 return 0;
8451}
8452
8453sub isUsedClass($$)
8454{
8455 my ($ClassId, $LibVersion) = @_;
8456 if(keys(%{$ParamClass{$LibVersion}{$ClassId}}))
8457 { # parameter of some exported method
8458 return 1;
8459 }
8460 my $CName = get_TypeName($ClassId, 1);
8461 if(keys(%{$ClassMethods{1}{$CName}}))
8462 { # method from target class
8463 return 1;
8464 }
8465 return 0;
8466}
8467
8468sub mergeVirtualTables($)
8469{ # check for changes in the virtual table
8470 my $Interface = $_[0];
8471 # affected method:
8472 # - virtual
8473 # - pure-virtual
8474 # - non-virtual
8475 if($CompleteSignature{1}{$Interface}{"Data"})
8476 { # global data is not affected
8477 return;
8478 }
8479 my $Class_Id = $CompleteSignature{1}{$Interface}{"Class"};
8480 my $CName = get_TypeName($Class_Id, 1);
8481 if($CompleteSignature{1}{$Interface}{"PureVirt"}
8482 and not isUsedClass($Class_Id, 1))
8483 { # pure virtuals should not be affected
8484 # if there are no exported methods using this class
8485 return;
8486 }
8487 $CheckedTypes{$CName} = 1;
8488 # check virtual table structure
8489 foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$CName}}))
8490 {
8491 next if($Interface eq $AddedVFunc);
8492 next if($VirtualReplacement{$AddedVFunc});
8493 my $VPos_Added = $VirtualTable{2}{$CName}{$AddedVFunc};
8494 if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"})
8495 { # pure virtual methods affect all others (virtual and non-virtual)
8496 %{$CompatProblems{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
8497 "Type_Name"=>$CName,
8498 "Type_Type"=>"Class",
8499 "Target"=>get_Signature($AddedVFunc, 2) );
8500 $VTableChanged{$CName} = 1;
8501 }
8502 elsif($VPos_Added>keys(%{$VirtualTable{1}{$CName}}))
8503 { # added virtual function at the end of v-table
8504 if(not keys(%{$VirtualTable_Full{1}{$CName}}))
8505 { # became polymorphous class, added v-table pointer
8506 %{$CompatProblems{$Interface}{"Added_First_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
8507 "Type_Name"=>$CName,
8508 "Type_Type"=>"Class",
8509 "Target"=>get_Signature($AddedVFunc, 2) );
8510 $VTableChanged{$CName} = 1;
8511 }
8512 else
8513 {
8514 my $VSize_Old = getVSize($CName, 1);
8515 my $VSize_New = getVSize($CName, 2);
8516 next if($VSize_Old==$VSize_New);# exception: register as removed and added virtual method
8517 if(isCopyingClass($Class_Id, 1))
8518 { # class has no constructors and v-table will be copied by applications, this may affect all methods
8519 my $ProblemType = "Added_Virtual_Method";
8520 if(isLeafClass($Class_Id, 1)) {
8521 $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Copying_Class";
8522 }
8523 %{$CompatProblems{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
8524 "Type_Name"=>$CName,
8525 "Type_Type"=>"Class",
8526 "Target"=>get_Signature($AddedVFunc, 2) );
8527 $VTableChanged{$CName} = 1;
8528 }
8529 else
8530 {
8531 my $ProblemType = "Added_Virtual_Method";
8532 if(isLeafClass($Class_Id, 1)) {
8533 $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class";
8534 }
8535 %{$CompatProblems{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
8536 "Type_Name"=>$CName,
8537 "Type_Type"=>"Class",
8538 "Target"=>get_Signature($AddedVFunc, 2) );
8539 $VTableChanged{$CName} = 1;
8540 }
8541 }
8542 }
8543 elsif($CompleteSignature{1}{$Interface}{"Virt"}
8544 or $CompleteSignature{1}{$Interface}{"PureVirt"})
8545 {
8546 my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
8547 my $VPos_New = $VirtualTable{2}{$CName}{$Interface};
8548 if($VPos_Added<=$VPos_Old and $VPos_Old!=$VPos_New)
8549 {
8550 my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
8551 foreach my $ASymbol (@Affected)
8552 {
8553 if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"}
8554 and not link_symbol($ASymbol, 1, "-Deps")) {
8555 next;
8556 }
8557 $CheckedSymbols{$ASymbol} = 1;
8558 %{$CompatProblems{$ASymbol}{"Added_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
8559 "Type_Name"=>$CName,
8560 "Type_Type"=>"Class",
8561 "Target"=>get_Signature($AddedVFunc, 2) );
8562 $VTableChanged{get_TypeName($CompleteSignature{1}{$ASymbol}{"Class"}, 1)} = 1;
8563 }
8564 }
8565 }
8566 else {
8567 # safe
8568 }
8569 }
8570 foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$CName}}))
8571 {
8572 next if($VirtualReplacement{$RemovedVFunc});
8573 if($RemovedVFunc eq $Interface
8574 and $CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
8575 { # This case is for removed virtual methods
8576 # implemented in both versions of a library
8577 next;
8578 }
8579 if(not keys(%{$VirtualTable_Full{2}{$CName}}))
8580 { # became non-polymorphous class, removed v-table pointer
8581 %{$CompatProblems{$Interface}{"Removed_Last_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=(
8582 "Type_Name"=>$CName,
8583 "Type_Type"=>"Class",
8584 "Target"=>get_Signature($RemovedVFunc, 1) );
8585 $VTableChanged{$CName} = 1;
8586 }
8587 elsif($CompleteSignature{1}{$Interface}{"Virt"}
8588 or $CompleteSignature{1}{$Interface}{"PureVirt"})
8589 {
8590 my $VPos_Removed = $VirtualTable{1}{$CName}{$RemovedVFunc};
8591 my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
8592 my $VPos_New = $VirtualTable{2}{$CName}{$Interface};
8593 if($VPos_Removed<=$VPos_Old and $VPos_Old!=$VPos_New)
8594 {
8595 my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
8596 foreach my $ASymbol (@Affected)
8597 {
8598 if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"}
8599 and not link_symbol($ASymbol, 1, "-Deps")) {
8600 next;
8601 }
8602 my $ProblemType = "Removed_Virtual_Method";
8603 if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) {
8604 $ProblemType = "Removed_Pure_Virtual_Method";
8605 }
8606 $CheckedSymbols{$ASymbol} = 1;
8607 %{$CompatProblems{$ASymbol}{$ProblemType}{$tr_name{$RemovedVFunc}}}=(
8608 "Type_Name"=>$CName,
8609 "Type_Type"=>"Class",
8610 "Target"=>get_Signature($RemovedVFunc, 1) );
8611 $VTableChanged{get_TypeName($CompleteSignature{1}{$ASymbol}{"Class"}, 1)} = 1;
8612 }
8613 }
8614 }
8615 }
8616}
8617
8618sub find_MemberPair_Pos_byName($$)
8619{
8620 my ($Member_Name, $Pair_Type) = @_;
8621 $Member_Name=~s/\A[_]+|[_]+\Z//g;
8622 foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
8623 {
8624 if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos})
8625 {
8626 my $Name = $Pair_Type->{"Memb"}{$MemberPair_Pos}{"name"};
8627 $Name=~s/\A[_]+|[_]+\Z//g;
8628 if($Name eq $Member_Name) {
8629 return $MemberPair_Pos;
8630 }
8631 }
8632 }
8633 return "lost";
8634}
8635
8636sub find_MemberPair_Pos_byVal($$)
8637{
8638 my ($Member_Value, $Pair_Type) = @_;
8639 foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
8640 {
8641 if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos}
8642 and $Pair_Type->{"Memb"}{$MemberPair_Pos}{"value"} eq $Member_Value) {
8643 return $MemberPair_Pos;
8644 }
8645 }
8646 return "lost";
8647}
8648
8649my %Priority_Value=(
8650 "High"=>3,
8651 "Medium"=>2,
8652 "Low"=>1);
8653
8654sub max_priority($$)
8655{
8656 my ($Pr1, $Pr2) = @_;
8657 if(cmp_priority($Pr1, $Pr2)) {
8658 return $Pr1;
8659 }
8660 else {
8661 return $Pr2;
8662 }
8663}
8664
8665sub cmp_priority($$)
8666{
8667 my ($Pr1, $Pr2) = @_;
8668 return ($Priority_Value{$Pr1}>$Priority_Value{$Pr2});
8669}
8670
8671sub getProblemSeverity($$)
8672{
8673 my ($Level, $Kind) = @_;
8674 return $CompatRules{$Level}{$Kind}{"Severity"};
8675}
8676
8677sub isRecurType($$$$)
8678{
8679 foreach (@RecurTypes)
8680 {
8681 if($_->{"Tid1"} eq $_[0]
8682 and $_->{"TDid1"} eq $_[1]
8683 and $_->{"Tid2"} eq $_[2]
8684 and $_->{"TDid2"} eq $_[3])
8685 {
8686 return 1;
8687 }
8688 }
8689 return 0;
8690}
8691
8692sub pushType($$$$)
8693{
8694 my %TypeIDs=(
8695 "Tid1" => $_[0],
8696 "TDid1" => $_[1],
8697 "Tid2" => $_[2],
8698 "TDid2" => $_[3] );
8699 push(@RecurTypes, \%TypeIDs);
8700}
8701
8702sub isRenamed($$$$$)
8703{
8704 my ($MemPos, $Type1, $LVersion1, $Type2, $LVersion2) = @_;
8705 my $Member_Name = $Type1->{"Memb"}{$MemPos}{"name"};
8706 my $MemberType_Id = $Type1->{"Memb"}{$MemPos}{"type"};
8707 my %MemberType_Pure = get_PureType($Tid_TDid{$LVersion1}{$MemberType_Id}, $MemberType_Id, $LVersion1);
8708 if(not defined $Type2->{"Memb"}{$MemPos}) {
8709 return "";
8710 }
8711 my $StraightPairType_Id = $Type2->{"Memb"}{$MemPos}{"type"};
8712 my %StraightPairType_Pure = get_PureType($Tid_TDid{$LVersion2}{$StraightPairType_Id}, $StraightPairType_Id, $LVersion2);
8713
8714 my $StraightPair_Name = $Type2->{"Memb"}{$MemPos}{"name"};
8715 my $MemberPair_Pos_Rev = ($Member_Name eq $StraightPair_Name)?$MemPos:find_MemberPair_Pos_byName($StraightPair_Name, $Type1);
8716 if($MemberPair_Pos_Rev eq "lost")
8717 {
8718 if($MemberType_Pure{"Name"} eq $StraightPairType_Pure{"Name"})
8719 {# base type match
8720 return $StraightPair_Name;
8721 }
8722 if(get_TypeName($MemberType_Id, $LVersion1) eq get_TypeName($StraightPairType_Id, $LVersion2))
8723 {# exact type match
8724 return $StraightPair_Name;
8725 }
8726 if($MemberType_Pure{"Size"} eq $StraightPairType_Pure{"Size"})
8727 {# size match
8728 return $StraightPair_Name;
8729 }
8730 if(isReserved($StraightPair_Name))
8731 {# reserved fields
8732 return $StraightPair_Name;
8733 }
8734 }
8735 return "";
8736}
8737
8738sub isLastElem($$)
8739{
8740 my ($Pos, $TypeRef) = @_;
8741 my $Name = $TypeRef->{"Memb"}{$Pos}{"name"};
8742 if($Name=~/last|count|max|total/i)
8743 { # GST_LEVEL_COUNT, GST_RTSP_ELAST
8744 return 1;
8745 }
8746 elsif($Name=~/END|NLIMITS\Z/)
8747 { # __RLIMIT_NLIMITS
8748 return 1;
8749 }
8750 elsif($Name=~/\AN[A-Z](.+)[a-z]+s\Z/
8751 and $Pos+1==keys(%{$TypeRef->{"Memb"}}))
8752 { # NImageFormats, NColorRoles
8753 return 1;
8754 }
8755 return 0;
8756}
8757
8758sub nonComparable($$)
8759{
8760 my ($T1, $T2) = @_;
8761 if($T1->{"Name"} ne $T2->{"Name"}
8762 and not isAnon($T1->{"Name"})
8763 and not isAnon($T2->{"Name"}))
8764 { # different names
8765 if($T1->{"Type"} ne "Pointer"
8766 or $T2->{"Type"} ne "Pointer")
8767 { # compare base types
8768 return 1;
8769 }
8770 if($T1->{"Name"}!~/\Avoid\s*\*/
8771 and $T2->{"Name"}=~/\Avoid\s*\*/)
8772 {
8773 return 1;
8774 }
8775 }
8776 elsif($T1->{"Type"} ne $T2->{"Type"})
8777 { # different types
8778 if($T1->{"Type"} eq "Class"
8779 and $T2->{"Type"} eq "Struct")
8780 { # "class" to "struct"
8781 return 0;
8782 }
8783 elsif($T2->{"Type"} eq "Class"
8784 and $T1->{"Type"} eq "Struct")
8785 { # "struct" to "class"
8786 return 0;
8787 }
8788 else
8789 { # "class" to "enum"
8790 # "union" to "class"
8791 # ...
8792 return 1;
8793 }
8794 }
8795 return 0;
8796}
8797
8798sub mergeTypes($$$$)
8799{
8800 my ($Type1_Id, $Type1_DId, $Type2_Id, $Type2_DId) = @_;
8801 return () if((not $Type1_Id and not $Type1_DId) or (not $Type2_Id and not $Type2_DId));
8802 my (%Sub_SubProblems, %SubProblems) = ();
8803 if($Cache{"mergeTypes"}{$Type1_Id}{$Type1_DId}{$Type2_Id}{$Type2_DId})
8804 { # already merged
8805 return %{$Cache{"mergeTypes"}{$Type1_Id}{$Type1_DId}{$Type2_Id}{$Type2_DId}};
8806 }
8807 my %Type1 = get_Type($Type1_DId, $Type1_Id, 1);
8808 my %Type2 = get_Type($Type2_DId, $Type2_Id, 2);
8809 my %Type1_Pure = get_PureType($Type1_DId, $Type1_Id, 1);
8810 my %Type2_Pure = get_PureType($Type2_DId, $Type2_Id, 2);
8811 $CheckedTypes{$Type1{"Name"}}=1;
8812 $CheckedTypes{$Type1_Pure{"Name"}}=1;
8813 return () if(not $Type1_Pure{"Size"} or not $Type2_Pure{"Size"});
8814 if(isRecurType($Type1_Pure{"Tid"}, $Type1_Pure{"TDid"}, $Type2_Pure{"Tid"}, $Type2_Pure{"TDid"}))
8815 { # skip recursive declarations
8816 return ();
8817 }
8818 return () if(not $Type1_Pure{"Name"} or not $Type2_Pure{"Name"});
8819 return () if($SkipTypes{1}{$Type1_Pure{"Name"}});
8820 return () if($SkipTypes{1}{$Type1{"Name"}});
8821
8822 my %Typedef_1 = goToFirst($Type1{"TDid"}, $Type1{"Tid"}, 1, "Typedef");
8823 my %Typedef_2 = goToFirst($Type2{"TDid"}, $Type2{"Tid"}, 2, "Typedef");
8824 if($Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef"
8825 and $Typedef_1{"Name"} eq $Typedef_2{"Name"} and not $UseOldDumps)
8826 {
8827 my %Base_1 = get_OneStep_BaseType($Typedef_1{"TDid"}, $Typedef_1{"Tid"}, 1);
8828 my %Base_2 = get_OneStep_BaseType($Typedef_2{"TDid"}, $Typedef_2{"Tid"}, 2);
8829 if(differentFmts())
8830 { # different GCC versions or different dumps
8831 $Base_1{"Name"} = uncover_typedefs($Base_1{"Name"}, 1);
8832 $Base_2{"Name"} = uncover_typedefs($Base_2{"Name"}, 2);
8833 # std::__va_list and __va_list
8834 $Base_1{"Name"}=~s/\A(\w+::)+//;
8835 $Base_2{"Name"}=~s/\A(\w+::)+//;
8836 $Base_1{"Name"} = formatName($Base_1{"Name"});
8837 $Base_2{"Name"} = formatName($Base_2{"Name"});
8838 }
8839 if($Base_1{"Name"}!~/anon\-/ and $Base_2{"Name"}!~/anon\-/
8840 and $Base_1{"Name"} ne $Base_2{"Name"})
8841 {
8842 if($Type1{"Size"} ne $Type2{"Size"})
8843 {
8844 %{$SubProblems{"DataType_Size"}{$Typedef_1{"Name"}}}=(
8845 "Target"=>$Typedef_1{"Name"},
8846 "Type_Name"=>$Typedef_1{"Name"},
8847 "Type_Type"=>"Typedef",
8848 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
8849 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE );
8850 }
8851 %{$SubProblems{"Typedef_BaseType"}{$Typedef_1{"Name"}}}=(
8852 "Target"=>$Typedef_1{"Name"},
8853 "Type_Name"=>$Typedef_1{"Name"},
8854 "Type_Type"=>"Typedef",
8855 "Old_Value"=>$Base_1{"Name"},
8856 "New_Value"=>$Base_2{"Name"} );
8857 }
8858 }
8859 if(nonComparable(\%Type1_Pure, \%Type2_Pure))
8860 { # different types (reported in detectTypeChange(...))
8861 if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}
8862 and $Type1_Pure{"Type"} ne $Type2_Pure{"Type"}
8863 and $Type1_Pure{"Type"}!~/Intrinsic|Pointer|Ref|Typedef/)
8864 { # different type of the type
8865 %{$SubProblems{"DataType_Type"}{$Type1_Pure{"Name"}}}=(
8866 "Target"=>$Type1_Pure{"Name"},
8867 "Type_Name"=>$Type1_Pure{"Name"},
8868 "Type_Type"=>$Type1_Pure{"Type"},
8869 "Old_Value"=>lc($Type1_Pure{"Type"}),
8870 "New_Value"=>lc($Type2_Pure{"Type"}) );
8871 }
8872 %{$Cache{"mergeTypes"}{$Type1_Id}{$Type1_DId}{$Type2_Id}{$Type2_DId}} = %SubProblems;
8873 return %SubProblems;
8874 }
8875 pushType($Type1_Pure{"Tid"}, $Type1_Pure{"TDid"},
8876 $Type2_Pure{"Tid"}, $Type2_Pure{"TDid"});
8877 if(($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}
8878 or (isAnon($Type1_Pure{"Name"}) and isAnon($Type2_Pure{"Name"})))
8879 and $Type1_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
8880 { # checking size
8881 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
8882 {
8883 my $ProblemKind = "DataType_Size";
8884 if($Type1_Pure{"Type"} eq "Class"
8885 and keys(%{$ClassMethods{1}{$Type1_Pure{"Name"}}}))
8886 {
8887 if(isCopyingClass($Type1_Pure{"Tid"}, 1)) {
8888 $ProblemKind = "Size_Of_Copying_Class";
8889 }
8890 elsif($AllocableClass{1}{$Type1_Pure{"Name"}})
8891 {
8892 if(int($Type2_Pure{"Size"})>int($Type1_Pure{"Size"})) {
8893 $ProblemKind = "Size_Of_Allocable_Class_Increased";
8894 }
8895 else {
8896 # descreased size of allocable class
8897 # it has no special effects
8898 }
8899 }
8900 }
8901 %{$SubProblems{$ProblemKind}{$Type1_Pure{"Name"}}}=(
8902 "Target"=>$Type1_Pure{"Name"},
8903 "Type_Name"=>$Type1_Pure{"Name"},
8904 "Type_Type"=>$Type1_Pure{"Type"},
8905 "Old_Size"=>$Type1_Pure{"Size"}*$BYTE_SIZE,
8906 "New_Size"=>$Type2_Pure{"Size"}*$BYTE_SIZE,
8907 "InitialType_Type"=>$Type1_Pure{"Type"} );
8908 }
8909 }
8910 if($Type1_Pure{"BaseType"}{"Tid"} and $Type2_Pure{"BaseType"}{"Tid"})
8911 {# checking base types
8912 %Sub_SubProblems = mergeTypes($Type1_Pure{"BaseType"}{"Tid"}, $Type1_Pure{"BaseType"}{"TDid"},
8913 $Type2_Pure{"BaseType"}{"Tid"}, $Type2_Pure{"BaseType"}{"TDid"});
8914 foreach my $Sub_SubProblemType (keys(%Sub_SubProblems))
8915 {
8916 foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems{$Sub_SubProblemType}}))
8917 {
8918 foreach my $Attr (keys(%{$Sub_SubProblems{$Sub_SubProblemType}{$Sub_SubLocation}})) {
8919 $SubProblems{$Sub_SubProblemType}{$Sub_SubLocation}{$Attr} = $Sub_SubProblems{$Sub_SubProblemType}{$Sub_SubLocation}{$Attr};
8920 }
8921 $SubProblems{$Sub_SubProblemType}{$Sub_SubLocation}{"InitialType_Type"} = $Type1_Pure{"Type"};
8922 }
8923 }
8924 }
8925 my (%AddedField, %RemovedField, %RenamedField, %RenamedField_Rev, %RelatedField, %RelatedField_Rev) = ();
8926 my %NameToPosA = map {$Type1_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type1_Pure{"Memb"}});
8927 my %NameToPosB = map {$Type2_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type2_Pure{"Memb"}});
8928 foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
8929 { # detect removed and renamed fields
8930 my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
8931 next if(not $Member_Name);
8932 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);
8933 if($MemberPair_Pos eq "lost")
8934 {
8935 if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
8936 {
8937 if(isUnnamed($Member_Name))
8938 { # support for old-version dumps
8939 # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
8940 if($UsedDump{2}{"V"}
8941 and cmpVersions($UsedDump{2}{"V"}, "2.1")<0) {
8942 next;
8943 }
8944 }
8945 if(my $RenamedTo = isRenamed($Member_Pos, \%Type1_Pure, 1, \%Type2_Pure, 2))
8946 { # renamed
8947 $RenamedField{$Member_Pos}=$RenamedTo;
8948 $RenamedField_Rev{$NameToPosB{$RenamedTo}}=$Member_Name;
8949 }
8950 else
8951 { # removed
8952 $RemovedField{$Member_Pos}=1;
8953 }
8954 }
8955 elsif($Type1_Pure{"Type"} eq "Enum")
8956 {
8957 my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
8958 next if($Member_Value1 eq "");
8959 $MemberPair_Pos = find_MemberPair_Pos_byVal($Member_Value1, \%Type2_Pure);
8960 if($MemberPair_Pos ne "lost")
8961 { # renamed
8962 my $RenamedTo = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"name"};
8963 my $MemberPair_Pos_Rev = find_MemberPair_Pos_byName($RenamedTo, \%Type1_Pure);
8964 if($MemberPair_Pos_Rev eq "lost")
8965 {
8966 $RenamedField{$Member_Pos}=$RenamedTo;
8967 $RenamedField_Rev{$NameToPosB{$RenamedTo}}=$Member_Name;
8968 }
8969 else {
8970 $RemovedField{$Member_Pos}=1;
8971 }
8972 }
8973 else
8974 { # removed
8975 $RemovedField{$Member_Pos}=1;
8976 }
8977 }
8978 }
8979 else
8980 { # related
8981 $RelatedField{$Member_Pos} = $MemberPair_Pos;
8982 $RelatedField_Rev{$MemberPair_Pos} = $Member_Pos;
8983 }
8984 }
8985 foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
8986 { # detect added fields
8987 my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
8988 next if(not $Member_Name);
8989 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);
8990 if($MemberPair_Pos eq "lost")
8991 {
8992 if(isUnnamed($Member_Name))
8993 { # support for old-version dumps
8994 # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
8995 if($UsedDump{1}{"V"}
8996 and cmpVersions($UsedDump{1}{"V"}, "2.1")<0) {
8997 next;
8998 }
8999 }
9000 if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union|Enum)\Z/)
9001 {
9002 if(not $RenamedField_Rev{$Member_Pos})
9003 { # added
9004 $AddedField{$Member_Pos}=1;
9005 }
9006 }
9007 }
9008 }
9009 if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
9010 { # detect moved fields
9011 my (%RelPos, %RelPosName, %AbsPos) = ();
9012 my $Pos = 0;
9013 foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
9014 { # relative positions in 1st version
9015 my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
9016 next if(not $Member_Name);
9017 if(not $RemovedField{$Member_Pos})
9018 { # old type without removed fields
9019 $RelPos{1}{$Member_Name}=$Pos;
9020 $RelPosName{1}{$Pos} = $Member_Name;
9021 $AbsPos{1}{$Pos++} = $Member_Pos;
9022 }
9023 }
9024 $Pos = 0;
9025 foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
9026 { # relative positions in 2nd version
9027 my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
9028 next if(not $Member_Name);
9029 if(not $AddedField{$Member_Pos})
9030 { # new type without added fields
9031 $RelPos{2}{$Member_Name}=$Pos;
9032 $RelPosName{2}{$Pos} = $Member_Name;
9033 $AbsPos{2}{$Pos++} = $Member_Pos;
9034 }
9035 }
9036 foreach my $Member_Name (keys(%{$RelPos{1}}))
9037 {
9038 my $RPos1 = $RelPos{1}{$Member_Name};
9039 my $AbsPos1 = $NameToPosA{$Member_Name};
9040 my $Member_Name2 = $Member_Name;
9041 if(my $RenamedTo = $RenamedField{$AbsPos1})
9042 { # renamed
9043 $Member_Name2 = $RenamedTo;
9044 }
9045 my $RPos2 = $RelPos{2}{$Member_Name2};
9046 if($RPos2 ne "" and $RPos1 ne $RPos2)
9047 { # different relative positions
9048 my $AbsPos2 = $NameToPosB{$Member_Name2};
9049 if($AbsPos1 ne $AbsPos2)
9050 { # different absolute positions
9051 my $ProblemType = "Moved_Field";
9052 if(not isPublic(\%Type1_Pure, $AbsPos1))
9053 { # may change layout and size of type
9054 $ProblemType = "Moved_Private_Field";
9055 }
9056 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
9057 { # affected size
9058 my $MemSize1 = get_TypeSize($Type1_Pure{"Memb"}{$AbsPos1}{"type"}, 1);
9059 my $MovedAbsPos = $AbsPos{1}{$RPos2};
9060 my $MemSize2 = get_TypeSize($Type1_Pure{"Memb"}{$MovedAbsPos}{"type"}, 1);
9061 if($MemSize1 ne $MemSize2) {
9062 $ProblemType .= "_And_Size";
9063 }
9064 }
9065 if($ProblemType eq "Moved_Private_Field") {
9066 next;
9067 }
9068 %{$SubProblems{$ProblemType}{$Member_Name}}=(
9069 "Target"=>$Member_Name,
9070 "Type_Name"=>$Type1_Pure{"Name"},
9071 "Type_Type"=>$Type1_Pure{"Type"},
9072 "Old_Value"=>$RPos1,
9073 "New_Value"=>$RPos2 );
9074 }
9075 }
9076 }
9077 }
9078 foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
9079 {# check older fields, public and private
9080 my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
9081 next if(not $Member_Name);
9082 if(my $RenamedTo = $RenamedField{$Member_Pos})
9083 { # renamed
9084 if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
9085 {
9086 if(isPublic(\%Type1_Pure, $Member_Pos))
9087 {
9088 %{$SubProblems{"Renamed_Field"}{$Member_Name}}=(
9089 "Target"=>$Member_Name,
9090 "Type_Name"=>$Type1_Pure{"Name"},
9091 "Type_Type"=>$Type1_Pure{"Type"},
9092 "Old_Value"=>$Member_Name,
9093 "New_Value"=>$RenamedTo );
9094 }
9095 }
9096 elsif($Type1_Pure{"Type"} eq "Enum")
9097 {
9098 %{$SubProblems{"Enum_Member_Name"}{$Type1_Pure{"Memb"}{$Member_Pos}{"value"}}}=(
9099 "Target"=>$Type1_Pure{"Memb"}{$Member_Pos}{"value"},
9100 "Type_Name"=>$Type1_Pure{"Name"},
9101 "Type_Type"=>$Type1_Pure{"Type"},
9102 "Old_Value"=>$Member_Name,
9103 "New_Value"=>$RenamedTo );
9104 }
9105 }
9106 elsif($RemovedField{$Member_Pos})
9107 { # removed
9108 if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
9109 {
9110 my $ProblemType = "Removed_Field";
9111 if(not isPublic(\%Type1_Pure, $Member_Pos)
9112 or isUnnamed($Member_Name)) {
9113 $ProblemType = "Removed_Private_Field";
9114 }
9115 if(not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, 1))
9116 {
9117 if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
9118 { # affected fields
9119 if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2))
9120 { # changed offset
9121 $ProblemType .= "_And_Layout";
9122 }
9123 }
9124 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
9125 { # affected size
9126 $ProblemType .= "_And_Size";
9127 }
9128 }
9129 if($ProblemType eq "Removed_Private_Field") {
9130 next;
9131 }
9132 %{$SubProblems{$ProblemType}{$Member_Name}}=(
9133 "Target"=>$Member_Name,
9134 "Type_Name"=>$Type1_Pure{"Name"},
9135 "Type_Type"=>$Type1_Pure{"Type"} );
9136 }
9137 elsif($Type2_Pure{"Type"} eq "Union")
9138 {
9139 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
9140 {
9141 %{$SubProblems{"Removed_Union_Field_And_Size"}{$Member_Name}}=(
9142 "Target"=>$Member_Name,
9143 "Type_Name"=>$Type1_Pure{"Name"},
9144 "Type_Type"=>$Type1_Pure{"Type"} );
9145 }
9146 else
9147 {
9148 %{$SubProblems{"Removed_Union_Field"}{$Member_Name}}=(
9149 "Target"=>$Member_Name,
9150 "Type_Name"=>$Type1_Pure{"Name"},
9151 "Type_Type"=>$Type1_Pure{"Type"} );
9152 }
9153 }
9154 elsif($Type1_Pure{"Type"} eq "Enum")
9155 {
9156 %{$SubProblems{"Enum_Member_Removed"}{$Member_Name}}=(
9157 "Target"=>$Member_Name,
9158 "Type_Name"=>$Type1_Pure{"Name"},
9159 "Type_Type"=>$Type1_Pure{"Type"},
9160 "Old_Value"=>$Member_Name );
9161 }
9162 }
9163 else
9164 { # changed
9165 my $MemberPair_Pos = $RelatedField{$Member_Pos};
9166 if($Type1_Pure{"Type"} eq "Enum")
9167 {
9168 my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
9169 next if($Member_Value1 eq "");
9170 my $Member_Value2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"value"};
9171 next if($Member_Value2 eq "");
9172 if($Member_Value1 ne $Member_Value2)
9173 {
9174 my $ProblemType = "Enum_Member_Value";
9175 if(isLastElem($Member_Pos, \%Type1_Pure)) {
9176 $ProblemType = "Enum_Last_Member_Value";
9177 }
9178 %{$SubProblems{$ProblemType}{$Member_Name}}=(
9179 "Target"=>$Member_Name,
9180 "Type_Name"=>$Type1_Pure{"Name"},
9181 "Type_Type"=>$Type1_Pure{"Type"},
9182 "Old_Value"=>$Member_Value1,
9183 "New_Value"=>$Member_Value2 );
9184 }
9185 }
9186 elsif($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
9187 {
9188 my $MemberType1_Id = $Type1_Pure{"Memb"}{$Member_Pos}{"type"};
9189 my $MemberType2_Id = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"type"};
9190 my $SizeV1 = get_TypeSize($MemberType1_Id, 1)*$BYTE_SIZE;
9191 if(my $BSize1 = $Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}) {
9192 $SizeV1 = $BSize1;
9193 }
9194 my $SizeV2 = get_TypeSize($MemberType2_Id, 2)*$BYTE_SIZE;
9195 if(my $BSize2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) {
9196 $SizeV2 = $BSize2;
9197 }
9198 my $MemberType1_Name = get_TypeName($MemberType1_Id, 1);
9199 my $MemberType2_Name = get_TypeName($MemberType2_Id, 2);
9200 if($SizeV1 ne $SizeV2)
9201 {
9202 if($MemberType1_Name eq $MemberType2_Name or (isAnon($MemberType1_Name) and isAnon($MemberType2_Name))
9203 or ($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"} and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}))
9204 { # field size change (including anon-structures and unions)
9205 # - same types
9206 # - unnamed types
9207 # - bitfields
9208 my $ProblemType = "Field_Size";
9209 if(not isPublic(\%Type1_Pure, $Member_Pos)
9210 or isUnnamed($Member_Name))
9211 { # should not be accessed by applications, goes to "Low Severity"
9212 # example: "abidata" members in GStreamer types
9213 $ProblemType = "Private_".$ProblemType;
9214 }
9215 if(not isMemPadded($Member_Pos, get_TypeSize($MemberType2_Id, 2)*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, 1))
9216 { # check an effect
9217 if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
9218 { # public fields after the current
9219 if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2))
9220 { # changed offset
9221 $ProblemType .= "_And_Layout";
9222 }
9223 }
9224 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
9225 $ProblemType .= "_And_Type_Size";
9226 }
9227 }
9228 if($ProblemType eq "Private_Field_Size")
9229 { # private field size with no effect
9230 $ProblemType = "";
9231 }
9232 if($ProblemType)
9233 { # register a problem
9234 %{$SubProblems{$ProblemType}{$Member_Name}}=(
9235 "Target"=>$Member_Name,
9236 "Type_Name"=>$Type1_Pure{"Name"},
9237 "Type_Type"=>$Type1_Pure{"Type"},
9238 "Old_Size"=>$SizeV1,
9239 "New_Size"=>$SizeV2);
9240 }
9241 }
9242 }
9243 if($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}
9244 or $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"})
9245 { # do NOT check bitfield type changes
9246 next;
9247 }
9248 %Sub_SubProblems = detectTypeChange($MemberType1_Id, $MemberType2_Id, "Field");
9249 foreach my $ProblemType (keys(%Sub_SubProblems))
9250 {
9251 my $Old_Value = $Sub_SubProblems{$ProblemType}{"Old_Value"};
9252 my $New_Value = $Sub_SubProblems{$ProblemType}{"New_Value"};
9253 if($ProblemType eq "Field_Type"
9254 or $ProblemType eq "Field_Type_And_Size")
9255 {
9256 if((not $UsedDump{1}{"V"} or cmpVersions($UsedDump{1}{"V"}, "2.6")>=0)
9257 and (not $UsedDump{2}{"V"} or cmpVersions($UsedDump{2}{"V"}, "2.6")>=0))
9258 {
9259 if($Old_Value!~/(\A|\W)volatile(\W|\Z)/
9260 and $New_Value=~/(\A|\W)volatile(\W|\Z)/)
9261 { # non-"volatile" to "volatile"
9262 %{$Sub_SubProblems{"Field_Became_Volatile"}} = %{$Sub_SubProblems{$ProblemType}};
9263 }
9264 }
9265 }
9266 }
9267 foreach my $ProblemType (keys(%Sub_SubProblems))
9268 {
9269 my $ProblemType_Init = $ProblemType;
9270 if($ProblemType eq "Field_Type_And_Size")
9271 {
9272 if(not isPublic(\%Type1_Pure, $Member_Pos)
9273 or isUnnamed($Member_Name)) {
9274 $ProblemType = "Private_".$ProblemType;
9275 }
9276 if(not isMemPadded($Member_Pos, get_TypeSize($MemberType2_Id, 2)*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, 1))
9277 { # check an effect
9278 if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
9279 { # public fields after the current
9280 if(getOffset($MNum-1, \%Type1_Pure, 1)!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, 2))
9281 { # changed offset
9282 $ProblemType .= "_And_Layout";
9283 }
9284 }
9285 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
9286 $ProblemType .= "_And_Type_Size";
9287 }
9288 }
9289 }
9290 else
9291 {
9292 if(not isPublic(\%Type1_Pure, $Member_Pos)
9293 or isUnnamed($Member_Name)) {
9294 next;
9295 }
9296 }
9297 if($ProblemType eq "Private_Field_Type_And_Size")
9298 { # private field change with no effect
9299 next;
9300 }
9301 %{$SubProblems{$ProblemType}{$Member_Name}}=(
9302 "Target"=>$Member_Name,
9303 "Type_Name"=>$Type1_Pure{"Name"},
9304 "Type_Type"=>$Type1_Pure{"Type"} );
9305 foreach my $Attr (keys(%{$Sub_SubProblems{$ProblemType_Init}}))
9306 { # other properties
9307 $SubProblems{$ProblemType}{$Member_Name}{$Attr} = $Sub_SubProblems{$ProblemType_Init}{$Attr};
9308 }
9309 }
9310 if(not isPublic(\%Type1_Pure, $Member_Pos))
9311 { # do NOT check internal type changes
9312 next;
9313 }
9314 if($MemberType1_Id and $MemberType2_Id)
9315 {# checking member type changes (replace)
9316 %Sub_SubProblems = mergeTypes($MemberType1_Id, $Tid_TDid{1}{$MemberType1_Id},
9317 $MemberType2_Id, $Tid_TDid{2}{$MemberType2_Id});
9318 foreach my $Sub_SubProblemType (keys(%Sub_SubProblems))
9319 {
9320 foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems{$Sub_SubProblemType}}))
9321 {
9322 my $NewLocation = ($Sub_SubLocation)?$Member_Name."->".$Sub_SubLocation:$Member_Name;
9323 $SubProblems{$Sub_SubProblemType}{$NewLocation}{"IsInTypeInternals"}=1;
9324 foreach my $Attr (keys(%{$Sub_SubProblems{$Sub_SubProblemType}{$Sub_SubLocation}})) {
9325 $SubProblems{$Sub_SubProblemType}{$NewLocation}{$Attr} = $Sub_SubProblems{$Sub_SubProblemType}{$Sub_SubLocation}{$Attr};
9326 }
9327 if($Sub_SubLocation!~/\-\>/) {
9328 $SubProblems{$Sub_SubProblemType}{$NewLocation}{"Start_Type_Name"} = $MemberType1_Name;
9329 }
9330 }
9331 }
9332 }
9333 }
9334 }
9335 }
9336 foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
9337 { # checking added members, public and private
9338 my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
9339 next if(not $Member_Name);
9340 if($AddedField{$Member_Pos})
9341 { # added
9342 if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
9343 {
9344 my $ProblemType = "Added_Field";
9345 if(not isPublic(\%Type2_Pure, $Member_Pos)
9346 or isUnnamed($Member_Name)) {
9347 $ProblemType = "Added_Private_Field";
9348 }
9349 if(not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, 2))
9350 {
9351 if(my $MNum = isAccessible(\%Type2_Pure, \%AddedField, $Member_Pos, -1))
9352 { # public fields after the current
9353 if(getOffset($MNum-1, \%Type2_Pure, 2)!=getOffset($RelatedField_Rev{$MNum-1}, \%Type1_Pure, 1))
9354 { # changed offset
9355 $ProblemType .= "_And_Layout";
9356 }
9357 }
9358 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
9359 $ProblemType .= "_And_Size";
9360 }
9361 }
9362 if($ProblemType eq "Added_Private_Field")
9363 { # skip added private fields
9364 next;
9365 }
9366 %{$SubProblems{$ProblemType}{$Member_Name}}=(
9367 "Target"=>$Member_Name,
9368 "Type_Name"=>$Type1_Pure{"Name"},
9369 "Type_Type"=>$Type1_Pure{"Type"} );
9370 }
9371 elsif($Type2_Pure{"Type"} eq "Union")
9372 {
9373 if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
9374 {
9375 %{$SubProblems{"Added_Union_Field_And_Size"}{$Member_Name}}=(
9376 "Target"=>$Member_Name,
9377 "Type_Name"=>$Type1_Pure{"Name"},
9378 "Type_Type"=>$Type1_Pure{"Type"} );
9379 }
9380 else
9381 {
9382 %{$SubProblems{"Added_Union_Field"}{$Member_Name}}=(
9383 "Target"=>$Member_Name,
9384 "Type_Name"=>$Type1_Pure{"Name"},
9385 "Type_Type"=>$Type1_Pure{"Type"} );
9386 }
9387 }
9388 elsif($Type2_Pure{"Type"} eq "Enum")
9389 {
9390 my $Member_Value = $Type2_Pure{"Memb"}{$Member_Pos}{"value"};
9391 next if($Member_Value eq "");
9392 %{$SubProblems{"Added_Enum_Member"}{$Member_Name}}=(
9393 "Target"=>$Member_Name,
9394 "Type_Name"=>$Type2_Pure{"Name"},
9395 "Type_Type"=>$Type2_Pure{"Type"},
9396 "New_Value"=>$Member_Value );
9397 }
9398 }
9399 }
9400 %{$Cache{"mergeTypes"}{$Type1_Id}{$Type1_DId}{$Type2_Id}{$Type2_DId}} = %SubProblems;
9401 pop(@RecurTypes);
9402 return %SubProblems;
9403}
9404
9405sub isUnnamed($) {
9406 return $_[0]=~/\Aunnamed\d+\Z/;
9407}
9408
9409sub get_TypeName($$)
9410{
9411 my ($TypeId, $LibVersion) = @_;
9412 return $TypeInfo{$LibVersion}{$Tid_TDid{$LibVersion}{$TypeId}}{$TypeId}{"Name"};
9413}
9414
9415sub get_TypeShort($$)
9416{
9417 my ($TypeId, $LibVersion) = @_;
9418 my $TypeName = $TypeInfo{$LibVersion}{$Tid_TDid{$LibVersion}{$TypeId}}{$TypeId}{"Name"};
9419 my $NameSpace = $TypeInfo{$LibVersion}{$Tid_TDid{$LibVersion}{$TypeId}}{$TypeId}{"NameSpace"};
9420 $TypeName=~s/\A$NameSpace\:\://g;
9421 return $TypeName;
9422}
9423
9424sub get_TypeSize($$)
9425{
9426 my ($TypeId, $LibVersion) = @_;
9427 return $TypeInfo{$LibVersion}{$Tid_TDid{$LibVersion}{$TypeId}}{$TypeId}{"Size"};
9428}
9429
9430sub get_TypeAttr($$$)
9431{
9432 my ($TypeId, $LibVersion, $Attr) = @_;
9433 return $TypeInfo{$LibVersion}{$Tid_TDid{$LibVersion}{$TypeId}}{$TypeId}{$Attr};
9434}
9435
9436sub goToFirst($$$$)
9437{
9438 my ($TypeDId, $TypeId, $LibVersion, $Type_Type) = @_;
9439 if(defined $Cache{"goToFirst"}{$TypeDId}{$TypeId}{$LibVersion}{$Type_Type}) {
9440 return %{$Cache{"goToFirst"}{$TypeDId}{$TypeId}{$LibVersion}{$Type_Type}};
9441 }
9442 return () if(not $TypeInfo{$LibVersion}{$TypeDId}{$TypeId});
9443 my %Type = %{$TypeInfo{$LibVersion}{$TypeDId}{$TypeId}};
9444 return () if(not $Type{"Type"});
9445 if($Type{"Type"} ne $Type_Type)
9446 {
9447 return () if(not $Type{"BaseType"}{"TDid"} and not $Type{"BaseType"}{"Tid"});
9448 %Type = goToFirst($Type{"BaseType"}{"TDid"}, $Type{"BaseType"}{"Tid"}, $LibVersion, $Type_Type);
9449 }
9450 $Cache{"goToFirst"}{$TypeDId}{$TypeId}{$LibVersion}{$Type_Type} = \%Type;
9451 return %Type;
9452}
9453
9454my %TypeSpecAttributes = (
9455 "Const" => 1,
9456 "Volatile" => 1,
9457 "ConstVolatile" => 1,
9458 "Restrict" => 1,
9459 "Typedef" => 1
9460);
9461
9462sub get_PureType($$$)
9463{
9464 my ($TypeDId, $TypeId, $LibVersion) = @_;
9465 return "" if(not $TypeId);
9466 if(defined $Cache{"get_PureType"}{$TypeDId}{$TypeId}{$LibVersion}) {
9467 return %{$Cache{"get_PureType"}{$TypeDId}{$TypeId}{$LibVersion}};
9468 }
9469 return "" if(not $TypeInfo{$LibVersion}{$TypeDId}{$TypeId});
9470 my %Type = %{$TypeInfo{$LibVersion}{$TypeDId}{$TypeId}};
9471 return %Type if(not $Type{"BaseType"}{"TDid"} and not $Type{"BaseType"}{"Tid"});
9472 if($TypeSpecAttributes{$Type{"Type"}}) {
9473 %Type = get_PureType($Type{"BaseType"}{"TDid"}, $Type{"BaseType"}{"Tid"}, $LibVersion);
9474 }
9475 $Cache{"get_PureType"}{$TypeDId}{$TypeId}{$LibVersion} = \%Type;
9476 return %Type;
9477}
9478
9479sub get_PointerLevel($$$)
9480{
9481 my ($TypeDId, $TypeId, $LibVersion) = @_;
9482 return 0 if(not $TypeId);
9483 if(defined $Cache{"get_PointerLevel"}{$TypeDId}{$TypeId}{$LibVersion}) {
9484 return $Cache{"get_PointerLevel"}{$TypeDId}{$TypeId}{$LibVersion};
9485 }
9486 return "" if(not $TypeInfo{$LibVersion}{$TypeDId}{$TypeId});
9487 my %Type = %{$TypeInfo{$LibVersion}{$TypeDId}{$TypeId}};
9488 return 1 if($Type{"Type"}=~/FuncPtr|MethodPtr|FieldPtr/);
9489 return 0 if(not $Type{"BaseType"}{"TDid"} and not $Type{"BaseType"}{"Tid"});
9490 my $PointerLevel = 0;
9491 if($Type{"Type"} =~/Pointer|Ref|FuncPtr|MethodPtr|FieldPtr/) {
9492 $PointerLevel += 1;
9493 }
9494 $PointerLevel += get_PointerLevel($Type{"BaseType"}{"TDid"}, $Type{"BaseType"}{"Tid"}, $LibVersion);
9495 $Cache{"get_PointerLevel"}{$TypeDId}{$TypeId}{$LibVersion} = $PointerLevel;
9496 return $PointerLevel;
9497}
9498
9499sub get_BaseType($$$)
9500{
9501 my ($TypeDId, $TypeId, $LibVersion) = @_;
9502 return () if(not $TypeId);
9503 if(defined $Cache{"get_BaseType"}{$TypeDId}{$TypeId}{$LibVersion}) {
9504 return %{$Cache{"get_BaseType"}{$TypeDId}{$TypeId}{$LibVersion}};
9505 }
9506 return () if(not $TypeInfo{$LibVersion}{$TypeDId}{$TypeId});
9507 my %Type = %{$TypeInfo{$LibVersion}{$TypeDId}{$TypeId}};
9508 return %Type if(not $Type{"BaseType"}{"TDid"} and not $Type{"BaseType"}{"Tid"});
9509 %Type = get_BaseType($Type{"BaseType"}{"TDid"}, $Type{"BaseType"}{"Tid"}, $LibVersion);
9510 $Cache{"get_BaseType"}{$TypeDId}{$TypeId}{$LibVersion} = \%Type;
9511 return %Type;
9512}
9513
9514sub get_BaseTypeQual($$$)
9515{
9516 my ($TypeDId, $TypeId, $LibVersion) = @_;
9517 return "" if(not $TypeId);
9518 return "" if(not $TypeInfo{$LibVersion}{$TypeDId}{$TypeId});
9519 my %Type = %{$TypeInfo{$LibVersion}{$TypeDId}{$TypeId}};
9520 return "" if(not $Type{"BaseType"}{"TDid"} and not $Type{"BaseType"}{"Tid"});
9521 my $Qual = "";
9522 if($Type{"Type"} eq "Pointer") {
9523 $Qual .= "*";
9524 }
9525 elsif($Type{"Type"} eq "Ref") {
9526 $Qual .= "&";
9527 }
9528 elsif($Type{"Type"} eq "ConstVolatile") {
9529 $Qual .= "const volatile";
9530 }
9531 elsif($Type{"Type"} eq "Const"
9532 or $Type{"Type"} eq "Volatile"
9533 or $Type{"Type"} eq "Restrict") {
9534 $Qual .= lc($Type{"Type"});
9535 }
9536 my $BQual = get_BaseTypeQual($Type{"BaseType"}{"TDid"}, $Type{"BaseType"}{"Tid"}, $LibVersion);
9537 return $BQual.$Qual;
9538}
9539
9540sub get_OneStep_BaseType($$$)
9541{
9542 my ($TypeDId, $TypeId, $LibVersion) = @_;
9543 return () if(not $TypeId);
9544 return () if(not $TypeInfo{$LibVersion}{$TypeDId}{$TypeId});
9545 my %Type = %{$TypeInfo{$LibVersion}{$TypeDId}{$TypeId}};
9546 if(not $Type{"BaseType"}{"TDid"}
9547 and not $Type{"BaseType"}{"Tid"}) {
9548 return %Type;
9549 }
9550 return get_Type($Type{"BaseType"}{"TDid"}, $Type{"BaseType"}{"Tid"}, $LibVersion);
9551}
9552
9553sub get_Type($$$)
9554{
9555 my ($TypeDId, $TypeId, $LibVersion) = @_;
9556 return "" if(not $TypeId);
9557 return "" if(not $TypeInfo{$LibVersion}{$TypeDId}{$TypeId});
9558 return %{$TypeInfo{$LibVersion}{$TypeDId}{$TypeId}};
9559}
9560
9561sub skipGlobalData($)
9562{
9563 my $Symbol = $_[0];
9564 return ($Symbol=~/\A(_ZGV|_ZTI|_ZTS|_ZTT|_ZTV|_ZTC|_ZThn|_ZTv0_n)/);
9565}
9566
9567sub isTemplateInstance($)
9568{
9569 my $Symbol = $_[0];
9570 return 0 if($Symbol!~/\A(_Z|\?)/);
9571 my $Signature = $tr_name{$Symbol};
9572 return 0 if($Signature!~/>/);
9573 my $ShortName = substr($Signature, 0, detect_center($Signature, "("));
9574 $ShortName=~s/::operator .*//;# class::operator template<instance>
9575 return ($ShortName=~/<.+>/);
9576}
9577
9578sub isTemplateSpec($$)
9579{
9580 my ($Symbol, $LibVersion) = @_;
9581 if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
9582 {
9583 if(get_TypeAttr($ClassId, $LibVersion, "Spec"))
9584 { # class specialization
9585 return 1;
9586 }
9587 elsif($CompleteSignature{$LibVersion}{$Symbol}{"Spec"})
9588 { # method specialization
9589 return 1;
9590 }
9591 }
9592 return 0;
9593}
9594
9595sub symbolFilter($$$)
9596{ # some special cases when the symbol cannot be imported
9597 my ($Symbol, $LibVersion, $Type) = @_;
9598 if(skipGlobalData($Symbol))
9599 { # non-public global data
9600 return 0;
9601 }
9602 if($CheckObjectsOnly) {
9603 return 0 if($Symbol=~/\A(_init|_fini)\Z/);
9604 }
9605 if($CheckHeadersOnly and $UsedDump{$LibVersion}{"V"}
9606 and cmpVersions($UsedDump{$LibVersion}{"V"}, "2.7")<0)
9607 { # support for old ABI dumps in --headers-only mode
9608 foreach my $Pos (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
9609 {
9610 if(my $Pid = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"})
9611 {
9612 my $PType = get_TypeAttr($Pid, $LibVersion, "Type");
9613 if(not $PType or $PType eq "Unknown") {
9614 return 0;
9615 }
9616 }
9617 }
9618 }
9619 if($Type=~/Imported/)
9620 {
9621 my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"};
9622 if(not $STDCXX_TESTING and $Symbol=~/\A(_ZS|_ZNS|_ZNKS)/)
9623 { # stdc++ interfaces
9624 return 0;
9625 }
9626 if($SkipSymbols{$LibVersion}{$Symbol})
9627 { # user defined symbols to ignore
9628 return 0;
9629 }
9630 my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"};
9631 if(not $NameSpace and $ClassId)
9632 { # class methods have no "NameSpace" attribute
9633 $NameSpace = get_TypeAttr($ClassId, $LibVersion, "NameSpace");
9634 }
9635 if($NameSpace)
9636 { # user defined namespaces to ignore
9637 if($SkipNameSpaces{$LibVersion}{$NameSpace}) {
9638 return 0;
9639 }
9640 foreach my $NS (keys(%{$SkipNameSpaces{$LibVersion}}))
9641 { # nested namespaces
9642 if($NameSpace=~/\A\Q$NS\E(\:\:|\Z)/) {
9643 return 0;
9644 }
9645 }
9646 }
9647 if(my $Header = $CompleteSignature{$LibVersion}{$Symbol}{"Header"})
9648 {
9649 if(my $Skip = skip_header($Header, $LibVersion))
9650 { # --skip-headers or <skip_headers> (not <skip_including>)
9651 if($Skip==1) {
9652 return 0;
9653 }
9654 }
9655 if(not is_target_header($Header))
9656 { # --header, --headers-list
9657 return 0;
9658 }
9659 }
9660 if($SymbolsListPath and not $SymbolsList{$Symbol})
9661 { # user defined symbols
9662 return 0;
9663 }
9664 if($AppPath and not $SymbolsList_App{$Symbol})
9665 { # user defined symbols (in application)
9666 return 0;
9667 }
9668 if($CompleteSignature{$LibVersion}{$Symbol}{"InLine"}
9669 or (isTemplateInstance($Symbol) and not isTemplateSpec($Symbol, $LibVersion)))
9670 {
9671 if($ClassId and $CompleteSignature{$LibVersion}{$Symbol}{"Virt"})
9672 { # inline virtual methods
9673 if($Type=~/InlineVirtual/) {
9674 return 1;
9675 }
9676 my $Allocable = (not isCopyingClass($ClassId, $LibVersion));
9677 if(not $Allocable)
9678 { # check bases
9679 foreach my $DCId (get_sub_classes($ClassId, $LibVersion, 1))
9680 {
9681 if(not isCopyingClass($DCId, $LibVersion))
9682 { # exists a derived class without default c-tor
9683 $Allocable=1;
9684 last;
9685 }
9686 }
9687 }
9688 if(not $Allocable) {
9689 return 0;
9690 }
9691 }
9692 else
9693 { # inline non-virtual methods
9694 return 0;
9695 }
9696 }
9697 }
9698 return 1;
9699}
9700
9701sub mergeImpl()
9702{
9703 my $DiffCmd = get_CmdPath("diff");
9704 if(not $DiffCmd) {
9705 exitStatus("Not_Found", "can't find \"diff\"");
9706 }
9707 foreach my $Interface (sort keys(%{$Symbol_Library{1}}))
9708 { # implementation changes
9709 next if($CompleteSignature{1}{$Interface}{"Private"});
9710 next if(not $CompleteSignature{1}{$Interface}{"Header"} and not $CheckObjectsOnly);
9711 next if(not $Symbol_Library{2}{$Interface} and not $Symbol_Library{2}{$SymVer{2}{$Interface}});
9712 next if(not symbolFilter($Interface, 1, "Imported"));
9713 my $Impl1 = canonify_implementation($Interface_Impl{1}{$Interface});
9714 next if(not $Impl1);
9715 my $Impl2 = canonify_implementation($Interface_Impl{2}{$Interface});
9716 next if(not $Impl2);
9717 if($Impl1 ne $Impl2)
9718 {
9719 writeFile("$TMP_DIR/impl1", $Impl1);
9720 writeFile("$TMP_DIR/impl2", $Impl2);
9721 my $Diff = `$DiffCmd -rNau $TMP_DIR/impl1 $TMP_DIR/impl2`;
9722 $Diff=~s/(---|\+\+\+).+\n//g;
9723 $Diff=~s/[ ]{3,}/ /g;
9724 $Diff=~s/\n\@\@/\n \n\@\@/g;
9725 unlink("$TMP_DIR/impl1", "$TMP_DIR/impl2");
9726 %{$ImplProblems{$Interface}}=(
9727 "Diff" => get_CodeView($Diff) );
9728 }
9729 }
9730}
9731
9732sub canonify_implementation($)
9733{
9734 my $FuncBody= $_[0];
9735 return "" if(not $FuncBody);
9736 $FuncBody=~s/0x[a-f\d]+/0x?/g;# addr
9737 $FuncBody=~s/((\A|\n)[a-z]+[\t ]+)[a-f\d]+([^x]|\Z)/$1?$3/g;# call, jump
9738 $FuncBody=~s/# [a-f\d]+ /# ? /g;# call, jump
9739 $FuncBody=~s/%([a-z]+[a-f\d]*)/\%reg/g;# registers
9740 while($FuncBody=~s/\nnop[ \t]*(\n|\Z)/$1/g){};# empty op
9741 $FuncBody=~s/<.+?\.cpp.+?>/<name.cpp>/g;
9742 $FuncBody=~s/(\A|\n)[a-f\d]+ </$1? </g;# 5e74 <_ZN...
9743 $FuncBody=~s/\.L\d+/.L/g;
9744 $FuncBody=~s/#(-?)\d+/#$1?/g;# r3, [r3, #120]
9745 $FuncBody=~s/[\n]{2,}/\n/g;
9746 return $FuncBody;
9747}
9748
9749sub get_CodeView($)
9750{
9751 my $Code = $_[0];
9752 my $View = "";
9753 foreach my $Line (split(/\n/, $Code))
9754 {
9755 if($Line=~s/\A(\+|-)/$1 /g)
9756 {# bold line
9757 $View .= "<tr><td><b>".htmlSpecChars($Line)."</b></td></tr>\n";
9758 }
9759 else {
9760 $View .= "<tr><td>".htmlSpecChars($Line)."</td></tr>\n";
9761 }
9762 }
9763 return "<table class='code_view'>$View</table>\n";
9764}
9765
9766sub getImplementations($$)
9767{
9768 my ($LibVersion, $Path) = @_;
9769 return if(not $LibVersion or not -e $Path);
9770 if($OSgroup eq "macos")
9771 {
9772 my $OtoolCmd = get_CmdPath("otool");
9773 if(not $OtoolCmd) {
9774 exitStatus("Not_Found", "can't find \"otool\"");
9775 }
9776 my $CurInterface = "";
9777 foreach my $Line (split(/\n/, `$OtoolCmd -tv $Path 2>$TMP_DIR/null`))
9778 {
9779 if($Line=~/\A\s*_(\w+)\s*:/i) {
9780 $CurInterface = $1;
9781 }
9782 elsif($Line=~/\A\s*[\da-z]+\s+(.+?)\Z/i) {
9783 $Interface_Impl{$LibVersion}{$CurInterface} .= "$1\n";
9784 }
9785 }
9786 }
9787 else
9788 {
9789 my $ObjdumpCmd = get_CmdPath("objdump");
9790 if(not $ObjdumpCmd) {
9791 exitStatus("Not_Found", "can't find \"objdump\"");
9792 }
9793 my $CurInterface = "";
9794 foreach my $Line (split(/\n/, `$ObjdumpCmd -d $Path 2>$TMP_DIR/null`))
9795 {
9796 if($Line=~/\A[\da-z]+\s+<(\w+)>/i) {
9797 $CurInterface = $1;
9798 }
9799 else
9800 { # x86: 51fa:(\t)89 e5 (\t)mov %esp,%ebp
9801 # arm: 5020:(\t)e24cb004(\t)sub(\t)fp, ip, #4(\t); 0x4
9802 if($Line=~/\A\s*[a-f\d]+:\s+([a-f\d]+\s+)+([a-z]+\s+.*?)\s*(;.*|)\Z/i) {
9803 $Interface_Impl{$LibVersion}{$CurInterface} .= "$2\n";
9804 }
9805 }
9806 }
9807 }
9808}
9809
9810sub detectAdded()
9811{
9812 foreach my $Symbol (keys(%{$Symbol_Library{2}}))
9813 {
9814 if(link_symbol($Symbol, 1, "+Deps"))
9815 { # linker can find a new symbol
9816 # in the old-version library
9817 # So, it's not a new symbol
9818 next;
9819 }
9820 if(my $VSym = $SymVer{2}{$Symbol}
9821 and $Symbol!~/\@/) {
9822 next;
9823 }
9824 $AddedInt{$Symbol} = 1;
9825 }
9826}
9827
9828sub detectRemoved()
9829{
9830 foreach my $Symbol (keys(%{$Symbol_Library{1}}))
9831 {
9832 if($CheckObjectsOnly) {
9833 $CheckedSymbols{$Symbol} = 1;
9834 }
9835 if(link_symbol($Symbol, 2, "+Deps"))
9836 { # linker can find an old symbol
9837 # in the new-version library
9838 next;
9839 }
9840 if(my $VSym = $SymVer{1}{$Symbol}
9841 and $Symbol!~/\@/) {
9842 next;
9843 }
9844 $RemovedInt{$Symbol} = 1;
9845 }
9846}
9847
9848sub mergeLibs()
9849{
9850 foreach my $Symbol (sort keys(%AddedInt))
9851 { # checking added symbols
9852 next if($CompleteSignature{2}{$Symbol}{"Private"});
9853 next if(not $CompleteSignature{2}{$Symbol}{"Header"} and not $CheckObjectsOnly);
9854 next if(not symbolFilter($Symbol, 2, "Imported"));
9855 %{$CompatProblems{$Symbol}{"Added_Interface"}{""}}=();
9856 }
9857 foreach my $Symbol (sort keys(%RemovedInt))
9858 { # checking removed symbols
9859 next if($CompleteSignature{1}{$Symbol}{"Private"});
9860 next if(not $CompleteSignature{1}{$Symbol}{"Header"} and not $CheckObjectsOnly);
9861 if($Symbol=~/\A_ZTV/)
9862 { # skip v-tables for templates, that should not be imported by applications
9863 next if($tr_name{$Symbol}=~/</);
9864 if(not keys(%{$ClassMethods{1}{$VTableClass{1}{$Symbol}}}))
9865 { # vtables for "private" classes
9866 # use case: vtable for QDragManager (Qt 4.5.3 to 4.6.0) became HIDDEN symbol
9867 next;
9868 }
9869 }
9870 else {
9871 next if(not symbolFilter($Symbol, 1, "Imported"));
9872 }
9873 if($CompleteSignature{1}{$Symbol}{"PureVirt"})
9874 { # symbols for pure virtual methods cannot be called by clients
9875 next;
9876 }
9877 %{$CompatProblems{$Symbol}{"Removed_Interface"}{""}}=();
9878 }
9879}
9880
9881sub detectAdded_H()
9882{
9883 foreach my $Symbol (sort keys(%{$CompleteSignature{2}}))
9884 {
9885 if($GeneratedSymbols{$Symbol}) {
9886 next;
9887 }
9888 if(not $CompleteSignature{1}{$Symbol}) {
9889 $AddedInt{$Symbol} = 1;
9890 }
9891 }
9892}
9893
9894sub detectRemoved_H()
9895{
9896 foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
9897 {
9898 if($GeneratedSymbols{$Symbol}) {
9899 next;
9900 }
9901 if(not $CompleteSignature{2}{$Symbol}) {
9902 $RemovedInt{$Symbol} = 1;
9903 }
9904 }
9905}
9906
9907sub mergeHeaders()
9908{
9909 foreach my $Symbol (sort keys(%AddedInt))
9910 { # checking added symbols
9911 next if($CompleteSignature{2}{$Symbol}{"PureVirt"});
9912 next if($CompleteSignature{2}{$Symbol}{"InLine"});
9913 next if($CompleteSignature{2}{$Symbol}{"Private"});
9914 next if(not symbolFilter($Symbol, 2, "Imported"));
9915 %{$CompatProblems{$Symbol}{"Added_Interface"}{""}}=();
9916 }
9917 foreach my $Symbol (sort keys(%RemovedInt))
9918 { # checking removed symbols
9919 next if($CompleteSignature{1}{$Symbol}{"PureVirt"});
9920 next if($CompleteSignature{1}{$Symbol}{"InLine"});
9921 next if($CompleteSignature{1}{$Symbol}{"Private"});
9922 next if(not symbolFilter($Symbol, 1, "Imported"));
9923 %{$CompatProblems{$Symbol}{"Removed_Interface"}{""}}=();
9924 }
9925}
9926
9927sub addParamNames($)
9928{
9929 my $LibraryVersion = $_[0];
9930 return if(not keys(%AddIntParams));
9931 my $SecondVersion = $LibraryVersion==1?2:1;
9932 foreach my $Interface (sort keys(%{$CompleteSignature{$LibraryVersion}}))
9933 {
9934 next if(not keys(%{$AddIntParams{$Interface}}));
9935 foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibraryVersion}{$Interface}{"Param"}}))
9936 {# add absent parameter names
9937 my $ParamName = $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"};
9938 if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddIntParams{$Interface}{$ParamPos})
9939 {# names from the external file
9940 if(defined $CompleteSignature{$SecondVersion}{$Interface}
9941 and defined $CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos})
9942 {
9943 if($CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) {
9944 $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
9945 }
9946 }
9947 else {
9948 $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
9949 }
9950 }
9951 }
9952 }
9953}
9954
9955sub detectChangedTypedefs()
9956{ # detect changed typedefs to create correct function signatures
9957 foreach my $Typedef (keys(%{$Typedef_BaseName{1}}))
9958 {
9959 next if(not $Typedef);
9960 next if(isAnon($Typedef_BaseName{1}{$Typedef}));
9961 next if(isAnon($Typedef_BaseName{2}{$Typedef}));
9962 next if(not $Typedef_BaseName{1}{$Typedef});
9963 next if(not $Typedef_BaseName{2}{$Typedef});# exclude added/removed
9964 if($Typedef_BaseName{1}{$Typedef} ne $Typedef_BaseName{2}{$Typedef}) {
9965 $ChangedTypedef{$Typedef} = 1;
9966 }
9967 }
9968}
9969
9970sub get_symbol_suffix($$)
9971{
9972 my ($Interface, $Full) = @_;
9973 $Interface=~s/\A([^\@\$\?]+)[\@\$]+/$1/g;# remove version
9974 my $Signature = $tr_name{$Interface};
9975 my $Suffix = substr($Signature, detect_center($Signature, "("));
9976 if(not $Full) {
9977 $Suffix=~s/(\))\s*(const volatile|volatile const|const|volatile)\Z/$1/g;
9978 }
9979 return $Suffix;
9980}
9981
9982sub get_symbol_prefix($$)
9983{
9984 my ($Symbol, $LibVersion) = @_;
9985 my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"};
9986 if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
9987 { # methods
9988 $ShortName = get_TypeName($ClassId, $LibVersion)."::".$ShortName;
9989 }
9990 return $ShortName;
9991}
9992
9993sub mergeSignatures()
9994{
9995 my %SubProblems = ();
9996
9997 registerVirtualTable(1);
9998 registerVirtualTable(2);
9999
10000 if($UsedDump{1}{"V"} and cmpVersions($UsedDump{1}{"V"}, "1.22")<0
10001 and (not $UsedDump{2}{"V"} or cmpVersions($UsedDump{2}{"V"}, "1.22")>=0))
10002 { # support for old ABI dumps
10003 foreach my $ClassName (keys(%{$VirtualTable{2}}))
10004 {
10005 if($ClassName=~/</)
10006 { # templates
10007 if(not defined $VirtualTable{1}{$ClassName})
10008 { # synchronize
10009 delete($VirtualTable{2}{$ClassName});
10010 }
10011 }
10012 }
10013 }
10014
10015 registerOverriding(1);
10016 registerOverriding(2);
10017
10018 setVirtFuncPositions(1);
10019 setVirtFuncPositions(2);
10020
10021 addParamNames(1);
10022 addParamNames(2);
10023
10024 detectChangedTypedefs();
10025 mergeBases();
10026 my %AddedOverloads = ();
10027 foreach my $Interface (sort keys(%AddedInt))
10028 { # check all added exported symbols
10029 next if(not $CompleteSignature{2}{$Interface}{"Header"});
10030 if(defined $CompleteSignature{1}{$Interface}
10031 and $CompleteSignature{1}{$Interface}{"Header"})
10032 { # double-check added symbol
10033 next;
10034 }
10035 next if(not symbolFilter($Interface, 2, "Imported"));
10036 if($Interface=~/\A(_Z|\?)/)
10037 { # C++
10038 $AddedOverloads{get_symbol_prefix($Interface, 2)}{get_symbol_suffix($Interface, 1)} = $Interface;
10039 }
10040 if(my $OverriddenMethod = $CompleteSignature{2}{$Interface}{"Override"})
10041 { # register virtual redefinition
10042 my $AffectedClass_Name = get_TypeName($CompleteSignature{2}{$Interface}{"Class"}, 2);
10043 if(defined $CompleteSignature{1}{$OverriddenMethod}
10044 and $CompleteSignature{1}{$OverriddenMethod}{"Virt"} and $ClassToId{1}{$AffectedClass_Name}
10045 and not $CompleteSignature{1}{$OverriddenMethod}{"Private"})
10046 { # public virtual methods, virtual destructors: class should exist in previous version
10047 if(isCopyingClass($ClassToId{1}{$AffectedClass_Name}, 1))
10048 { # old v-table (copied) will be used by applications
10049 next;
10050 }
10051 if(defined $CompleteSignature{1}{$Interface}
10052 and $CompleteSignature{1}{$Interface}{"InLine"})
10053 { # auto-generated virtual destructors stay in the header (and v-table), added to library
10054 # use case: Ice 3.3.1 -> 3.4.0
10055 next;
10056 }
10057 %{$CompatProblems{$OverriddenMethod}{"Overridden_Virtual_Method"}{$tr_name{$Interface}}}=(
10058 "Type_Name"=>$AffectedClass_Name,
10059 "Type_Type"=>"Class",
10060 "Target"=>get_Signature($Interface, 2),
10061 "Old_Value"=>get_Signature($OverriddenMethod, 2),
10062 "New_Value"=>get_Signature($Interface, 2) );
10063 }
10064 }
10065 }
10066 foreach my $Interface (sort keys(%RemovedInt))
10067 {
10068 next if(not $CompleteSignature{1}{$Interface}{"Header"});
10069 if(defined $CompleteSignature{2}{$Interface}
10070 and $CompleteSignature{2}{$Interface}{"Header"})
10071 { # double-check removed symbol
10072 next;
10073 }
10074 next if($CompleteSignature{1}{$Interface}{"Private"}); # skip private methods
10075 next if(not symbolFilter($Interface, 1, "Imported"));
10076 $CheckedSymbols{$Interface} = 1;
10077 if(my $OverriddenMethod = $CompleteSignature{1}{$Interface}{"Override"})
10078 { # register virtual redefinition
10079 my $AffectedClass_Name = get_TypeName($CompleteSignature{1}{$Interface}{"Class"}, 1);
10080 if(defined $CompleteSignature{2}{$OverriddenMethod}
10081 and $CompleteSignature{2}{$OverriddenMethod}{"Virt"} and $ClassToId{2}{$AffectedClass_Name})
10082 { # virtual methods, virtual destructors: class should exist in newer version
10083 if(isCopyingClass($CompleteSignature{1}{$Interface}{"Class"}, 1))
10084 { # old v-table (copied) will be used by applications
10085 next;
10086 }
10087 if(defined $CompleteSignature{2}{$Interface}
10088 and $CompleteSignature{2}{$Interface}{"InLine"})
10089 { # auto-generated virtual destructors stay in the header (and v-table), removed from library
10090 # use case: Ice 3.3.1 -> 3.4.0
10091 next;
10092 }
10093 %{$CompatProblems{$Interface}{"Overridden_Virtual_Method_B"}{$tr_name{$OverriddenMethod}}}=(
10094 "Type_Name"=>$AffectedClass_Name,
10095 "Type_Type"=>"Class",
10096 "Target"=>get_Signature($OverriddenMethod, 1),
10097 "Old_Value"=>get_Signature($Interface, 1),
10098 "New_Value"=>get_Signature($OverriddenMethod, 1) );
10099 }
10100 }
10101 if($OSgroup eq "windows")
10102 { # register the reason of symbol name change
10103 if(my $NewSymbol = $mangled_name{2}{$tr_name{$Interface}})
10104 {
10105 if($AddedInt{$NewSymbol})
10106 {
10107 if($CompleteSignature{1}{$Interface}{"Static"} ne $CompleteSignature{2}{$NewSymbol}{"Static"})
10108 {
10109 if($CompleteSignature{2}{$NewSymbol}{"Static"}) {
10110 %{$CompatProblems{$Interface}{"Symbol_Changed_Static"}{$tr_name{$Interface}}}=(
10111 "Target"=>$tr_name{$Interface},
10112 "Old_Value"=>$Interface,
10113 "New_Value"=>$NewSymbol );
10114 }
10115 else {
10116 %{$CompatProblems{$Interface}{"Symbol_Changed_NonStatic"}{$tr_name{$Interface}}}=(
10117 "Target"=>$tr_name{$Interface},
10118 "Old_Value"=>$Interface,
10119 "New_Value"=>$NewSymbol );
10120 }
10121 }
10122 if($CompleteSignature{1}{$Interface}{"Virt"} ne $CompleteSignature{2}{$NewSymbol}{"Virt"})
10123 {
10124 if($CompleteSignature{2}{$NewSymbol}{"Virt"}) {
10125 %{$CompatProblems{$Interface}{"Symbol_Changed_Virtual"}{$tr_name{$Interface}}}=(
10126 "Target"=>$tr_name{$Interface},
10127 "Old_Value"=>$Interface,
10128 "New_Value"=>$NewSymbol );
10129 }
10130 else {
10131 %{$CompatProblems{$Interface}{"Symbol_Changed_NonVirtual"}{$tr_name{$Interface}}}=(
10132 "Target"=>$tr_name{$Interface},
10133 "Old_Value"=>$Interface,
10134 "New_Value"=>$NewSymbol );
10135 }
10136 }
10137 my $ReturnTypeName1 = get_TypeName($CompleteSignature{1}{$Interface}{"Return"}, 1);
10138 my $ReturnTypeName2 = get_TypeName($CompleteSignature{2}{$NewSymbol}{"Return"}, 2);
10139 if($ReturnTypeName1 ne $ReturnTypeName2)
10140 {
10141 my $ProblemType = "Symbol_Changed_Return";
10142 if($CompleteSignature{1}{$Interface}{"Data"}) {
10143 $ProblemType = "Global_Data_Symbol_Changed_Type";
10144 }
10145 %{$CompatProblems{$Interface}{$ProblemType}{$tr_name{$Interface}}}=(
10146 "Target"=>$tr_name{$Interface},
10147 "Old_Type"=>$ReturnTypeName1,
10148 "New_Type"=>$ReturnTypeName2,
10149 "Old_Value"=>$Interface,
10150 "New_Value"=>$NewSymbol );
10151 }
10152 }
10153 }
10154 }
10155 if($Interface=~/\A(_Z|\?)/)
10156 { # C++
10157 my $Prefix = get_symbol_prefix($Interface, 1);
10158 if(my @Overloads = sort keys(%{$AddedOverloads{$Prefix}})
10159 and not $AddedOverloads{$Prefix}{get_symbol_suffix($Interface, 1)})
10160 { # changed signature: params, "const"-qualifier
10161 my $NewSymbol = $AddedOverloads{$Prefix}{$Overloads[0]};
10162 if($CompleteSignature{1}{$Interface}{"Constructor"}) {
10163 if($Interface=~/(C1E|C2E)/) {
10164 my $CtorType = $1;
10165 $NewSymbol=~s/(C1E|C2E)/$CtorType/g;
10166 }
10167 }
10168 elsif($CompleteSignature{1}{$Interface}{"Destructor"}) {
10169 if($Interface=~/(D0E|D1E|D2E)/) {
10170 my $DtorType = $1;
10171 $NewSymbol=~s/(D0E|D1E|D2E)/$DtorType/g;
10172 }
10173 }
10174 if($CompleteSignature{1}{$Interface}{"NameSpace"} eq $CompleteSignature{2}{$NewSymbol}{"NameSpace"})
10175 { # from the same class and namespace
10176 if($CompleteSignature{1}{$Interface}{"Const"}
10177 and not $CompleteSignature{2}{$NewSymbol}{"Const"})
10178 { # "const" to non-"const"
10179 %{$CompatProblems{$Interface}{"Symbol_Changed_Became_NonConst"}{$tr_name{$Interface}}}=(
10180 "Target"=>$tr_name{$Interface},
10181 "New_Signature"=>get_Signature($NewSymbol, 2),
10182 "Old_Value"=>$Interface,
10183 "New_Value"=>$NewSymbol );
10184 }
10185 elsif(not $CompleteSignature{1}{$Interface}{"Const"}
10186 and $CompleteSignature{2}{$NewSymbol}{"Const"})
10187 { # non-"const" to "const"
10188 %{$CompatProblems{$Interface}{"Symbol_Changed_Became_Const"}{$tr_name{$Interface}}}=(
10189 "Target"=>$tr_name{$Interface},
10190 "New_Signature"=>get_Signature($NewSymbol, 2),
10191 "Old_Value"=>$Interface,
10192 "New_Value"=>$NewSymbol );
10193 }
10194 if($CompleteSignature{1}{$Interface}{"Volatile"}
10195 and not $CompleteSignature{2}{$NewSymbol}{"Volatile"})
10196 { # "volatile" to non-"volatile"
10197
10198 %{$CompatProblems{$Interface}{"Symbol_Changed_Became_NonVolatile"}{$tr_name{$Interface}}}=(
10199 "Target"=>$tr_name{$Interface},
10200 "New_Signature"=>get_Signature($NewSymbol, 2),
10201 "Old_Value"=>$Interface,
10202 "New_Value"=>$NewSymbol );
10203 }
10204 elsif(not $CompleteSignature{1}{$Interface}{"Volatile"}
10205 and $CompleteSignature{2}{$NewSymbol}{"Volatile"})
10206 { # non-"volatile" to "volatile"
10207 %{$CompatProblems{$Interface}{"Symbol_Changed_Became_Volatile"}{$tr_name{$Interface}}}=(
10208 "Target"=>$tr_name{$Interface},
10209 "New_Signature"=>get_Signature($NewSymbol, 2),
10210 "Old_Value"=>$Interface,
10211 "New_Value"=>$NewSymbol );
10212 }
10213 if(get_symbol_suffix($Interface, 0) ne get_symbol_suffix($NewSymbol, 0))
10214 { # params list
10215 %{$CompatProblems{$Interface}{"Symbol_Changed_Parameters"}{$tr_name{$Interface}}}=(
10216 "Target"=>$tr_name{$Interface},
10217 "New_Signature"=>get_Signature($NewSymbol, 2),
10218 "Old_Value"=>$Interface,
10219 "New_Value"=>$NewSymbol );
10220 }
10221 }
10222 }
10223 }
10224 }
10225 foreach my $Interface (sort keys(%{$CompleteSignature{1}}))
10226 { # checking interfaces
10227 if($Interface!~/[\@\$\?]/)
10228 { # symbol without version
10229 if(my $VSym = $SymVer{1}{$Interface})
10230 { # the symbol is linked with versioned symbol
10231 if($CompleteSignature{2}{$VSym}{"MnglName"})
10232 { # show report for symbol@ver only
10233 next;
10234 }
10235 elsif(not link_symbol($VSym, 2, "-Deps"))
10236 { # changed version: sym@v1 to sym@v2
10237 # do NOT show report for symbol
10238 next;
10239 }
10240 }
10241 }
10242 if($CompleteSignature{1}{$Interface}{"Private"})
10243 { # private symbols
10244 next;
10245 }
10246 if(not $CompleteSignature{1}{$Interface}{"MnglName"}
10247 or not $CompleteSignature{2}{$Interface}{"MnglName"})
10248 { # absent mangled name
10249 next;
10250 }
10251 if(not $CompleteSignature{1}{$Interface}{"Header"}
10252 or not $CompleteSignature{2}{$Interface}{"Header"})
10253 { # without a header
10254 next;
10255 }
10256 if($CheckHeadersOnly)
10257 { # skip added and removed pure virtual methods
10258 next if(not $CompleteSignature{1}{$Interface}{"PureVirt"} and $CompleteSignature{2}{$Interface}{"PureVirt"});
10259 next if($CompleteSignature{1}{$Interface}{"PureVirt"} and not $CompleteSignature{2}{$Interface}{"PureVirt"});
10260 }
10261 else
10262 { # skip external, added and removed functions except pure virtual methods
10263 if(not link_symbol($Interface, 1, "-Deps")
10264 or not link_symbol($Interface, 2, "-Deps"))
10265 { # symbols from target library(ies) only
10266 # excluding dependent libraries
10267 if(not $CompleteSignature{1}{$Interface}{"PureVirt"}
10268 or not $CompleteSignature{2}{$Interface}{"PureVirt"}) {
10269 next;
10270 }
10271 }
10272 }
10273 if(not symbolFilter($Interface, 1, "Imported|InlineVirtual"))
10274 { # symbols that cannot be imported
10275 next;
10276 }
10277 # checking virtual table
10278 if($CompleteSignature{1}{$Interface}{"Class"}) {
10279 mergeVirtualTables($Interface);
10280 }
10281 if($COMPILE_ERRORS)
10282 { # if some errors occurred at the compiling stage
10283 # then some false positives can be skipped here
10284 if(not $CompleteSignature{1}{$Interface}{"Data"} and $CompleteSignature{2}{$Interface}{"Data"}
10285 and not $CompleteSignature{2}{$Interface}{"Object"})
10286 { # missed information about parameters in newer version
10287 next;
10288 }
10289 if($CompleteSignature{1}{$Interface}{"Data"} and not $CompleteSignature{1}{$Interface}{"Object"}
10290 and not $CompleteSignature{2}{$Interface}{"Data"})
10291 {# missed information about parameters in older version
10292 next;
10293 }
10294 }
10295 my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Interface);
10296 # checking attributes
10297 if($CompleteSignature{2}{$Interface}{"Static"}
10298 and not $CompleteSignature{1}{$Interface}{"Static"} and $Interface=~/\A(_Z|\?)/) {
10299 %{$CompatProblems{$Interface}{"Method_Became_Static"}{""}}=();
10300 }
10301 elsif(not $CompleteSignature{2}{$Interface}{"Static"}
10302 and $CompleteSignature{1}{$Interface}{"Static"} and $Interface=~/\A(_Z|\?)/) {
10303 %{$CompatProblems{$Interface}{"Method_Became_NonStatic"}{""}}=();
10304 }
10305 if(($CompleteSignature{1}{$Interface}{"Virt"} and $CompleteSignature{2}{$Interface}{"Virt"})
10306 or ($CompleteSignature{1}{$Interface}{"PureVirt"} and $CompleteSignature{2}{$Interface}{"PureVirt"}))
10307 { # relative position of virtual and pure virtual methods
10308 if($CompleteSignature{1}{$Interface}{"RelPos"}!=$CompleteSignature{2}{$Interface}{"RelPos"})
10309 {
10310 my $Class_Id = $CompleteSignature{1}{$Interface}{"Class"};
10311 if($VirtualTable{1}{get_TypeName($Class_Id, 1)}{$Interface}!=$VirtualTable{2}{get_TypeName($Class_Id, 1)}{$Interface})
10312 { # check the absolute position of virtual method (including added and removed methods)
10313 my %Class_Type = get_Type($Tid_TDid{1}{$Class_Id}, $Class_Id, 1);
10314 my $ProblemType = "Virtual_Method_Position";
10315 if($CompleteSignature{1}{$Interface}{"PureVirt"}) {
10316 $ProblemType = "Pure_Virtual_Method_Position";
10317 }
10318 if(isUsedClass($Class_Id, 1))
10319 {
10320 my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
10321 foreach my $AffectedInterface (@Affected)
10322 {
10323 %{$CompatProblems{$AffectedInterface}{$ProblemType}{$tr_name{$MnglName}}}=(
10324 "Type_Name"=>$Class_Type{"Name"},
10325 "Type_Type"=>"Class",
10326 "Old_Value"=>$CompleteSignature{1}{$Interface}{"RelPos"},
10327 "New_Value"=>$CompleteSignature{2}{$Interface}{"RelPos"},
10328 "Target"=>get_Signature($Interface, 1) );
10329 }
10330 $VTableChanged{$Class_Type{"Name"}} = 1;
10331 }
10332 }
10333 }
10334 }
10335 if($CompleteSignature{1}{$Interface}{"PureVirt"}
10336 or $CompleteSignature{2}{$Interface}{"PureVirt"})
10337 { # do NOT check type changes in pure virtuals
10338 next;
10339 }
10340 $CheckedSymbols{$Interface}=1;
10341 if($Interface=~/\A(_Z|\?)/
10342 or keys(%{$CompleteSignature{1}{$Interface}{"Param"}})==keys(%{$CompleteSignature{2}{$Interface}{"Param"}}))
10343 { # C/C++: changes in parameters
10344 foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Interface}{"Param"}}))
10345 { # checking parameters
10346 mergeParameters($Interface, $ParamPos, $ParamPos);
10347 }
10348 }
10349 else
10350 { # C: added/removed parameters
10351 foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{2}{$Interface}{"Param"}}))
10352 { # checking added parameters
10353 my $ParamType2_Id = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos}{"type"};
10354 last if(get_TypeName($ParamType2_Id, 2) eq "...");
10355 my $Parameter_Name = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos}{"name"};
10356 my $Parameter_OldName = (defined $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos})?$CompleteSignature{1}{$Interface}{"Param"}{$ParamPos}{"name"}:"";
10357 my $ParamPos_Prev = "-1";
10358 if($Parameter_Name=~/\Ap\d+\Z/i)
10359 { # added unnamed parameter ( pN )
10360 my @Positions1 = find_ParamPair_Pos_byTypeAndPos(get_TypeName($ParamType2_Id, 2), $ParamPos, "backward", $Interface, 1);
10361 my @Positions2 = find_ParamPair_Pos_byTypeAndPos(get_TypeName($ParamType2_Id, 2), $ParamPos, "backward", $Interface, 2);
10362 if($#Positions1==-1 or $#Positions2>$#Positions1) {
10363 $ParamPos_Prev = "lost";
10364 }
10365 }
10366 else {
10367 $ParamPos_Prev = find_ParamPair_Pos_byName($Parameter_Name, $Interface, 1);
10368 }
10369 if($ParamPos_Prev eq "lost")
10370 {
10371 if($ParamPos>keys(%{$CompleteSignature{1}{$Interface}{"Param"}})-1)
10372 {
10373 my $ProblemType = "Added_Parameter";
10374 if($Parameter_Name=~/\Ap\d+\Z/) {
10375 $ProblemType = "Added_Unnamed_Parameter";
10376 }
10377 %{$CompatProblems{$Interface}{$ProblemType}{numToStr($ParamPos+1)." Parameter"}}=(
10378 "Target"=>$Parameter_Name,
10379 "Param_Pos"=>$ParamPos,
10380 "Param_Type"=>get_TypeName($ParamType2_Id, 2),
10381 "New_Signature"=>get_Signature($Interface, 2) );
10382 }
10383 else
10384 {
10385 my %ParamType_Pure = get_PureType($Tid_TDid{2}{$ParamType2_Id}, $ParamType2_Id, 2);
10386 my $ParamStraightPairType_Id = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos}{"type"};
10387 my %ParamStraightPairType_Pure = get_PureType($Tid_TDid{1}{$ParamStraightPairType_Id}, $ParamStraightPairType_Id, 1);
10388 if(($ParamType_Pure{"Name"} eq $ParamStraightPairType_Pure{"Name"} or get_TypeName($ParamType2_Id, 2) eq get_TypeName($ParamStraightPairType_Id, 1))
10389 and find_ParamPair_Pos_byName($Parameter_OldName, $Interface, 2) eq "lost")
10390 {
10391 if($Parameter_OldName!~/\Ap\d+\Z/ and $Parameter_Name!~/\Ap\d+\Z/)
10392 {
10393 %{$CompatProblems{$Interface}{"Renamed_Parameter"}{numToStr($ParamPos+1)." Parameter"}}=(
10394 "Target"=>$Parameter_OldName,
10395 "Param_Pos"=>$ParamPos,
10396 "Param_Type"=>get_TypeName($ParamType2_Id, 2),
10397 "Old_Value"=>$Parameter_OldName,
10398 "New_Value"=>$Parameter_Name,
10399 "New_Signature"=>get_Signature($Interface, 2) );
10400 }
10401 }
10402 else
10403 {
10404 my $ProblemType = "Added_Middle_Parameter";
10405 if($Parameter_Name=~/\Ap\d+\Z/) {
10406 $ProblemType = "Added_Middle_Unnamed_Parameter";
10407 }
10408 %{$CompatProblems{$Interface}{$ProblemType}{numToStr($ParamPos+1)." Parameter"}}=(
10409 "Target"=>$Parameter_Name,
10410 "Param_Pos"=>$ParamPos,
10411 "Param_Type"=>get_TypeName($ParamType2_Id, 2),
10412 "New_Signature"=>get_Signature($Interface, 2) );
10413 }
10414 }
10415 }
10416 }
10417 foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Interface}{"Param"}}))
10418 { # check relevant parameters
10419 my $ParamType1_Id = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos}{"type"};
10420 my $ParamName1 = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos}{"name"};
10421 # FIXME: find relevant parameter by name
10422 if(defined $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos})
10423 {
10424 my $ParamType2_Id = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos}{"type"};
10425 my $ParamName2 = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos}{"name"};
10426 if(($ParamName1!~/\Ap\d+\Z/i and $ParamName1 eq $ParamName2)
10427 or get_TypeName($ParamType1_Id, 1) eq get_TypeName($ParamType2_Id, 2)) {
10428 mergeParameters($Interface, $ParamPos, $ParamPos);
10429 }
10430 }
10431 }
10432 foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Interface}{"Param"}}))
10433 { # checking removed parameters
10434 my $ParamType1_Id = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos}{"type"};
10435 last if(get_TypeName($ParamType1_Id, 1) eq "...");
10436 my $Parameter_Name = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos}{"name"};
10437 my $Parameter_NewName = (defined $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos})?$CompleteSignature{2}{$Interface}{"Param"}{$ParamPos}{"name"}:"";
10438 my $ParamPos_New = "-1";
10439 if($Parameter_Name=~/\Ap\d+\Z/i)
10440 { # removed unnamed parameter ( pN )
10441 my @Positions1 = find_ParamPair_Pos_byTypeAndPos(get_TypeName($ParamType1_Id, 1), $ParamPos, "forward", $Interface, 1);
10442 my @Positions2 = find_ParamPair_Pos_byTypeAndPos(get_TypeName($ParamType1_Id, 1), $ParamPos, "forward", $Interface, 2);
10443 if($#Positions2==-1 or $#Positions2<$#Positions1) {
10444 $ParamPos_New = "lost";
10445 }
10446 }
10447 else {
10448 $ParamPos_New = find_ParamPair_Pos_byName($Parameter_Name, $Interface, 2);
10449 }
10450 if($ParamPos_New eq "lost")
10451 {
10452 if($ParamPos>keys(%{$CompleteSignature{2}{$Interface}{"Param"}})-1)
10453 {
10454 my $ProblemType = "Removed_Parameter";
10455 if($Parameter_Name=~/\Ap\d+\Z/) {
10456 $ProblemType = "Removed_Unnamed_Parameter";
10457 }
10458 %{$CompatProblems{$Interface}{$ProblemType}{numToStr($ParamPos+1)." Parameter"}}=(
10459 "Target"=>$Parameter_Name,
10460 "Param_Pos"=>$ParamPos,
10461 "Param_Type"=>get_TypeName($ParamType1_Id, 1),
10462 "New_Signature"=>get_Signature($Interface, 2) );
10463 }
10464 elsif($ParamPos<keys(%{$CompleteSignature{1}{$Interface}{"Param"}})-1)
10465 {
10466 my %ParamType_Pure = get_PureType($Tid_TDid{1}{$ParamType1_Id}, $ParamType1_Id, 1);
10467 my $ParamStraightPairType_Id = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos}{"type"};
10468 my %ParamStraightPairType_Pure = get_PureType($Tid_TDid{2}{$ParamStraightPairType_Id}, $ParamStraightPairType_Id, 2);
10469 if(($ParamType_Pure{"Name"} eq $ParamStraightPairType_Pure{"Name"} or get_TypeName($ParamType1_Id, 1) eq get_TypeName($ParamStraightPairType_Id, 2))
10470 and find_ParamPair_Pos_byName($Parameter_NewName, $Interface, 1) eq "lost")
10471 {
10472 if($Parameter_NewName!~/\Ap\d+\Z/ and $Parameter_Name!~/\Ap\d+\Z/)
10473 {
10474 %{$CompatProblems{$Interface}{"Renamed_Parameter"}{numToStr($ParamPos+1)." Parameter"}}=(
10475 "Target"=>$Parameter_Name,
10476 "Param_Pos"=>$ParamPos,
10477 "Param_Type"=>get_TypeName($ParamType1_Id, 1),
10478 "Old_Value"=>$Parameter_Name,
10479 "New_Value"=>$Parameter_NewName,
10480 "New_Signature"=>get_Signature($Interface, 2) );
10481 }
10482 }
10483 else
10484 {
10485 my $ProblemType = "Removed_Middle_Parameter";
10486 if($Parameter_Name=~/\Ap\d+\Z/) {
10487 $ProblemType = "Removed_Middle_Unnamed_Parameter";
10488 }
10489 %{$CompatProblems{$Interface}{$ProblemType}{numToStr($ParamPos+1)." Parameter"}}=(
10490 "Target"=>$Parameter_Name,
10491 "Param_Pos"=>$ParamPos,
10492 "Param_Type"=>get_TypeName($ParamType1_Id, 1),
10493 "New_Signature"=>get_Signature($Interface, 2) );
10494 }
10495 }
10496 }
10497 }
10498 }
10499 # checking return type
10500 my $ReturnType1_Id = $CompleteSignature{1}{$Interface}{"Return"};
10501 my $ReturnType2_Id = $CompleteSignature{2}{$Interface}{"Return"};
10502 %SubProblems = detectTypeChange($ReturnType1_Id, $ReturnType2_Id, "Return");
10503 foreach my $SubProblemType (keys(%SubProblems))
10504 {
10505 my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
10506 my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
10507 my $NewProblemType = $SubProblemType;
10508 if($SubProblemType eq "Return_Type_Became_Void"
10509 and keys(%{$CompleteSignature{1}{$Interface}{"Param"}}))
10510 { # parameters stack has been affected
10511 $NewProblemType = "Return_Type_Became_Void_And_Stack_Layout";
10512 }
10513 elsif($SubProblemType eq "Return_Type_From_Void")
10514 { # parameters stack has been affected
10515 if(keys(%{$CompleteSignature{1}{$Interface}{"Param"}})) {
10516 $NewProblemType = "Return_Type_From_Void_And_Stack_Layout";
10517 }
10518 else
10519 { # safe
10520 delete($SubProblems{$SubProblemType});
10521 next;
10522 }
10523 }
10524 elsif($SubProblemType eq "Return_Type_And_Size"
10525 and $CompleteSignature{1}{$Interface}{"Data"}) {
10526 $NewProblemType = "Global_Data_Type_And_Size";
10527 }
10528 elsif($SubProblemType eq "Return_Type")
10529 {
10530 if($CompleteSignature{1}{$Interface}{"Data"})
10531 {
10532 if(removedConstness($Old_Value, $New_Value))
10533 { # const -> non-const global data
10534 $NewProblemType = "Global_Data_Became_Non_Const";
10535 }
10536 elsif(removedConstness($New_Value, $Old_Value))
10537 { # non-const -> const global data
10538 $NewProblemType = "Global_Data_Became_Const";
10539 }
10540 else {
10541 $NewProblemType = "Global_Data_Type";
10542 }
10543 }
10544 else
10545 {
10546 if(removedConstness($New_Value, $Old_Value)) {
10547 $NewProblemType = "Return_Type_Became_Const";
10548 }
10549 }
10550 }
10551 @{$CompatProblems{$Interface}{$NewProblemType}{"retval"}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}};
10552 }
10553 if($ReturnType1_Id and $ReturnType2_Id)
10554 {
10555 @RecurTypes = ();
10556 %SubProblems = mergeTypes($ReturnType1_Id, $Tid_TDid{1}{$ReturnType1_Id},
10557 $ReturnType2_Id, $Tid_TDid{2}{$ReturnType2_Id});
10558 foreach my $SubProblemType (keys(%SubProblems))
10559 { # add "Global_Data_Size" problem
10560 my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
10561 my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
10562 if($SubProblemType eq "DataType_Size"
10563 and $CompleteSignature{1}{$Interface}{"Data"}
10564 and get_PointerLevel($Tid_TDid{1}{$ReturnType1_Id}, $ReturnType1_Id, 1)==0)
10565 { # add a new problem
10566 %{$SubProblems{"Global_Data_Size"}} = %{$SubProblems{$SubProblemType}};
10567 }
10568 }
10569 foreach my $SubProblemType (keys(%SubProblems))
10570 {
10571 foreach my $SubLocation (keys(%{$SubProblems{$SubProblemType}}))
10572 {
10573 my $NewLocation = ($SubLocation)?"retval->".$SubLocation:"retval";
10574 %{$CompatProblems{$Interface}{$SubProblemType}{$NewLocation}}=(
10575 "Return_Type_Name"=>get_TypeName($ReturnType1_Id, 1) );
10576 @{$CompatProblems{$Interface}{$SubProblemType}{$NewLocation}}{keys(%{$SubProblems{$SubProblemType}{$SubLocation}})} = values %{$SubProblems{$SubProblemType}{$SubLocation}};
10577 if($SubLocation!~/\-\>/) {
10578 $CompatProblems{$Interface}{$SubProblemType}{$NewLocation}{"Start_Type_Name"} = get_TypeName($ReturnType1_Id, 1);
10579 }
10580 }
10581 }
10582 }
10583
10584 # checking object type
10585 my $ObjectType1_Id = $CompleteSignature{1}{$Interface}{"Class"};
10586 my $ObjectType2_Id = $CompleteSignature{2}{$Interface}{"Class"};
10587 if($ObjectType1_Id and $ObjectType2_Id
10588 and not $CompleteSignature{1}{$Interface}{"Static"})
10589 {
10590 my $ThisPtr1_Id = getTypeIdByName(get_TypeName($ObjectType1_Id, 1)."*const", 1);
10591 my $ThisPtr2_Id = getTypeIdByName(get_TypeName($ObjectType2_Id, 2)."*const", 2);
10592 if($ThisPtr1_Id and $ThisPtr2_Id)
10593 {
10594 @RecurTypes = ();
10595 %SubProblems = mergeTypes($ThisPtr1_Id, $Tid_TDid{1}{$ThisPtr1_Id},
10596 $ThisPtr2_Id, $Tid_TDid{2}{$ThisPtr2_Id});
10597 foreach my $SubProblemType (keys(%SubProblems))
10598 {
10599 foreach my $SubLocation (keys(%{$SubProblems{$SubProblemType}}))
10600 {
10601 my $NewLocation = ($SubLocation)?"this->".$SubLocation:"this";
10602 %{$CompatProblems{$Interface}{$SubProblemType}{$NewLocation}}=(
10603 "Object_Type_Name"=>get_TypeName($ObjectType1_Id, 1) );
10604 @{$CompatProblems{$Interface}{$SubProblemType}{$NewLocation}}{keys(%{$SubProblems{$SubProblemType}{$SubLocation}})} = values %{$SubProblems{$SubProblemType}{$SubLocation}};
10605 if($SubLocation!~/\-\>/) {
10606 $CompatProblems{$Interface}{$SubProblemType}{$NewLocation}{"Start_Type_Name"} = get_TypeName($ObjectType1_Id, 1);
10607 }
10608 }
10609 }
10610 }
10611 }
10612 }
10613 mergeVTables();
10614}
10615
10616sub removedConstness($$)
10617{
10618 my ($Old_Value, $New_Value) = @_;
10619 if($Old_Value eq $New_Value) {
10620 return 0;
10621 }
10622 while($Old_Value=~s/(\A|\W)const(\W|\Z)/$1$2/)
10623 { # remove all "const" qualifiers
10624 # one-by-one, left-to-right
10625 $Old_Value=~s/\s+\Z//g;
10626 $Old_Value=~s/\A\s+//g;
10627 $Old_Value = formatName($Old_Value);
10628 if($Old_Value eq $New_Value)
10629 { # compare with a new type
10630 return 1;
10631 }
10632 }
10633 return 0;
10634}
10635
10636sub mergeParameters($$$)
10637{
10638 my ($Interface, $ParamPos1, $ParamPos2) = @_;
10639 return if(not $Interface);
10640 return if(not defined $CompleteSignature{1}{$Interface}{"Param"});
10641 return if(not defined $CompleteSignature{2}{$Interface}{"Param"});
10642 my $ParamType1_Id = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos1}{"type"};
10643 my $ParamName1 = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos1}{"name"};
10644 my $ParamType2_Id = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos2}{"type"};
10645 my $ParamName2 = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos2}{"name"};
10646 return if(not $ParamType1_Id or not $ParamType2_Id);
10647 my %Type1 = get_Type($Tid_TDid{1}{$ParamType1_Id}, $ParamType1_Id, 1);
10648 my %Type2 = get_Type($Tid_TDid{2}{$ParamType2_Id}, $ParamType2_Id, 2);
10649 my %BaseType1 = get_BaseType($Tid_TDid{1}{$ParamType1_Id}, $ParamType1_Id, 1);
10650 my %BaseType2 = get_BaseType($Tid_TDid{2}{$ParamType2_Id}, $ParamType2_Id, 2);
10651 my $Parameter_Location = ($ParamName1)?$ParamName1:numToStr($ParamPos1+1)." Parameter";
10652 if((not $UsedDump{1}{"V"} or cmpVersions($UsedDump{1}{"V"}, "2.6.1")>=0)
10653 and (not $UsedDump{2}{"V"} or cmpVersions($UsedDump{2}{"V"}, "2.6.1")>=0))
10654 { # "reg" attribute added in ACC 1.95.1 (dump 2.6.1 format)
10655 if($CompleteSignature{1}{$Interface}{"Param"}{$ParamPos1}{"reg"}
10656 and not $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos2}{"reg"})
10657 {
10658 %{$CompatProblems{$Interface}{"Parameter_Became_Non_Register"}{$Parameter_Location}}=(
10659 "Target"=>$ParamName1,
10660 "Param_Pos"=>$ParamPos1 );
10661 }
10662 elsif(not $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos1}{"reg"}
10663 and $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos2}{"reg"})
10664 {
10665 %{$CompatProblems{$Interface}{"Parameter_Became_Register"}{$Parameter_Location}}=(
10666 "Target"=>$ParamName1,
10667 "Param_Pos"=>$ParamPos1 );
10668 }
10669 }
10670 if((not $UsedDump{1}{"V"} or cmpVersions($UsedDump{1}{"V"}, "2.0")>=0)
10671 and (not $UsedDump{2}{"V"} or cmpVersions($UsedDump{2}{"V"}, "2.0")>=0))
10672 { # "default" attribute added in ACC 1.22 (dump 2.0 format)
10673 my $DefaultValue_Old = $CompleteSignature{1}{$Interface}{"Param"}{$ParamPos1}{"default"};
10674 my $DefaultValue_New = $CompleteSignature{2}{$Interface}{"Param"}{$ParamPos2}{"default"};
10675 my %PureType1 = get_PureType($Tid_TDid{1}{$ParamType1_Id}, $ParamType1_Id, 1);
10676 if($PureType1{"Name"}=~/\A(char\*|char const\*)\Z/)
10677 {
10678 if($DefaultValue_Old)
10679 { # FIXME: how to distinguish "0" and 0 (NULL)
10680 $DefaultValue_Old = "\"$DefaultValue_Old\"";
10681 }
10682 if($DefaultValue_New) {
10683 $DefaultValue_New = "\"$DefaultValue_New\"";
10684 }
10685 }
10686 elsif($PureType1{"Name"}=~/\A(char)\Z/)
10687 {
10688 if($DefaultValue_Old) {
10689 $DefaultValue_Old = "\'$DefaultValue_Old\'";
10690 }
10691 if($DefaultValue_New) {
10692 $DefaultValue_New = "\'$DefaultValue_New\'";
10693 }
10694 }
10695 if($DefaultValue_Old ne "")
10696 {
10697 if($DefaultValue_New ne "")
10698 {
10699 if($DefaultValue_Old ne $DefaultValue_New)
10700 {
10701 %{$CompatProblems{$Interface}{"Parameter_Default_Value_Changed"}{$Parameter_Location}}=(
10702 "Target"=>$ParamName1,
10703 "Param_Pos"=>$ParamPos1,
10704 "Old_Value"=>$DefaultValue_Old,
10705 "New_Value"=>$DefaultValue_New );
10706 }
10707 }
10708 else
10709 {
10710 %{$CompatProblems{$Interface}{"Parameter_Default_Value_Removed"}{$Parameter_Location}}=(
10711 "Target"=>$ParamName1,
10712 "Param_Pos"=>$ParamPos1,
10713 "Old_Value"=>$DefaultValue_Old );
10714 }
10715 }
10716 }
10717 if($ParamName1 ne $ParamName2
10718 and $ParamType1_Id!=-1 and $ParamType2_Id!=-1
10719 and $ParamName1!~/\Ap\d+\Z/ and $ParamName2!~/\Ap\d+\Z/)
10720 { # except unnamed "..." value list (Id=-1)
10721 %{$CompatProblems{$Interface}{"Renamed_Parameter"}{numToStr($ParamPos1+1)." Parameter"}}=(
10722 "Target"=>$ParamName1,
10723 "Param_Pos"=>$ParamPos1,
10724 "Param_Type"=>get_TypeName($ParamType1_Id, 1),
10725 "Old_Value"=>$ParamName1,
10726 "New_Value"=>$ParamName2,
10727 "New_Signature"=>get_Signature($Interface, 2) );
10728 }
10729 # checking type change (replace)
10730 my %SubProblems = detectTypeChange($ParamType1_Id, $ParamType2_Id, "Parameter");
10731 foreach my $SubProblemType (keys(%SubProblems))
10732 { # add new problems, remove false alarms
10733 my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
10734 my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
10735 if($SubProblemType eq "Parameter_Type")
10736 {
10737 if((not $UsedDump{1}{"V"} or cmpVersions($UsedDump{1}{"V"}, "2.6")>=0)
10738 and (not $UsedDump{2}{"V"} or cmpVersions($UsedDump{2}{"V"}, "2.6")>=0))
10739 {
10740 if($Old_Value!~/(\A|\W)restrict(\W|\Z)/
10741 and $New_Value=~/(\A|\W)restrict(\W|\Z)/)
10742 { # change to be "restrict"
10743 %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}};
10744 }
10745 }
10746 if($Type2{"Type"} eq "Const" and $BaseType2{"Name"} eq $Type1{"Name"}
10747 and $Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
10748 { # int to "int const"
10749 delete($SubProblems{$SubProblemType});
10750 }
10751 if($Type1{"Type"} eq "Const" and $BaseType1{"Name"} eq $Type2{"Name"}
10752 and $Type2{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
10753 { # "int const" to int
10754 delete($SubProblems{$SubProblemType});
10755 }
10756 }
10757 }
10758 foreach my $SubProblemType (keys(%SubProblems))
10759 { # modify/register problems
10760 my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
10761 my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
10762 my $NewProblemType = $SubProblemType;
10763 if($Old_Value eq "..." and $New_Value ne "...")
10764 { # change from "..." to "int"
10765 if($ParamPos1==0)
10766 { # ISO C requires a named argument before "..."
10767 next;
10768 }
10769 $NewProblemType = "Parameter_Became_NonVaList";
10770 }
10771 elsif($New_Value eq "..." and $Old_Value ne "...")
10772 { # change from "int" to "..."
10773 if($ParamPos2==0)
10774 { # ISO C requires a named argument before "..."
10775 next;
10776 }
10777 $NewProblemType = "Parameter_Became_VaList";
10778 }
10779 elsif($SubProblemType eq "Parameter_Type"
10780 and removedConstness($Old_Value, $New_Value))
10781 { # parameter: "const" to non-"const"
10782 $NewProblemType = "Parameter_Became_Non_Const";
10783 }
10784 elsif($SubProblemType eq "Parameter_Type_And_Size"
10785 or $SubProblemType eq "Parameter_Type")
10786 {
10787 my ($Arch1, $Arch2) = (getArch(1), getArch(2));
10788 if($Arch1 eq "unknown" or $Arch2 eq "unknown")
10789 { # if one of the architectures is unknown
10790 # then set other arhitecture to unknown too
10791 ($Arch1, $Arch2) = ("unknown", "unknown");
10792 }
10793 my ($Method1, $Passed1, $SizeOnStack1, $RegName1) = callingConvention($Interface, $ParamPos1, 1, $Arch1);
10794 my ($Method2, $Passed2, $SizeOnStack2, $RegName2) = callingConvention($Interface, $ParamPos2, 2, $Arch2);
10795 if($Method1 eq $Method2)
10796 {
10797 if($Method1 eq "stack" and $SizeOnStack1 ne $SizeOnStack2) {
10798 $NewProblemType = "Parameter_Type_And_Stack";
10799 }
10800 elsif($Method1 eq "register" and $RegName1 ne $RegName2) {
10801 $NewProblemType = "Parameter_Type_And_Register";
10802 }
10803 }
10804 else
10805 {
10806 if($Method1 eq "stack") {
10807 $NewProblemType = "Parameter_Type_And_Pass_Through_Register";
10808 }
10809 elsif($Method1 eq "register") {
10810 $NewProblemType = "Parameter_Type_And_Pass_Through_Stack";
10811 }
10812 }
10813 $SubProblems{$SubProblemType}{"Old_Reg"} = $RegName1;
10814 $SubProblems{$SubProblemType}{"New_Reg"} = $RegName2;
10815 }
10816 %{$CompatProblems{$Interface}{$NewProblemType}{$Parameter_Location}}=(
10817 "Target"=>$ParamName1,
10818 "Param_Pos"=>$ParamPos1,
10819 "New_Signature"=>get_Signature($Interface, 2) );
10820 @{$CompatProblems{$Interface}{$NewProblemType}{$Parameter_Location}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}};
10821 }
10822 @RecurTypes = ();
10823 # checking type definition changes
10824 my %SubProblems_Merge = mergeTypes($ParamType1_Id, $Tid_TDid{1}{$ParamType1_Id}, $ParamType2_Id, $Tid_TDid{2}{$ParamType2_Id});
10825 foreach my $SubProblemType (keys(%SubProblems_Merge))
10826 {
10827 foreach my $SubLocation (keys(%{$SubProblems_Merge{$SubProblemType}}))
10828 {
10829 my $NewProblemType = $SubProblemType;
10830 if($SubProblemType eq "DataType_Size")
10831 {
10832 my $InitialType_Type = $SubProblems_Merge{$SubProblemType}{$SubLocation}{"InitialType_Type"};
10833 if($InitialType_Type!~/\A(Pointer|Ref)\Z/ and $SubLocation!~/\-\>/)
10834 { # stack has been affected
10835 $NewProblemType = "DataType_Size_And_Stack";
10836 }
10837 }
10838 my $NewLocation = ($SubLocation)?$Parameter_Location."->".$SubLocation:$Parameter_Location;
10839 %{$CompatProblems{$Interface}{$NewProblemType}{$NewLocation}}=(
10840 "Param_Type"=>get_TypeName($ParamType1_Id, 1),
10841 "Param_Pos"=>$ParamPos1,
10842 "Param_Name"=>$ParamName1 );
10843 @{$CompatProblems{$Interface}{$NewProblemType}{$NewLocation}}{keys(%{$SubProblems_Merge{$SubProblemType}{$SubLocation}})} = values %{$SubProblems_Merge{$SubProblemType}{$SubLocation}};
10844 if($SubLocation!~/\-\>/) {
10845 $CompatProblems{$Interface}{$NewProblemType}{$NewLocation}{"Start_Type_Name"} = get_TypeName($ParamType1_Id, 1);
10846 }
10847 }
10848 }
10849}
10850
10851sub callingConvention($$$$)
10852{ # calling conventions for different compilers and operating systems
10853 my ($Interface, $ParamPos, $LibVersion, $Arch) = @_;
10854 my $ParamTypeId = $CompleteSignature{$LibVersion}{$Interface}{"Param"}{$ParamPos}{"type"};
10855 my %Type = get_PureType($Tid_TDid{$LibVersion}{$ParamTypeId}, $ParamTypeId, $LibVersion);
10856 my ($Method, $Alignment, $Passed, $Register) = ("", 0, "", "");
10857 if($OSgroup=~/\A(linux|macos|freebsd)\Z/)
10858 {# GCC
10859 if($Arch eq "x86")
10860 { # System V ABI Intel386 ("Function Calling Sequence")
10861 # The stack is word aligned. Although the architecture does not require any
10862 # alignment of the stack, software convention and the operating system
10863 # requires that the stack be aligned on a word boundary.
10864
10865 # Argument words are pushed onto the stack in reverse order (that is, the
10866 # rightmost argument in C call syntax has the highest address), preserving the
10867 # stack’s word alignment. All incoming arguments appear on the stack, residing
10868 # in the stack frame of the caller.
10869
10870 # An argument’s size is increased, if necessary, to make it a multiple of words.
10871 # This may require tail padding, depending on the size of the argument.
10872
10873 # Other areas depend on the compiler and the code being compiled. The stan-
10874 # dard calling sequence does not define a maximum stack frame size, nor does
10875 # it restrict how a language system uses the ‘‘unspecified’’ area of the stan-
10876 # dard stack frame.
10877 ($Method, $Alignment) = ("stack", 4);
10878 }
10879 elsif($Arch eq "x86_64")
10880 { # System V AMD64 ABI ("Function Calling Sequence")
10881 ($Method, $Alignment) = ("stack", 8);# eightbyte aligned
10882 }
10883 elsif($Arch eq "arm")
10884 { # Procedure Call Standard for the ARM Architecture
10885 # The stack must be double-word aligned
10886 ($Method, $Alignment) = ("stack", 8);# double-word
10887 }
10888 }
10889 elsif($OSgroup eq "windows")
10890 {# MS C++ Compiler
10891 if($Arch eq "x86")
10892 {
10893 if($ParamPos==0) {
10894 ($Method, $Register, $Passed) = ("register", "ecx", "value");
10895 }
10896 elsif($ParamPos==1) {
10897 ($Method, $Register, $Passed) = ("register", "edx", "value");
10898 }
10899 else {
10900 ($Method, $Alignment) = ("stack", 4);
10901 }
10902 }
10903 elsif($Arch eq "x86_64")
10904 {
10905 if($ParamPos<=3)
10906 {
10907 if($Type{"Name"}=~/\A(float|double|long double)\Z/) {
10908 ($Method, $Passed) = ("xmm".$ParamPos, "value");
10909 }
10910 elsif($Type{"Name"}=~/\A(unsigned |)(short|int|long|long long)\Z/
10911 or $Type{"Type"}=~/\A(Struct|Union|Enum|Array)\Z/
10912 or $Type{"Name"}=~/\A(__m64|__m128)\Z/)
10913 {
10914 if($ParamPos==0) {
10915 ($Method, $Register, $Passed) = ("register", "rcx", "value");
10916 }
10917 elsif($ParamPos==1) {
10918 ($Method, $Register, $Passed) = ("register", "rdx", "value");
10919 }
10920 elsif($ParamPos==2) {
10921 ($Method, $Register, $Passed) = ("register", "r8", "value");
10922 }
10923 elsif($ParamPos==3) {
10924 ($Method, $Register, $Passed) = ("register", "r9", "value");
10925 }
10926 if($Type{"Size"}>64
10927 or $Type{"Type"} eq "Array") {
10928 $Passed = "pointer";
10929 }
10930 }
10931 }
10932 else {
10933 ($Method, $Alignment) = ("stack", 8);# word alignment
10934 }
10935 }
10936 }
10937 if($Method eq "register") {
10938 return ("register", $Passed, "", $Register);
10939 }
10940 else
10941 {# on the stack
10942 if(not $Alignment)
10943 {# default convention
10944 $Alignment = $WORD_SIZE{$LibVersion};
10945 }
10946 if(not $Passed)
10947 {# default convention
10948 $Passed = "value";
10949 }
10950 my $SizeOnStack = $Type{"Size"};
10951 # FIXME: improve stack alignment
10952 if($SizeOnStack!=$Alignment) {
10953 $SizeOnStack = int(($Type{"Size"}+$Alignment)/$Alignment)*$Alignment;
10954 }
10955 return ("stack", $Passed, $SizeOnStack, "");
10956 }
10957}
10958
10959sub find_ParamPair_Pos_byName($$$)
10960{
10961 my ($Name, $Interface, $LibVersion) = @_;
10962 foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Interface}{"Param"}}))
10963 {
10964 next if(not defined $CompleteSignature{$LibVersion}{$Interface}{"Param"}{$ParamPos});
10965 if($CompleteSignature{$LibVersion}{$Interface}{"Param"}{$ParamPos}{"name"} eq $Name)
10966 {
10967 return $ParamPos;
10968 }
10969 }
10970 return "lost";
10971}
10972
10973sub find_ParamPair_Pos_byTypeAndPos($$$$$)
10974{
10975 my ($TypeName, $MediumPos, $Order, $Interface, $LibVersion) = @_;
10976 my @Positions = ();
10977 foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Interface}{"Param"}}))
10978 {
10979 next if($Order eq "backward" and $ParamPos>$MediumPos);
10980 next if($Order eq "forward" and $ParamPos<$MediumPos);
10981 next if(not defined $CompleteSignature{$LibVersion}{$Interface}{"Param"}{$ParamPos});
10982 my $PTypeId = $CompleteSignature{$LibVersion}{$Interface}{"Param"}{$ParamPos}{"type"};
10983 if(get_TypeName($PTypeId, $LibVersion) eq $TypeName) {
10984 push(@Positions, $ParamPos);
10985 }
10986 }
10987 return @Positions;
10988}
10989
10990sub getTypeIdByName($$)
10991{
10992 my ($TypeName, $Version) = @_;
10993 return $TName_Tid{$Version}{formatName($TypeName)};
10994}
10995
10996sub checkFormatChange($$)
10997{
10998 my ($Type1_Id, $Type2_Id) = @_;
10999 my $Type1_DId = $Tid_TDid{1}{$Type1_Id};
11000 my $Type2_DId = $Tid_TDid{2}{$Type2_Id};
11001 my %Type1_Pure = get_PureType($Type1_DId, $Type1_Id, 1);
11002 my %Type2_Pure = get_PureType($Type2_DId, $Type2_Id, 2);
11003 if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
11004 { # equal types
11005 return 0;
11006 }
11007 if($Type1_Pure{"Name"}=~/\*/
11008 or $Type2_Pure{"Name"}=~/\*/)
11009 { # compared in detectTypeChange()
11010 return 0;
11011 }
11012 my %FloatType = map {$_=>1} (
11013 "float",
11014 "double",
11015 "long double"
11016 );
11017 if($Type1_Pure{"Type"} ne $Type2_Pure{"Type"})
11018 { # different types
11019 if($Type1_Pure{"Type"} eq "Intrinsic"
11020 and $Type2_Pure{"Type"} eq "Enum")
11021 { # "int" to "enum"
11022 return 0;
11023 }
11024 elsif($Type2_Pure{"Type"} eq "Intrinsic"
11025 and $Type1_Pure{"Type"} eq "Enum")
11026 { # "enum" to "int"
11027 return 0;
11028 }
11029 else
11030 { # "union" to "struct"
11031 # ...
11032 return 1;
11033 }
11034 }
11035 else
11036 {
11037 if($Type1_Pure{"Type"} eq "Intrinsic")
11038 {
11039 if($FloatType{$Type1_Pure{"Name"}}
11040 or $FloatType{$Type2_Pure{"Name"}})
11041 { # "float" to "double"
11042 # "float" to "int"
11043 return 1;
11044 }
11045 }
11046 elsif($Type1_Pure{"Type"}=~/Class|Struct|Union|Enum/)
11047 {
11048 my @Membs1 = keys(%{$Type1_Pure{"Memb"}});
11049 my @Membs2 = keys(%{$Type2_Pure{"Memb"}});
11050 if($#Membs1!=$#Membs2)
11051 { # different number of elements
11052 return 1;
11053 }
11054 if($Type1_Pure{"Type"} eq "Enum")
11055 {
11056 foreach my $Pos (@Membs1)
11057 { # compare elements by name and value
11058 if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"}
11059 or $Type1_Pure{"Memb"}{$Pos}{"value"} ne $Type2_Pure{"Memb"}{$Pos}{"value"})
11060 { # different names
11061 return 1;
11062 }
11063 }
11064 }
11065 else
11066 {
11067 foreach my $Pos (@Membs1)
11068 { # compare elements by type name
11069 my $MT1 = get_TypeName($Type1_Pure{"Memb"}{$Pos}{"type"}, 1);
11070 my $MT2 = get_TypeName($Type2_Pure{"Memb"}{$Pos}{"type"}, 2);
11071 if($MT1 ne $MT2)
11072 { # different types
11073 return 1;
11074 }
11075 }
11076 }
11077 }
11078 }
11079 return 0;
11080}
11081
11082sub isScalar($) {
11083 return ($_[0]=~/\A(unsigned |)(short|int|long|long long)\Z/);
11084}
11085
11086sub isFloat($) {
11087 return ($_[0]=~/\A(float|double|long double)\Z/);
11088}
11089
11090sub detectTypeChange($$$)
11091{
11092 my ($Type1_Id, $Type2_Id, $Prefix) = @_;
11093 my %LocalProblems = ();
11094 my $Type1_DId = $Tid_TDid{1}{$Type1_Id};
11095 my $Type2_DId = $Tid_TDid{2}{$Type2_Id};
11096 my %Type1 = get_Type($Type1_DId, $Type1_Id, 1);
11097 my %Type2 = get_Type($Type2_DId, $Type2_Id, 2);
11098 my %Type1_Pure = get_PureType($Type1_DId, $Type1_Id, 1);
11099 my %Type2_Pure = get_PureType($Type2_DId, $Type2_Id, 2);
11100 my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type1_Pure{"TDid"}, $Type1_Pure{"Tid"}, 1):get_BaseType($Type1_DId, $Type1_Id, 1);
11101 my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type2_Pure{"TDid"}, $Type2_Pure{"Tid"}, 2):get_BaseType($Type2_DId, $Type2_Id, 2);
11102 my $Type1_PLevel = get_PointerLevel($Type1_DId, $Type1_Id, 1);
11103 my $Type2_PLevel = get_PointerLevel($Type2_DId, $Type2_Id, 2);
11104 return () if(not $Type1{"Name"} or not $Type2{"Name"});
11105 return () if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"});
11106 return () if($Type1_PLevel eq "" or $Type2_PLevel eq "");
11107 if($Type1_Base{"Name"} ne $Type2_Base{"Name"}
11108 and ($Type1{"Name"} eq $Type2{"Name"} or ($Type1_PLevel>=1 and $Type1_PLevel==$Type2_PLevel
11109 and $Type1_Base{"Name"} ne "void" and $Type2_Base{"Name"} ne "void")))
11110 { # base type change
11111 if($Type1{"Type"} eq "Typedef" and $Type2{"Type"} eq "Typedef"
11112 and $Type1{"Name"} eq $Type2{"Name"})
11113 { # will be reported in mergeTypes() as typedef problem
11114 return ();
11115 }
11116 if($Type1_Base{"Name"}!~/anon\-/ and $Type2_Base{"Name"}!~/anon\-/)
11117 {
11118 if($Type1_Base{"Size"} ne $Type2_Base{"Size"}
11119 and $Type1_Base{"Size"} and $Type2_Base{"Size"})
11120 {
11121 %{$LocalProblems{$Prefix."_BaseType_And_Size"}}=(
11122 "Old_Value"=>$Type1_Base{"Name"},
11123 "New_Value"=>$Type2_Base{"Name"},
11124 "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
11125 "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE,
11126 "InitialType_Type"=>$Type1_Pure{"Type"});
11127 }
11128 else
11129 {
11130 if(checkFormatChange($Type1_Base{"Tid"}, $Type2_Base{"Tid"}))
11131 { # format change
11132 %{$LocalProblems{$Prefix."_BaseType_Format"}}=(
11133 "Old_Value"=>$Type1_Base{"Name"},
11134 "New_Value"=>$Type2_Base{"Name"},
11135 "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
11136 "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE,
11137 "InitialType_Type"=>$Type1_Pure{"Type"});
11138 }
11139 elsif(tNameLock($Type1_Base{"Tid"}, $Type2_Base{"Tid"}))
11140 {
11141 %{$LocalProblems{$Prefix."_BaseType"}}=(
11142 "Old_Value"=>$Type1_Base{"Name"},
11143 "New_Value"=>$Type2_Base{"Name"},
11144 "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
11145 "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE,
11146 "InitialType_Type"=>$Type1_Pure{"Type"});
11147 }
11148 }
11149 }
11150 }
11151 elsif($Type1{"Name"} ne $Type2{"Name"})
11152 { # type change
11153 if($Type1{"Name"}!~/anon\-/ and $Type2{"Name"}!~/anon\-/)
11154 {
11155 if($Prefix eq "Return" and $Type1{"Name"} eq "void"
11156 and $Type2_Pure{"Type"}=~/Intrinsic|Enum/) {
11157 # safe change
11158 }
11159 elsif($Prefix eq "Return"
11160 and $Type1_Pure{"Name"} eq "void")
11161 {
11162 %{$LocalProblems{"Return_Type_From_Void"}}=(
11163 "New_Value"=>$Type2{"Name"},
11164 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE,
11165 "InitialType_Type"=>$Type1_Pure{"Type"});
11166 }
11167 elsif($Prefix eq "Return" and $Type1_Pure{"Type"}=~/Intrinsic|Enum/
11168 and $Type2_Pure{"Type"}=~/Struct|Class|Union/)
11169 { # returns into hidden first parameter instead of a register
11170
11171 # System V ABI Intel386 ("Function Calling Sequence")
11172 # A function that returns an integral or pointer value places its result in register %eax.
11173
11174 # A floating-point return value appears on the top of the Intel387 register stack. The
11175 # caller then must remove the value from the Intel387 stack, even if it doesn’t use the
11176 # value.
11177
11178 # If a function returns a structure or union, then the caller provides space for the
11179 # return value and places its address on the stack as argument word zero. In effect,
11180 # this address becomes a ‘‘hidden’’ first argument.
11181
11182 %{$LocalProblems{"Return_Type_From_Register_To_Stack"}}=(
11183 "Old_Value"=>$Type1{"Name"},
11184 "New_Value"=>$Type2{"Name"},
11185 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11186 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE,
11187 "InitialType_Type"=>$Type1_Pure{"Type"});
11188 }
11189 elsif($Prefix eq "Return"
11190 and $Type2_Pure{"Name"} eq "void")
11191 {
11192 %{$LocalProblems{"Return_Type_Became_Void"}}=(
11193 "Old_Value"=>$Type1{"Name"},
11194 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11195 "InitialType_Type"=>$Type1_Pure{"Type"});
11196 }
11197 elsif($Prefix eq "Return"
11198 and ((isScalar($Type1_Pure{"Name"}) and isFloat($Type2_Pure{"Name"}))
11199 or (isScalar($Type2_Pure{"Name"}) and isFloat($Type1_Pure{"Name"}))))
11200 { # The scalar and floating-point values are passed in different registers
11201 %{$LocalProblems{"Return_Type_And_Register"}}=(
11202 "Old_Value"=>$Type1{"Name"},
11203 "New_Value"=>$Type2{"Name"},
11204 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11205 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE,
11206 "InitialType_Type"=>$Type1_Pure{"Type"});
11207 }
11208 elsif($Prefix eq "Return" and $Type2_Pure{"Type"}=~/Intrinsic|Enum/
11209 and $Type1_Pure{"Type"}=~/Struct|Class|Union/)
11210 { # returns in a register instead of a hidden first parameter
11211 %{$LocalProblems{"Return_Type_From_Stack_To_Register"}}=(
11212 "Old_Value"=>$Type1{"Name"},
11213 "New_Value"=>$Type2{"Name"},
11214 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11215 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE,
11216 "InitialType_Type"=>$Type1_Pure{"Type"});
11217 }
11218 else
11219 {
11220 if($Type1{"Size"} ne $Type2{"Size"}
11221 and $Type1{"Size"} and $Type2{"Size"})
11222 {
11223 %{$LocalProblems{$Prefix."_Type_And_Size"}}=(
11224 "Old_Value"=>$Type1{"Name"},
11225 "New_Value"=>$Type2{"Name"},
11226 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11227 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE,
11228 "InitialType_Type"=>$Type1_Pure{"Type"});
11229 }
11230 else
11231 {
11232 if(checkFormatChange($Type1_Id, $Type2_Id))
11233 { # format change
11234 %{$LocalProblems{$Prefix."_Type_Format"}}=(
11235 "Old_Value"=>$Type1{"Name"},
11236 "New_Value"=>$Type2{"Name"},
11237 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11238 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE,
11239 "InitialType_Type"=>$Type1_Pure{"Type"});
11240 }
11241 elsif(tNameLock($Type1_Id, $Type2_Id))
11242 { # FIXME: correct this condition
11243 %{$LocalProblems{$Prefix."_Type"}}=(
11244 "Old_Value"=>$Type1{"Name"},
11245 "New_Value"=>$Type2{"Name"},
11246 "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11247 "New_Size"=>$Type2{"Size"}*$BYTE_SIZE,
11248 "InitialType_Type"=>$Type1_Pure{"Type"});
11249 }
11250 }
11251 }
11252 }
11253 }
11254 if($Type1_PLevel!=$Type2_PLevel)
11255 {
11256 if($Type1{"Name"} ne "void" and $Type1{"Name"} ne "..."
11257 and $Type2{"Name"} ne "void" and $Type2{"Name"} ne "...")
11258 {
11259 if($Type2_PLevel>$Type1_PLevel) {
11260 %{$LocalProblems{$Prefix."_PointerLevel_Increased"}}=(
11261 "Old_Value"=>$Type1_PLevel,
11262 "New_Value"=>$Type2_PLevel);
11263 }
11264 else {
11265 %{$LocalProblems{$Prefix."_PointerLevel_Decreased"}}=(
11266 "Old_Value"=>$Type1_PLevel,
11267 "New_Value"=>$Type2_PLevel);
11268 }
11269 }
11270 }
11271 if($Type1_Pure{"Type"} eq "Array")
11272 { # base_type[N] -> base_type[N]
11273 # base_type: older_structure -> typedef to newer_structure
11274 my %SubProblems = detectTypeChange($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Prefix);
11275 foreach my $SubProblemType (keys(%SubProblems))
11276 {
11277 $SubProblemType=~s/_Type/_BaseType/g;
11278 next if(defined $LocalProblems{$SubProblemType});
11279 foreach my $Attr (keys(%{$SubProblems{$SubProblemType}})) {
11280 $LocalProblems{$SubProblemType}{$Attr} = $SubProblems{$SubProblemType}{$Attr};
11281 }
11282 }
11283 }
11284 return %LocalProblems;
11285}
11286
11287sub tNameLock($$)
11288{
11289 my ($Tid1, $Tid2) = @_;
11290 if(differentFmts())
11291 { # different formats
11292 if($UseOldDumps)
11293 { # old dumps
11294 return 0;
11295 }
11296 my $TN1 = get_TypeName($Tid1, 1);
11297 my $TN2 = get_TypeName($Tid2, 2);
11298 my %Base1 = get_Type($Tid_TDid{1}{$Tid1}, $Tid1, 1);
11299 while($Base1{"Type"} eq "Typedef") {
11300 %Base1 = get_OneStep_BaseType($Base1{"TDid"}, $Base1{"Tid"}, 1);
11301 }
11302 my %Base2 = get_Type($Tid_TDid{2}{$Tid2}, $Tid2, 2);
11303 while($Base2{"Type"} eq "Typedef") {
11304 %Base2 = get_OneStep_BaseType($Base2{"TDid"}, $Base2{"Tid"}, 2);
11305 }
11306 my $Base1 = uncover_typedefs($Base1{"Name"}, 1);
11307 my $Base2 = uncover_typedefs($Base2{"Name"}, 2);
11308 if($TN1 ne $TN2
11309 and $Base1 eq $Base2)
11310 { # equal base types
11311 return 0;
11312 }
11313 if(($UsedDump{1}{"V"} and cmpVersions($UsedDump{1}{"V"}, "2.6")<0)
11314 or ($UsedDump{2}{"V"} and cmpVersions($UsedDump{2}{"V"}, "2.6")<0))
11315 {
11316 if($TN1!~/(\A|\W)restrict(\W|\Z)/
11317 and $TN2=~/(\A|\W)restrict(\W|\Z)/) {
11318 return 0;
11319 }
11320 }
11321
11322 }
11323 return 1;
11324}
11325
11326sub differentFmts()
11327{
11328 if(getGccVersion(1) ne getGccVersion(2))
11329 { # different GCC versions
11330 return 1;
11331 }
11332 if(cmpVersions(formatVersion($UsedDump{1}{"V"}, 2),
11333 formatVersion($UsedDump{2}{"V"}, 2))!=0)
11334 { # different dump versions (skip micro version)
11335 return 1;
11336 }
11337 return 0;
11338}
11339
11340sub formatVersion($$)
11341{ # cut off version digits
11342 my ($Version, $Digits) = @_;
11343 my @Elems = split(/\./, $Version);
11344 return join(".", splice(@Elems, 0, $Digits));
11345}
11346
11347sub htmlSpecChars($)
11348{
11349 my $Str = $_[0];
11350 $Str=~s/\&([^#]|\Z)/&amp;$1/g;
11351 $Str=~s/</&lt;/g;
11352 $Str=~s/\-\>/&#45;&gt;/g; # &minus;
11353 $Str=~s/>/&gt;/g;
11354 $Str=~s/([^ ])( )([^ ])/$1\@ALONE_SP\@$3/g;
11355 $Str=~s/ /&#160;/g; # &nbsp;
11356 $Str=~s/\@ALONE_SP\@/ /g;
11357 $Str=~s/\n/<br\/>/g;
11358 $Str=~s/\"/&quot;/g;
11359 $Str=~s/\'/&#39;/g;
11360 return $Str;
11361}
11362
11363sub black_name($)
11364{
11365 my $Name = $_[0];
11366 return "<span class='iname_b'>".highLight_Signature($Name)."</span>";
11367}
11368
11369sub highLight_Signature($)
11370{
11371 my $Signature = $_[0];
11372 return highLight_Signature_PPos_Italic($Signature, "", 0, 0, 0);
11373}
11374
11375sub highLight_Signature_Italic_Color($)
11376{
11377 my $Signature = $_[0];
11378 return highLight_Signature_PPos_Italic($Signature, "", 1, 1, 1);
11379}
11380
11381sub separate_symbol($)
11382{
11383 my $Symbol = $_[0];
11384 my ($Name, $Spec, $Ver) = ($Symbol, "", "");
11385 if($Symbol=~/\A([^\@\$\?]+)([\@\$]+)([^\@\$]+)\Z/) {
11386 ($Name, $Spec, $Ver) = ($1, $2, $3);
11387 }
11388 return ($Name, $Spec, $Ver);
11389}
11390
11391sub cut_f_attrs($)
11392{
11393 if($_[0]=~s/(\))((| (const volatile|const|volatile))(| \[static\]))\Z/$1/) {
11394 return $2;
11395 }
11396 return "";
11397}
11398
11399sub highLight_Signature_PPos_Italic($$$$$)
11400{
11401 my ($FullSignature, $Parameter_Position, $ItalicParams, $ColorParams, $ShowReturn) = @_;
11402 if($CheckObjectsOnly) {
11403 $ItalicParams=$ColorParams=0;
11404 }
11405 my ($Signature, $VersionSpec, $SymbolVersion) = separate_symbol($FullSignature);
11406 my $Return = "";
11407 if($ShowRetVal and $Signature=~s/([^:]):([^:].+?)\Z/$1/g) {
11408 $Return = $2;
11409 }
11410 my $SCenter = detect_center($Signature, "(");
11411 if(not $SCenter)
11412 {# global data
11413 $Signature = htmlSpecChars($Signature);
11414 $Signature=~s!(\[data\])!<span style='color:Black;font-weight:normal;'>$1</span>!g;
11415 $Signature .= (($SymbolVersion)?"<span class='symver'>&#160;$VersionSpec&#160;$SymbolVersion</span>":"");
11416 if($Return and $ShowReturn) {
11417 $Signature .= "<span class='int_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
11418 }
11419 return $Signature;
11420 }
11421 my ($Begin, $End) = (substr($Signature, 0, $SCenter), "");
11422 $Begin.=" " if($Begin!~/ \Z/);
11423 $End = cut_f_attrs($Signature);
11424 my @Parts = ();
11425 my @SParts = get_s_params($Signature, 1);
11426 foreach my $Num (0 .. $#SParts)
11427 {
11428 my $Part = $SParts[$Num];
11429 $Part=~s/\A\s+|\s+\Z//g;
11430 my ($Part_Styled, $ParamName) = (htmlSpecChars($Part), "");
11431 if($Part=~/\([\*]+(\w+)\)/i) {
11432 $ParamName = $1;#func-ptr
11433 }
11434 elsif($Part=~/(\w+)[\,\)]*\Z/i) {
11435 $ParamName = $1;
11436 }
11437 if(not $ParamName) {
11438 push(@Parts, $Part_Styled);
11439 next;
11440 }
11441 if($ItalicParams and not $TName_Tid{1}{$Part}
11442 and not $TName_Tid{2}{$Part})
11443 {
11444 if($Parameter_Position ne ""
11445 and $Num==$Parameter_Position) {
11446 $Part_Styled =~ s!(\W)$ParamName([\,\)]|\Z)!$1<span class='focus_p'>$ParamName</span>$2!ig;
11447 }
11448 elsif($ColorParams) {
11449 $Part_Styled =~ s!(\W)$ParamName([\,\)]|\Z)!$1<span class='color_p'>$ParamName</span>$2!ig;
11450 }
11451 else {
11452 $Part_Styled =~ s!(\W)$ParamName([\,\)]|\Z)!$1<span style='font-style:italic;'>$ParamName</span>$2!ig;
11453 }
11454 }
11455 $Part_Styled=~s/,(\w)/, $1/g;
11456 push(@Parts, $Part_Styled);
11457 }
11458 if(@Parts)
11459 {
11460 foreach my $Num (0 .. $#Parts)
11461 {
11462 if($Num==$#Parts)
11463 { # add ")" to the last parameter
11464 $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]." )</span>";
11465 }
11466 elsif(length($Parts[$Num])<=45) {
11467 $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]."</span>";
11468 }
11469 }
11470 $Signature = htmlSpecChars($Begin)."<span class='int_p'>(&#160;".join(" ", @Parts)."</span>".$End;
11471 }
11472 else {
11473 $Signature = htmlSpecChars($Begin)."<span class='int_p'>(&#160;)</span>".$End;
11474 }
11475 if($Return and $ShowReturn) {
11476 $Signature .= "<span class='int_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
11477 }
11478 $Signature=~s!\[\]![<span style='padding-left:2px;'>]</span>!g;
11479 $Signature=~s!operator=!operator<span style='padding-left:2px'>=</span>!g;
11480 $Signature=~s!(\[in-charge\]|\[not-in-charge\]|\[in-charge-deleting\]|\[static\])!<span style='color:Black;font-weight:normal;'>$1</span>!g;
11481 return $Signature.(($SymbolVersion)?"<span class='symver'>&#160;$VersionSpec&#160;$SymbolVersion</span>":"");
11482}
11483
11484sub get_s_params($$)
11485{
11486 my ($Signature, $Comma) = @_;
11487 my @Parts = ();
11488 my $Signature = $Signature;
11489 my $ShortName = substr($Signature, 0, detect_center($Signature, "("));
11490 $Signature=~s/\A\Q$ShortName\E\(//g;
11491 cut_f_attrs($Signature);
11492 $Signature=~s/\)\Z//;
11493 return separate_params($Signature, $Comma);
11494}
11495
11496sub separate_params($$)
11497{
11498 my ($Params, $Comma) = @_;
11499 my @Parts = ();
11500 my ($Bracket_Num, $Bracket2_Num, $Part_Num) = (0, 0, 0);
11501 foreach my $Pos (0 .. length($Params) - 1)
11502 {
11503 my $Symbol = substr($Params, $Pos, 1);
11504 $Bracket_Num += 1 if($Symbol eq "(");
11505 $Bracket_Num -= 1 if($Symbol eq ")");
11506 $Bracket2_Num += 1 if($Symbol eq "<");
11507 $Bracket2_Num -= 1 if($Symbol eq ">");
11508 if($Symbol eq "," and $Bracket_Num==0 and $Bracket2_Num==0)
11509 {
11510 if($Comma) {# include comma
11511 $Parts[$Part_Num] .= $Symbol;
11512 }
11513 $Part_Num += 1;
11514 }
11515 else {
11516 $Parts[$Part_Num] .= $Symbol;
11517 }
11518 }
11519 return @Parts;
11520}
11521
11522sub detect_center($$)
11523{
11524 my ($Sign, $Target) = @_;
11525 my %B = (
11526 "("=>0,
11527 "<"=>0,
11528 ")"=>0,
11529 ">"=>0 );
11530 my $Center = 0;
11531 if($Sign=~s/(operator([<>\-\=\*]+|\(\)))//g)
11532 { # operators: (),->,->*,<,<=,<<,<<=,>,>=,>>,>>=
11533 $Center+=length($1);
11534 }
11535 foreach my $Pos (0 .. length($Sign)-1)
11536 {
11537 my $S = substr($Sign, $Pos, 1);
11538 if($S eq $Target)
11539 {
11540 if($B{"("}==$B{")"}
11541 and $B{"<"}==$B{">"}) {
11542 return $Center;
11543 }
11544 }
11545 if(defined $B{$S}) {
11546 $B{$S}+=1;
11547 }
11548 $Center+=1;
11549 }
11550 return 0;
11551}
11552
11553sub appendFile($$)
11554{
11555 my ($Path, $Content) = @_;
11556 return if(not $Path);
11557 if(my $Dir = get_dirname($Path)) {
11558 mkpath($Dir);
11559 }
11560 open(FILE, ">>".$Path) || die ("can't open file \'$Path\': $!\n");
11561 print FILE $Content;
11562 close(FILE);
11563}
11564
11565sub writeFile($$)
11566{
11567 my ($Path, $Content) = @_;
11568 return if(not $Path);
11569 if(my $Dir = get_dirname($Path)) {
11570 mkpath($Dir);
11571 }
11572 open (FILE, ">".$Path) || die ("can't open file \'$Path\': $!\n");
11573 print FILE $Content;
11574 close(FILE);
11575}
11576
11577sub readFile($)
11578{
11579 my $Path = $_[0];
11580 return "" if(not $Path or not -f $Path);
11581 open (FILE, $Path);
11582 local $/ = undef;
11583 my $Content = <FILE>;
11584 close(FILE);
11585 if($Path!~/\.(tu|class)\Z/) {
11586 $Content=~s/\r/\n/g;
11587 }
11588 return $Content;
11589}
11590
11591sub get_filename($)
11592{ # much faster than basename() from File::Basename module
11593 if($_[0]=~/([^\/\\]+)[\/\\]*\Z/) {
11594 return $1;
11595 }
11596 return "";
11597}
11598
11599sub get_dirname($)
11600{ # much faster than dirname() from File::Basename module
11601 if($_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) {
11602 return $1;
11603 }
11604 return "";
11605}
11606
11607sub separate_path($) {
11608 return (get_dirname($_[0]), get_filename($_[0]));
11609}
11610
11611sub esc($)
11612{
11613 my $Str = $_[0];
11614 $Str=~s/([()\[\]{}$ &'"`;,<>\+])/\\$1/g;
11615 return $Str;
11616}
11617
11618sub readLineNum($$)
11619{
11620 my ($Path, $Num) = @_;
11621 return "" if(not $Path or not -f $Path);
11622 open (FILE, $Path);
11623 foreach (1 ... $Num) {
11624 <FILE>;
11625 }
11626 my $Line = <FILE>;
11627 close(FILE);
11628 return $Line;
11629}
11630
11631sub readAttributes($)
11632{
11633 my $Path = $_[0];
11634 return () if(not $Path or not -f $Path);
11635 my %Attributes = ();
11636 if(readLineNum($Path, 0)=~/<!--\s+(.+)\s+-->/) {
11637 foreach my $AttrVal (split(/;/, $1)) {
11638 if($AttrVal=~/(.+):(.+)/)
11639 {
11640 my ($Name, $Value) = ($1, $2);
11641 $Attributes{$Name} = $Value;
11642 }
11643 }
11644 }
11645 return \%Attributes;
11646}
11647
11648sub is_abs($) {
11649 return ($_[0]=~/\A(\/|\w+:[\/\\])/);
11650}
11651
11652sub get_abs_path($)
11653{ # abs_path() should NOT be called for absolute inputs
11654 # because it can change them
11655 my $Path = $_[0];
11656 if(not is_abs($Path)) {
11657 $Path = abs_path($Path);
11658 }
11659 return $Path;
11660}
11661
11662sub get_OSgroup()
11663{
11664 $_ = $Config{"osname"};
11665 if(/macos|darwin|rhapsody/i) {
11666 return "macos";
11667 }
11668 elsif(/freebsd|openbsd|netbsd/i) {
11669 return "bsd";
11670 }
11671 elsif(/haiku|beos/i) {
11672 return "beos";
11673 }
11674 elsif(/symbian|epoc/i) {
11675 return "symbian";
11676 }
11677 elsif(/win/i) {
11678 return "windows";
11679 }
11680 else {
11681 return $_;
11682 }
11683}
11684
11685sub getGccVersion($)
11686{
11687 my $LibVersion = $_[0];
11688 if($GCC_VERSION{$LibVersion})
11689 { # dump version
11690 return $GCC_VERSION{$LibVersion};
11691 }
11692 elsif($UsedDump{$LibVersion}{"V"})
11693 { # old-version dumps
11694 return "unknown";
11695 }
11696 my $GccVersion = get_dumpversion($GCC_PATH); # host version
11697 if(not $GccVersion) {
11698 return "unknown";
11699 }
11700 return $GccVersion;
11701}
11702
11703sub showArch($)
11704{
11705 my $Arch = $_[0];
11706 if($Arch eq "arm"
11707 or $Arch eq "mips") {
11708 return uc($Arch);
11709 }
11710 return $Arch;
11711}
11712
11713sub getArch($)
11714{
11715 my $LibVersion = $_[0];
11716 if($CPU_ARCH{$LibVersion})
11717 { # dump version
11718 return $CPU_ARCH{$LibVersion};
11719 }
11720 elsif($UsedDump{$LibVersion}{"V"})
11721 { # old-version dumps
11722 return "unknown";
11723 }
11724 if(defined $Cache{"getArch"}{$LibVersion}) {
11725 return $Cache{"getArch"}{$LibVersion};
11726 }
11727 my $Arch = get_dumpmachine($GCC_PATH); # host version
11728 if(not $Arch) {
11729 return "unknown";
11730 }
11731 if($Arch=~/\A([\w]{3,})(-|\Z)/) {
11732 $Arch = $1;
11733 }
11734 $Arch = "x86" if($Arch=~/\Ai[3-7]86\Z/);
11735 if($OSgroup eq "windows") {
11736 $Arch = "x86" if($Arch=~/win32|mingw32/i);
11737 $Arch = "x86_64" if($Arch=~/win64|mingw64/i);
11738 }
11739 $Cache{"getArch"}{$LibVersion} = $Arch;
11740 return $Arch;
11741}
11742
11743sub get_Report_Header()
11744{
11745 my $ArchInfo = " on <span style='color:Blue;'>".showArch(getArch(1))."</span>";
11746 if(getArch(1) ne getArch(2) or getArch(1) eq "unknown")
11747 { # don't show architecture in the header
11748 $ArchInfo="";
11749 }
11750 my $Report_Header = "<h1><span class='nowrap'>Binary compatibility report for the <span style='color:Blue;'>$TargetLibraryFName</span> $TargetComponent</span>";
11751 $Report_Header .= " <span class='nowrap'>&#160;between <span style='color:Red;'>".$Descriptor{1}{"Version"}."</span> and <span style='color:Red;'>".$Descriptor{2}{"Version"}."</span> versions$ArchInfo</span>";
11752 if($AppPath) {
11753 $Report_Header .= " <span class='nowrap'>&#160;(relating to the portability of application <span style='color:Blue;'>".get_filename($AppPath)."</span>)</span>";
11754 }
11755 $Report_Header .= "</h1>\n";
11756 return $Report_Header;
11757}
11758
11759sub get_SourceInfo()
11760{
11761 my $CheckedHeaders = "<a name='Headers'></a><h2>Header Files (".keys(%{$Registered_Headers{1}}).")</h2><hr/>\n";
11762 $CheckedHeaders .= "<div class='h_list'>\n";
11763 foreach my $Header_Path (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} keys(%{$Registered_Headers{1}}))
11764 {
11765 my $Identity = $Registered_Headers{1}{$Header_Path}{"Identity"};
11766 my $Header_Name = get_filename($Identity);
11767 my $Dest_Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
11768 $CheckedHeaders .= "$Header_Name$Dest_Comment<br/>\n";
11769 }
11770 $CheckedHeaders .= "</div>\n";
11771 $CheckedHeaders .= "<br/><a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
11772 my $CheckedLibs = "<a name='Libs'></a><h2>".ucfirst($SLIB_TYPE)." Libraries (".keys(%{$Library_Symbol{1}}).")</h2><hr/>\n";
11773 $CheckedLibs .= "<div class='lib_list'>\n";
11774 foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$Library_Symbol{1}}))
11775 {
11776 $Library.=" (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
11777 $CheckedLibs .= "$Library<br/>\n";
11778 }
11779 $CheckedLibs .= "</div>\n";
11780 $CheckedLibs .= "<br/><a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
11781 if($CheckObjectsOnly) {
11782 $CheckedHeaders = "";
11783 }
11784 if($CheckHeadersOnly) {
11785 $CheckedLibs = "";
11786 }
11787 return $CheckedHeaders.$CheckedLibs;
11788}
11789
11790sub get_TypeProblems_Count($$$)
11791{
11792 my ($TypeChanges, $TargetPriority, $Level) = @_;
11793 my $Type_Problems_Count = 0;
11794 foreach my $Type_Name (sort keys(%{$TypeChanges}))
11795 {
11796 my %Kinds_Target = ();
11797 foreach my $Kind (keys(%{$TypeChanges->{$Type_Name}}))
11798 {
11799 foreach my $Location (keys(%{$TypeChanges->{$Type_Name}{$Kind}}))
11800 {
11801 my $Target = $TypeChanges->{$Type_Name}{$Kind}{$Location}{"Target"};
11802 my $Priority = getProblemSeverity($Level, $Kind);
11803 next if($Priority ne $TargetPriority);
11804 if($Kinds_Target{$Kind}{$Target}) {
11805 next;
11806 }
11807 if(cmp_priority($Type_MaxPriority{$Level}{$Type_Name}{$Kind}{$Target}, $Priority))
11808 { # select a problem with the highest priority
11809 next;
11810 }
11811 $Kinds_Target{$Kind}{$Target} = 1;
11812 $Type_Problems_Count += 1;
11813 }
11814 }
11815 }
11816 return $Type_Problems_Count;
11817}
11818
11819sub get_Summary($)
11820{
11821 my $Level = $_[0]; # API or ABI
11822 my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low,
11823 $T_Problems_High, $T_Problems_Medium, $T_Problems_Low, $I_Other, $T_Other) = (0,0,0,0,0,0,0,0,0,0);
11824 # check rules
11825 foreach my $Interface (sort keys(%CompatProblems))
11826 {
11827 foreach my $Kind (keys(%{$CompatProblems{$Interface}}))
11828 {
11829 if(not defined $CompatRules{$Level}{$Kind})
11830 { # unknown rule
11831 if(not $UnknownRules{$Level}{$Kind})
11832 { # only one warning
11833 printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
11834 $UnknownRules{$Level}{$Kind}=1;
11835 }
11836 delete($CompatProblems{$Interface}{$Kind});
11837 }
11838 }
11839 }
11840 foreach my $Interface (sort keys(%CompatProblems))
11841 {
11842 foreach my $Kind (sort keys(%{$CompatProblems{$Interface}}))
11843 {
11844 if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols")
11845 {
11846 foreach my $Location (sort keys(%{$CompatProblems{$Interface}{$Kind}}))
11847 {
11848 my $Priority = getProblemSeverity($Level, $Kind);
11849 if($Kind eq "Added_Interface") {
11850 $Added += 1;
11851 }
11852 elsif($Kind eq "Removed_Interface")
11853 {
11854 $Removed += 1;
11855 $TotalAffected{$Level}{$Interface} = $Priority;
11856 }
11857 else
11858 {
11859 if($Priority eq "Safe") {
11860 $I_Other += 1;
11861 }
11862 elsif($Priority eq "High") {
11863 $I_Problems_High += 1;
11864 }
11865 elsif($Priority eq "Medium") {
11866 $I_Problems_Medium += 1;
11867 }
11868 elsif($Priority eq "Low") {
11869 $I_Problems_Low += 1;
11870 }
11871 if(($Priority ne "Low" or $StrictCompat)
11872 and $Priority ne "Safe") {
11873 $TotalAffected{$Level}{$Interface} = $Priority;
11874 }
11875 }
11876 }
11877 }
11878 }
11879 }
11880 my %TypeChanges = ();
11881 foreach my $Interface (sort keys(%CompatProblems))
11882 {
11883 foreach my $Kind (keys(%{$CompatProblems{$Interface}}))
11884 {
11885 if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
11886 {
11887 foreach my $Location (sort {cmp_locations($b, $a)} sort keys(%{$CompatProblems{$Interface}{$Kind}}))
11888 {
11889 my $Type_Name = $CompatProblems{$Interface}{$Kind}{$Location}{"Type_Name"};
11890 my $Target = $CompatProblems{$Interface}{$Kind}{$Location}{"Target"};
11891 my $Priority = getProblemSeverity($Level, $Kind);
11892 if(cmp_priority($Type_MaxPriority{$Level}{$Type_Name}{$Kind}{$Target}, $Priority))
11893 { # select a problem with the highest priority
11894 next;
11895 }
11896 if(($Priority ne "Low" or $StrictCompat)
11897 and $Priority ne "Safe") {
11898 $TotalAffected{$Level}{$Interface} = max_priority($TotalAffected{$Level}{$Interface}, $Priority);
11899 }
11900 %{$TypeChanges{$Type_Name}{$Kind}{$Location}} = %{$CompatProblems{$Interface}{$Kind}{$Location}};
11901 $Type_MaxPriority{$Level}{$Type_Name}{$Kind}{$Target} = max_priority($Type_MaxPriority{$Level}{$Type_Name}{$Kind}{$Target}, $Priority);
11902 }
11903 }
11904 }
11905 }
11906 $T_Problems_High = get_TypeProblems_Count(\%TypeChanges, "High", $Level);
11907 $T_Problems_Medium = get_TypeProblems_Count(\%TypeChanges, "Medium", $Level);
11908 $T_Problems_Low = get_TypeProblems_Count(\%TypeChanges, "Low", $Level);
11909 $T_Other = get_TypeProblems_Count(\%TypeChanges, "Safe", $Level);
11910 if($CheckObjectsOnly)
11911 { # only removed exported symbols
11912 $RESULT{"Affected"} = $Removed*100/keys(%{$Symbol_Library{1}});
11913 }
11914 else
11915 { # changed and removed public symbols
11916 my $SCount = keys(%CheckedSymbols);
11917 if($ExtendedCheck)
11918 { # don't count external_func_0 for constants
11919 $SCount-=1;
11920 }
11921 if($SCount)
11922 {
11923 my %Weight = (
11924 "High" => 100,
11925 "Medium" => 50,
11926 "Low" => 25
11927 );
11928 foreach (keys(%{$TotalAffected{$Level}})) {
11929 $RESULT{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
11930 }
11931 $RESULT{"Affected"} = $RESULT{"Affected"}/$SCount;
11932 }
11933 else {
11934 $RESULT{"Affected"} = 0;
11935 }
11936 }
11937 $RESULT{"Affected"} = show_number($RESULT{"Affected"});
11938 if($RESULT{"Affected"}>=100) {
11939 $RESULT{"Affected"} = 100;
11940 }
11941
11942 $RESULT{"Problems"} = $Removed + $T_Problems_High + $I_Problems_High;
11943 $RESULT{"Problems"} += $T_Problems_Medium + $I_Problems_Medium;
11944 $RESULT{$StrictCompat?"Problems":"Warnings"} += $T_Problems_Low + $I_Problems_Low;
11945 $RESULT{$StrictCompat?"Problems":"Warnings"} += keys(%ProblemsWithConstants);
11946 if($CheckImpl) {
11947 $RESULT{$StrictCompat?"Problems":"Warnings"} += keys(%ImplProblems);
11948 }
11949 $RESULT{"Verdict"} = $RESULT{"Problems"}?"incompatible":"compatible";
11950
11951 my $TotalTypes = keys(%CheckedTypes);
11952 if(not $TotalTypes) {# list all the types
11953 $TotalTypes = keys(%{$TName_Tid{1}});
11954 }
11955
11956 my ($Arch1, $Arch2) = (getArch(1), getArch(2));
11957 my ($GccV1, $GccV2) = (getGccVersion(1), getGccVersion(2));
11958
11959 my ($TestInfo, $TestResults, $Problem_Summary) = ();
11960
11961 if($ReportFormat eq "xml")
11962 { # XML
11963 # test info
11964 $TestInfo .= " <library>$TargetLibraryName</library>\n";
11965 $TestInfo .= " <version1>\n";
11966 $TestInfo .= " <number>".$Descriptor{1}{"Version"}."</number>\n";
11967 $TestInfo .= " <architecture>$Arch1</architecture>\n";
11968 $TestInfo .= " <gcc>$GccV1</gcc>\n";
11969 $TestInfo .= " </version1>\n";
11970
11971 $TestInfo .= " <version2>\n";
11972 $TestInfo .= " <number>".$Descriptor{2}{"Version"}."</number>\n";
11973 $TestInfo .= " <architecture>$Arch2</architecture>\n";
11974 $TestInfo .= " <gcc>$GccV2</gcc>\n";
11975 $TestInfo .= " </version2>\n";
11976 $TestInfo = "<test_info>\n".$TestInfo."</test_info>\n\n";
11977
11978 # test results
11979 $TestResults .= " <headers>\n";
11980 foreach my $Name (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} keys(%{$Registered_Headers{1}}))
11981 {
11982 my $Identity = $Registered_Headers{1}{$_}{"Identity"};
11983 my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
11984 $TestResults .= " <name>".get_filename($Name).$Comment."</name>\n";
11985 }
11986 $TestResults .= " </headers>\n";
11987
11988 $TestResults .= " <libs>\n";
11989 foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$Library_Symbol{1}}))
11990 {
11991 $Library.=" (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
11992 $TestResults .= " <name>$Library</name>\n";
11993 }
11994 $TestResults .= " </libs>\n";
11995
11996 $TestResults .= " <symbols>".(keys(%CheckedSymbols) - keys(%GeneratedSymbols))."</symbols>\n";
11997 $TestResults .= " <types>".$TotalTypes."</types>\n";
11998
11999 $TestResults .= " <verdict>".$RESULT{"Verdict"}."</verdict>\n";
12000 $TestResults .= " <affected>".$RESULT{"Affected"}."</affected>\n";
12001 $TestResults = "<test_results>\n".$TestResults."</test_results>\n\n";
12002
12003 # problem summary
12004 $Problem_Summary .= " <added_symbols>".$Added."</added_symbols>\n";
12005 $Problem_Summary .= " <removed_symbols>".$Removed."</removed_symbols>\n";
12006
12007 $Problem_Summary .= " <problems_with_types>\n";
12008 $Problem_Summary .= " <high>$T_Problems_High</high>\n";
12009 $Problem_Summary .= " <medium>$T_Problems_Medium</medium>\n";
12010 $Problem_Summary .= " <low>$T_Problems_Low</low>\n";
12011 $Problem_Summary .= " <safe>$T_Other</safe>\n";
12012 $Problem_Summary .= " </problems_with_types>\n";
12013
12014 $Problem_Summary .= " <problems_with_symbols>\n";
12015 $Problem_Summary .= " <high>$I_Problems_High</high>\n";
12016 $Problem_Summary .= " <medium>$I_Problems_Medium</medium>\n";
12017 $Problem_Summary .= " <low>$I_Problems_Low</low>\n";
12018 $Problem_Summary .= " </problems_with_symbols>\n";
12019
12020 $Problem_Summary .= " <problems_with_constants>\n";
12021 $Problem_Summary .= " <low>".keys(%ProblemsWithConstants)."</low>\n";
12022 $Problem_Summary .= " </problems_with_constants>\n";
12023 if($CheckImpl)
12024 {
12025 $Problem_Summary .= " <impl>\n";
12026 $Problem_Summary .= " <low>".keys(%ImplProblems)."</low>\n";
12027 $Problem_Summary .= " </impl>\n";
12028 }
12029 $Problem_Summary = "<problem_summary>\n".$Problem_Summary."</problem_summary>\n\n";
12030
12031 return ($TestInfo.$TestResults.$Problem_Summary, "");
12032 }
12033 else
12034 { # HTML
12035 # test info
12036 $TestInfo = "<h2>Test Info</h2><hr/>\n";
12037 $TestInfo .= "<table cellpadding='3' cellspacing='0' class='summary'>\n";
12038 $TestInfo .= "<tr><th>".ucfirst($TargetComponent)." Name</th><td>$TargetLibraryFName</td></tr>\n";
12039
12040 my (@VInf1, @VInf2, $AddTestInfo) = ();
12041 if($Arch1 ne "unknown"
12042 and $Arch2 ne "unknown")
12043 { # CPU arch
12044 if($Arch1 eq $Arch2)
12045 { # go to the separate section
12046 $AddTestInfo .= "<tr><th>CPU Architecture</th><td>".showArch($Arch1)."</td></tr>\n";
12047 }
12048 else
12049 { # go to the version number
12050 push(@VInf1, showArch($Arch1));
12051 push(@VInf2, showArch($Arch2));
12052 }
12053 }
12054 if($GccV1 ne "unknown"
12055 and $GccV2 ne "unknown"
12056 and $OStarget ne "windows")
12057 { # GCC version
12058 if($GccV1 eq $GccV2)
12059 { # go to the separate section
12060 $AddTestInfo .= "<tr><th>GCC Version</th><td>$GccV1</td></tr>\n";
12061 }
12062 else
12063 { # go to the version number
12064 push(@VInf1, "gcc ".$GccV1);
12065 push(@VInf2, "gcc ".$GccV2);
12066 }
12067 }
12068 # show long version names with GCC version and CPU architecture name (if different)
12069 $TestInfo .= "<tr><th>Version #1</th><td>".$Descriptor{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."</td></tr>\n";
12070 $TestInfo .= "<tr><th>Version #2</th><td>".$Descriptor{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."</td></tr>\n";
12071 $TestInfo .= $AddTestInfo;
12072 #if($COMMON_LANGUAGE{1}) {
12073 # $TestInfo .= "<tr><th>Language</th><td>".$COMMON_LANGUAGE{1}."</td></tr>\n";
12074 #}
12075 if($ExtendedCheck) {
12076 $TestInfo .= "<tr><th>Mode</th><td>Extended</td></tr>\n";
12077 }
12078 $TestInfo .= "</table>\n";
12079
12080 # test results
12081 $TestResults = "<h2>Test Results</h2><hr/>\n";
12082 $TestResults .= "<table cellpadding='3' cellspacing='0' class='summary'>";
12083
12084 my $Headers_Link = "0";
12085 $Headers_Link = "<a href='#Headers' style='color:Blue;'>".keys(%{$Registered_Headers{1}})."</a>" if(keys(%{$Registered_Headers{1}})>0);
12086 $TestResults .= "<tr><th>Total Header Files</th><td>".($CheckObjectsOnly?"0&#160;(not&#160;analyzed)":$Headers_Link)."</td></tr>\n";
12087
12088 if(not $ExtendedCheck)
12089 {
12090 my $Libs_Link = "0";
12091 $Libs_Link = "<a href='#Libs' style='color:Blue;'>".keys(%{$Library_Symbol{1}})."</a>" if(keys(%{$Library_Symbol{1}})>0);
12092 $TestResults .= "<tr><th>Total ".ucfirst($SLIB_TYPE)." Libraries</th><td>".($CheckHeadersOnly?"0&#160;(not&#160;analyzed)":$Libs_Link)."</td></tr>\n";
12093 }
12094
12095 $TestResults .= "<tr><th>Total Symbols / Types</th><td>".(keys(%CheckedSymbols) - keys(%GeneratedSymbols))." / ".$TotalTypes."</td></tr>\n";
12096
12097 my $Verdict = "";
12098 if($RESULT{"Problems"}) {
12099 $Verdict = "<span style='color:Red;'><b>Incompatible<br/>(".$RESULT{"Affected"}."%)</b></span>";
12100 }
12101 else {
12102 $Verdict = "<span style='color:Green;'><b>Compatible</b></span>";
12103 }
12104 my $META_DATA = $RESULT{"Problems"}?"verdict:incompatible;":"verdict:compatible;";
12105
12106 $TestResults .= "<tr><th>Verdict</th><td>$Verdict</td></tr>";
12107 $TestResults .= "</table>\n";
12108
12109 $META_DATA .= "affected:".$RESULT{"Affected"}.";";# in percents
12110 # problem summary
12111 $Problem_Summary = "<h2>Problem Summary</h2><hr/>\n";
12112 $Problem_Summary .= "<table cellpadding='3' cellspacing='0' class='summary'>";
12113 $Problem_Summary .= "<tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr>";
12114
12115 my $Added_Link = "0";
12116 $Added_Link = "<a href='#Added' style='color:Blue;'>$Added</a>" if($Added>0);
12117 #$Added_Link = "n/a" if($CheckHeadersOnly);
12118 $META_DATA .= "added:$Added;";
12119 $Problem_Summary .= "<tr><th>Added Symbols</th><td>-</td><td>$Added_Link</td></tr>\n";
12120
12121 my $Removed_Link = "0";
12122 $Removed_Link = "<a href='#Removed' style='color:Blue;'>$Removed</a>" if($Removed>0);
12123 #$Removed_Link = "n/a" if($CheckHeadersOnly);
12124 $META_DATA .= "removed:$Removed;";
12125 $Problem_Summary .= "<tr><th>Removed Symbols</th><td style='color:Red;'>High</td><td>$Removed_Link</td></tr>\n";
12126
12127 my $TH_Link = "0";
12128 $TH_Link = "<a href='#Type_Problems_High' style='color:Blue;'>$T_Problems_High</a>" if($T_Problems_High>0);
12129 $TH_Link = "n/a" if($CheckObjectsOnly);
12130 $META_DATA .= "type_problems_high:$T_Problems_High;";
12131 $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Data Types</th><td style='color:Red;'>High</td><td>$TH_Link</td></tr>\n";
12132
12133 my $TM_Link = "0";
12134 $TM_Link = "<a href='#Type_Problems_Medium' style='color:Blue;'>$T_Problems_Medium</a>" if($T_Problems_Medium>0);
12135 $TM_Link = "n/a" if($CheckObjectsOnly);
12136 $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
12137 $Problem_Summary .= "<tr><td>Medium</td><td>$TM_Link</td></tr>\n";
12138
12139 my $TL_Link = "0";
12140 $TL_Link = "<a href='#Type_Problems_Low' style='color:Blue;'>$T_Problems_Low</a>" if($T_Problems_Low>0);
12141 $TL_Link = "n/a" if($CheckObjectsOnly);
12142 $META_DATA .= "type_problems_low:$T_Problems_Low;";
12143 $Problem_Summary .= "<tr><td>Low</td><td>$TL_Link</td></tr>\n";
12144
12145 my $IH_Link = "0";
12146 $IH_Link = "<a href='#Interface_Problems_High' style='color:Blue;'>$I_Problems_High</a>" if($I_Problems_High>0);
12147 $IH_Link = "n/a" if($CheckObjectsOnly);
12148 $META_DATA .= "interface_problems_high:$I_Problems_High;";
12149 $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Symbols</th><td style='color:Red;'>High</td><td>$IH_Link</td></tr>\n";
12150
12151 my $IM_Link = "0";
12152 $IM_Link = "<a href='#Interface_Problems_Medium' style='color:Blue;'>$I_Problems_Medium</a>" if($I_Problems_Medium>0);
12153 $IM_Link = "n/a" if($CheckObjectsOnly);
12154 $META_DATA .= "interface_problems_medium:$I_Problems_Medium;";
12155 $Problem_Summary .= "<tr><td>Medium</td><td>$IM_Link</td></tr>\n";
12156
12157 my $IL_Link = "0";
12158 $IL_Link = "<a href='#Interface_Problems_Low' style='color:Blue;'>$I_Problems_Low</a>" if($I_Problems_Low>0);
12159 $IL_Link = "n/a" if($CheckObjectsOnly);
12160 $META_DATA .= "interface_problems_low:$I_Problems_Low;";
12161 $Problem_Summary .= "<tr><td>Low</td><td>$IL_Link</td></tr>\n";
12162
12163 my $ChangedConstants_Link = "0";
12164 if(keys(%CheckedSymbols) and keys(%ProblemsWithConstants)>0) {
12165 $ChangedConstants_Link = "<a href='#Changed_Constants' style='color:Blue;'>".keys(%ProblemsWithConstants)."</a>";
12166 }
12167 $ChangedConstants_Link = "n/a" if($CheckObjectsOnly);
12168 $META_DATA .= "changed_constants:".keys(%ProblemsWithConstants).";";
12169 $Problem_Summary .= "<tr><th>Problems with<br/>Constants</th><td>Low</td><td>$ChangedConstants_Link</td></tr>\n";
12170
12171 if($CheckImpl)
12172 {
12173 my $ChangedImpl_Link = "0";
12174 $ChangedImpl_Link = "<a href='#Changed_Implementation' style='color:Blue;'>".keys(%ImplProblems)."</a>" if(keys(%ImplProblems)>0);
12175 $ChangedImpl_Link = "n/a" if($CheckHeadersOnly);
12176 $META_DATA .= "changed_implementation:".keys(%ImplProblems).";";
12177 $Problem_Summary .= "<tr><th>Problems with<br/>Implementation</th><td>Low</td><td>$ChangedImpl_Link</td></tr>\n";
12178 }
12179 # Safe Changes
12180 my $TS_Link = "0";
12181 if($T_Other) {
12182 $TS_Link = "<a href='#Other_Changes_In_Types' style='color:Blue;'>$T_Other</a>";
12183 }
12184 $TS_Link = "n/a" if($CheckObjectsOnly);
12185 $Problem_Summary .= "<tr><th>Other Changes</th><td>-</td><td>$TS_Link</td></tr>\n";
12186
12187 $META_DATA .= "tool_version:$TOOL_VERSION";
12188 $Problem_Summary .= "</table>\n";
12189 return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA);
12190 }
12191}
12192
12193sub show_number($)
12194{
12195 if($_[0]
12196 and my $Num = cut_off_number($_[0], 3))
12197 {
12198 if($Num eq "0") {
12199 $Num = cut_off_number($_[0], 7);
12200 }
12201 if($Num eq "0") {
12202 $Num = $_[0];
12203 }
12204 return $Num;
12205 }
12206 return $_[0];
12207}
12208
12209sub cut_off_number($$)
12210{
12211 my ($num, $digs_to_cut) = @_;
12212 if($num!~/\./)
12213 {
12214 $num .= ".";
12215 foreach (1 .. $digs_to_cut-1) {
12216 $num .= "0";
12217 }
12218 }
12219 elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
12220 {
12221 foreach (1 .. $digs_to_cut - 1 - length($1)) {
12222 $num .= "0";
12223 }
12224 }
12225 elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
12226 $num=sprintf("%.".($digs_to_cut-1)."f", $num);
12227 }
12228 $num=~s/\.[0]+\Z//g;
12229 return $num;
12230}
12231
12232sub get_Report_ChangedConstants()
12233{
12234 my ($CHANGED_CONSTANTS, %HeaderConstant) = ();
12235 foreach my $Constant (keys(%ProblemsWithConstants)) {
12236 $HeaderConstant{$Constants{1}{$Constant}{"Header"}}{$Constant} = 1;
12237 }
12238 my $Kind = "Changed_Constant";
12239 if($ReportFormat eq "xml")
12240 { # XML
12241 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%HeaderConstant))
12242 {
12243 $CHANGED_CONSTANTS .= " <header name=\"$HeaderName\">\n";
12244 foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$HeaderConstant{$HeaderName}}))
12245 {
12246 $CHANGED_CONSTANTS .= " <constant name=\"$Constant\">\n";
12247 my $Change = $CompatRules{"Binary"}{$Kind}{"Change"};
12248 my $Effect = $CompatRules{"Binary"}{$Kind}{"Effect"};
12249 my $Overcome = $CompatRules{"Binary"}{$Kind}{"Overcome"};
12250 $CHANGED_CONSTANTS .= " <problem id=\"$Kind\">\n";
12251 $CHANGED_CONSTANTS .= " <change".getXmlParams($Change, $ProblemsWithConstants{$Constant}).">$Change</change>\n";
12252 $CHANGED_CONSTANTS .= " <effect".getXmlParams($Effect, $ProblemsWithConstants{$Constant}).">$Effect</effect>\n";
12253 $CHANGED_CONSTANTS .= " <overcome".getXmlParams($Overcome, $ProblemsWithConstants{$Constant}).">$Overcome</overcome>\n";
12254 $CHANGED_CONSTANTS .= " </problem>\n";
12255 $CHANGED_CONSTANTS .= " </constant>\n";
12256 }
12257 $CHANGED_CONSTANTS .= " </header>\n";
12258 }
12259 $CHANGED_CONSTANTS = "<problems_with_constants severity=\"Low\">\n".$CHANGED_CONSTANTS."</problems_with_constants>\n\n";
12260 }
12261 else
12262 { # HTML
12263 my $Number = 0;
12264 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%HeaderConstant))
12265 {
12266 $CHANGED_CONSTANTS .= "<span class='h_name'>$HeaderName</span><br/>\n";
12267 foreach my $Name (sort {lc($a) cmp lc($b)} keys(%{$HeaderConstant{$HeaderName}}))
12268 {
12269 $Number += 1;
12270 my $Change = applyMacroses("Binary", $Kind, $CompatRules{"Binary"}{$Kind}{"Change"}, $ProblemsWithConstants{$Name});
12271 my $Effect = $CompatRules{"Binary"}{$Kind}{"Effect"};
12272 my $Report = "<tr><th>1</th><td align='left' valign='top'>".$Change."</td><td align='left' valign='top'>$Effect</td></tr>\n";
12273 $Report = $ContentDivStart."<table cellpadding='3' cellspacing='0' class='problems_table'><tr><th width='2%'></th><th width='47%'>Change</th><th>Effect</th></tr>".$Report."</table><br/>$ContentDivEnd\n";
12274 $Report = $ContentSpanStart."<span class='extendable'>[+]</span> ".$Name.$ContentSpanEnd."<br/>\n".$Report;
12275 $CHANGED_CONSTANTS .= insertIDs($Report);
12276 }
12277 $CHANGED_CONSTANTS .= "<br/>\n";
12278 }
12279 if($CHANGED_CONSTANTS) {
12280 $CHANGED_CONSTANTS = "<a name='Changed_Constants'></a><h2>Problems with Constants ($Number)</h2><hr/>\n".$CHANGED_CONSTANTS."<a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
12281 }
12282 }
12283 return $CHANGED_CONSTANTS;
12284}
12285
12286sub get_Report_Impl()
12287{
12288 my ($CHANGED_IMPLEMENTATION, %HeaderLibFunc);
12289 foreach my $Interface (sort keys(%ImplProblems))
12290 {
12291 my $HeaderName = $CompleteSignature{1}{$Interface}{"Header"};
12292 my $DyLib = $Symbol_Library{1}{$Interface};
12293 $HeaderLibFunc{$HeaderName}{$DyLib}{$Interface} = 1;
12294 }
12295 my $Changed_Number = 0;
12296 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%HeaderLibFunc))
12297 {
12298 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$HeaderLibFunc{$HeaderName}}))
12299 {
12300 my $FDyLib=$DyLib.($DyLib!~/\.\w+\Z/?" (.$LIB_EXT)":"");
12301 if($HeaderName) {
12302 $CHANGED_IMPLEMENTATION .= "<span class='h_name'>$HeaderName</span>, <span class='lib_name'>$FDyLib</span><br/>\n";
12303 }
12304 else {
12305 $CHANGED_IMPLEMENTATION .= "<span class='lib_name'>$DyLib</span><br/>\n";
12306 }
12307 my %NameSpace_Interface = ();
12308 foreach my $Interface (keys(%{$HeaderLibFunc{$HeaderName}{$DyLib}})) {
12309 $NameSpace_Interface{get_IntNameSpace($Interface, 2)}{$Interface} = 1;
12310 }
12311 foreach my $NameSpace (sort keys(%NameSpace_Interface))
12312 {
12313 $CHANGED_IMPLEMENTATION .= ($NameSpace)?"<span class='ns_title'>namespace</span> <span class='ns'>$NameSpace</span>"."<br/>\n":"";
12314 my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NameSpace_Interface{$NameSpace}});
12315 foreach my $Interface (@SortedInterfaces)
12316 {
12317 $Changed_Number += 1;
12318 my $Signature = get_Signature($Interface, 1);
12319 if($NameSpace) {
12320 $Signature=~s/(\W|\A)\Q$NameSpace\E\:\:(\w)/$1$2/g;
12321 }
12322 $CHANGED_IMPLEMENTATION .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[ symbol: <b>$Interface</b> ]</span>".$ImplProblems{$Interface}{"Diff"}."<br/><br/>".$ContentDivEnd."\n");
12323 }
12324 }
12325 $CHANGED_IMPLEMENTATION .= "<br/>\n";
12326 }
12327 }
12328 if($CHANGED_IMPLEMENTATION) {
12329 $CHANGED_IMPLEMENTATION = "<a name='Changed_Implementation'></a><h2>Problems with Implementation ($Changed_Number)</h2><hr/>\n".$CHANGED_IMPLEMENTATION."<a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
12330 }
12331 return $CHANGED_IMPLEMENTATION;
12332}
12333
12334sub get_Report_Added($)
12335{
12336 my $Level = $_[0]; # API or ABI
12337
12338 my ($ADDED_INTERFACES, %FuncAddedInHeaderLib);
12339 foreach my $Interface (sort keys(%CompatProblems))
12340 {
12341 foreach my $Kind (sort keys(%{$CompatProblems{$Interface}}))
12342 {
12343 if($Kind eq "Added_Interface")
12344 {
12345 my $HeaderName = $CompleteSignature{2}{$Interface}{"Header"};
12346 my $DyLib = $Symbol_Library{2}{$Interface};
12347 $FuncAddedInHeaderLib{$HeaderName}{$DyLib}{$Interface} = 1;
12348 }
12349 }
12350 }
12351 if($ReportFormat eq "xml")
12352 { # XML
12353 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%FuncAddedInHeaderLib))
12354 {
12355 $ADDED_INTERFACES .= " <header name=\"$HeaderName\">\n";
12356 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$FuncAddedInHeaderLib{$HeaderName}}))
12357 {
12358 $ADDED_INTERFACES .= " <library name=\"$DyLib\">\n";
12359 foreach my $Interface (keys(%{$FuncAddedInHeaderLib{$HeaderName}{$DyLib}})) {
12360 $ADDED_INTERFACES .= " <name>$Interface</name>\n";
12361 }
12362 $ADDED_INTERFACES .= " </library>\n";
12363 }
12364 $ADDED_INTERFACES .= " </header>\n";
12365 }
12366 $ADDED_INTERFACES = "<added_symbols>\n".$ADDED_INTERFACES."</added_symbols>\n\n";
12367 }
12368 else
12369 { # HTML
12370 my $Added_Number = 0;
12371 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%FuncAddedInHeaderLib))
12372 {
12373 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$FuncAddedInHeaderLib{$HeaderName}}))
12374 {
12375 my $FDyLib=$DyLib.($DyLib!~/\.\w+\Z/?" (.$LIB_EXT)":"");
12376 if($HeaderName and $DyLib) {
12377 $ADDED_INTERFACES .= "<span class='h_name'>$HeaderName</span>, <span class='lib_name'>$FDyLib</span><br/>\n";
12378 }
12379 elsif($DyLib) {
12380 $ADDED_INTERFACES .= "<span class='lib_name'>$FDyLib</span><br/>\n";
12381 }
12382 elsif($HeaderName) {
12383 $ADDED_INTERFACES .= "<span class='h_name'>$HeaderName</span><br/>\n";
12384 }
12385 my %NameSpace_Interface = ();
12386 foreach my $Interface (keys(%{$FuncAddedInHeaderLib{$HeaderName}{$DyLib}})) {
12387 $NameSpace_Interface{get_IntNameSpace($Interface, 2)}{$Interface} = 1;
12388 }
12389 foreach my $NameSpace (sort keys(%NameSpace_Interface))
12390 {
12391 $ADDED_INTERFACES .= ($NameSpace)?"<span class='ns_title'>namespace</span> <span class='ns'>$NameSpace</span>"."<br/>\n":"";
12392 my @SortedInterfaces = sort {lc(get_Signature($a, 2)) cmp lc(get_Signature($b, 2))} keys(%{$NameSpace_Interface{$NameSpace}});
12393 foreach my $Interface (@SortedInterfaces)
12394 {
12395 $Added_Number += 1;
12396 my $SubReport = "";
12397 my $Signature = get_Signature($Interface, 2);
12398 if($NameSpace) {
12399 $Signature=~s/(\W|\A)\Q$NameSpace\E\:\:(\w)/$1$2/g;
12400 }
12401 if($Interface=~/\A(_Z|\?)/) {
12402 if($Signature) {
12403 $SubReport = insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[ symbol: <b>$Interface</b> ]</span><br/><br/>".$ContentDivEnd."\n");
12404 }
12405 else {
12406 $SubReport = "<span class=\"iname\">".$Interface."</span><br/>\n";
12407 }
12408 }
12409 else {
12410 if($Signature) {
12411 $SubReport = "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
12412 }
12413 else {
12414 $SubReport = "<span class=\"iname\">".$Interface."</span><br/>\n";
12415 }
12416 }
12417 $ADDED_INTERFACES .= $SubReport;
12418 }
12419 }
12420 $ADDED_INTERFACES .= "<br/>\n";
12421 }
12422 }
12423 if($ADDED_INTERFACES) {
12424 $ADDED_INTERFACES = "<a name='Added'></a><h2>Added Symbols ($Added_Number)</h2><hr/>\n".$ADDED_INTERFACES."<a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
12425 }
12426 }
12427 return $ADDED_INTERFACES;
12428}
12429
12430sub get_Report_Removed($)
12431{
12432 my $Level = $_[0];# API or ABI
12433 my (%FuncRemovedFromHeaderLib, $REMOVED_INTERFACES) = ();
12434 foreach my $Interface (sort keys(%CompatProblems))
12435 {
12436 foreach my $Kind (sort keys(%{$CompatProblems{$Interface}}))
12437 {
12438 if($Kind eq "Removed_Interface")
12439 {
12440 my $HeaderName = $CompleteSignature{1}{$Interface}{"Header"};
12441 my $DyLib = $Symbol_Library{1}{$Interface};
12442 $FuncRemovedFromHeaderLib{$HeaderName}{$DyLib}{$Interface} = 1;
12443 }
12444 }
12445 }
12446 if($ReportFormat eq "xml")
12447 { # XML
12448 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%FuncRemovedFromHeaderLib))
12449 {
12450 $REMOVED_INTERFACES .= " <header name=\"$HeaderName\">\n";
12451 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$FuncRemovedFromHeaderLib{$HeaderName}}))
12452 {
12453 $REMOVED_INTERFACES .= " <library name=\"$DyLib\">\n";
12454 foreach my $Interface (keys(%{$FuncRemovedFromHeaderLib{$HeaderName}{$DyLib}})) {
12455 $REMOVED_INTERFACES .= " <name>$Interface</name>\n";
12456 }
12457 $REMOVED_INTERFACES .= " </library>\n";
12458 }
12459 $REMOVED_INTERFACES .= " </header>\n";
12460 }
12461 $REMOVED_INTERFACES = "<removed_symbols>\n".$REMOVED_INTERFACES."</removed_symbols>\n\n";
12462 }
12463 else
12464 { # HTML
12465 my $Removed_Number = 0;
12466 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%FuncRemovedFromHeaderLib))
12467 {
12468 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$FuncRemovedFromHeaderLib{$HeaderName}}))
12469 {
12470 my $FDyLib=$DyLib.($DyLib!~/\.\w+\Z/?" (.$LIB_EXT)":"");
12471 if($HeaderName and $DyLib) {
12472 $REMOVED_INTERFACES .= "<span class='h_name'>$HeaderName</span>, <span class='lib_name'>$FDyLib</span><br/>\n";
12473 }
12474 elsif($DyLib) {
12475 $REMOVED_INTERFACES .= "<span class='lib_name'>$FDyLib</span><br/>\n";
12476 }
12477 elsif($HeaderName) {
12478 $REMOVED_INTERFACES .= "<span class='h_name'>$HeaderName</span><br/>\n";
12479 }
12480 my %NameSpace_Interface = ();
12481 foreach my $Interface (keys(%{$FuncRemovedFromHeaderLib{$HeaderName}{$DyLib}}))
12482 {
12483 $NameSpace_Interface{get_IntNameSpace($Interface, 1)}{$Interface} = 1;
12484 }
12485 foreach my $NameSpace (sort keys(%NameSpace_Interface))
12486 {
12487 $REMOVED_INTERFACES .= ($NameSpace)?"<span class='ns_title'>namespace</span> <span class='ns'>$NameSpace</span>"."<br/>\n":"";
12488 my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NameSpace_Interface{$NameSpace}});
12489 foreach my $Interface (@SortedInterfaces)
12490 {
12491 $Removed_Number += 1;
12492 my $SubReport = "";
12493 my $Signature = get_Signature($Interface, 1);
12494 if($NameSpace) {
12495 $Signature=~s/(\W|\A)\Q$NameSpace\E\:\:(\w)/$1$2/g;
12496 }
12497 if($Interface=~/\A(_Z|\?)/) {
12498 if($Signature) {
12499 $SubReport = insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[ symbol: <b>$Interface</b> ]</span><br/><br/>".$ContentDivEnd."\n");
12500 }
12501 else {
12502 $SubReport = "<span class=\"iname\">".$Interface."</span><br/>\n";
12503 }
12504 }
12505 else {
12506 if($Signature) {
12507 $SubReport = "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
12508 }
12509 else {
12510 $SubReport = "<span class=\"iname\">".$Interface."</span><br/>\n";
12511 }
12512 }
12513 $REMOVED_INTERFACES .= $SubReport;
12514 }
12515 }
12516 $REMOVED_INTERFACES .= "<br/>\n";
12517 }
12518 }
12519 if($REMOVED_INTERFACES) {
12520 $REMOVED_INTERFACES = "<a name='Removed'></a><a name='Withdrawn'></a><h2>Removed Symbols ($Removed_Number)</h2><hr/>\n".$REMOVED_INTERFACES."<a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
12521 }
12522 }
12523 return $REMOVED_INTERFACES;
12524}
12525
12526sub getXmlParams($$)
12527{
12528 my ($Content, $Problem) = @_;
12529 return "" if(not $Content or not $Problem);
12530 my %XMLparams = ();
12531 foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
12532 {
12533 my $Macro = "\@".lc($Attr);
12534 if($Content=~/\Q$Macro\E/) {
12535 $XMLparams{lc($Attr)} = $Problem->{$Attr};
12536 }
12537 }
12538 my @PString = ();
12539 foreach my $P (sort {$b cmp $a} keys(%XMLparams)) {
12540 push(@PString, $P."=\"".htmlSpecChars($XMLparams{$P})."\"");
12541 }
12542 if(@PString) {
12543 return " ".join(" ", @PString);
12544 }
12545 else {
12546 return "";
12547 }
12548}
12549
12550sub addMarkup($)
12551{
12552 my $Content = $_[0];
12553 # auto-markup
12554 $Content=~s/\n[ ]*//; # spaces
12555 $Content=~s!(\@\w+\s*\(\@\w+\))!<nowrap>$1</nowrap>!g; # @old_type (@old_size)
12556 $Content=~s!(... \(\w+\))!<nowrap><b>$1</b></nowrap>!g; # ... (va_list)
12557 $Content=~s!([2-9]\))!<br/>$1!g; # 1), 2), ...
12558 if($Content=~/\ANOTE:/)
12559 { # notes
12560 $Content=~s!(NOTE):!<b>$1</b>:!g;
12561 }
12562 else {
12563 $Content=~s!(NOTE):!<br/><b>$1</b>:!g;
12564 }
12565 $Content=~s! (out)-! <b>$1</b>-!g; # out-parameters
12566 my @Keywords = (
12567 "void",
12568 "const",
12569 "static",
12570 "restrict",
12571 "volatile",
12572 "register",
12573 "virtual",
12574 "virtually"
12575 );
12576 my $MKeys = join("|", @Keywords);
12577 foreach (@Keywords) {
12578 $MKeys .= "|non-".$_;
12579 }
12580 $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1<b>$2</b>$3!ig; # intrinsic types, modifiers
12581 return $Content;
12582}
12583
12584sub applyMacroses($$$$)
12585{
12586 my ($Level, $Kind, $Content, $Problem) = @_;
12587 return "" if(not $Content or not $Problem);
12588 $Problem->{"Word_Size"} = $WORD_SIZE{2};
12589 $Content = addMarkup($Content);
12590 # macros
12591 foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
12592 {
12593 my $Macro = "\@".lc($Attr);
12594 my $Value = $Problem->{$Attr};
12595 if($Value=~/\s\(/)
12596 { # functions
12597 $Value = black_name($Value);
12598 }
12599 elsif($Value=~/\s/) {
12600 $Value = "<span class='value'>".htmlSpecChars($Value)."</span>";
12601 }
12602 elsif($Value=~/\A\d+\Z/
12603 and ($Attr eq "Old_Size" or $Attr eq "New_Size"))
12604 { # bits to bytes
12605 if($Value % $BYTE_SIZE)
12606 { # bits
12607 if($Value==1) {
12608 $Value = "<b>".$Value."</b> bit";
12609 }
12610 else {
12611 $Value = "<b>".$Value."</b> bits";
12612 }
12613 }
12614 else
12615 { # bytes
12616 $Value /= $BYTE_SIZE;
12617 if($Value==1) {
12618 $Value = "<b>".$Value."</b> byte";
12619 }
12620 else {
12621 $Value = "<b>".$Value."</b> bytes";
12622 }
12623 }
12624 }
12625 else {
12626 $Value = "<b>".htmlSpecChars($Value)."</b>";
12627 }
12628 $Content=~s/\Q$Macro\E/$Value/g;
12629 }
12630
12631 if($Content=~/(\A|[^\@\w])\@\w/)
12632 {
12633 if(not $IncompleteRules{$Level}{$Kind})
12634 { # only one warning
12635 printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")");
12636 $IncompleteRules{$Level}{$Kind} = 1;
12637 }
12638 }
12639 $Content=~s!<nowrap>(.+?)</nowrap>!<span class='nowrap'>$1</span>!g;
12640 return $Content;
12641}
12642
12643sub get_Report_InterfaceProblems($$)
12644{
12645 my ($TargetPriority, $Level) = @_;
12646 my ($INTERFACE_PROBLEMS, %FuncHeaderLib, %SymbolChanges);
12647 foreach my $Interface (sort keys(%CompatProblems))
12648 {
12649 next if($Interface=~/\A([^\@\$\?]+)[\@\$]+/ and defined $CompatProblems{$1});
12650 foreach my $Kind (sort keys(%{$CompatProblems{$Interface}}))
12651 {
12652 if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols"
12653 and $Kind ne "Added_Interface" and $Kind ne "Removed_Interface")
12654 {
12655 my $HeaderName = $CompleteSignature{1}{$Interface}{"Header"};
12656 my $DyLib = $Symbol_Library{1}{$Interface};
12657 if(not $DyLib and my $VSym = $SymVer{1}{$Interface})
12658 { # Symbol with Version
12659 $DyLib = $Symbol_Library{1}{$VSym};
12660 }
12661 $FuncHeaderLib{$HeaderName}{$DyLib}{$Interface} = 1;
12662 %{$SymbolChanges{$Interface}{$Kind}} = %{$CompatProblems{$Interface}{$Kind}};
12663 foreach my $Location (sort keys(%{$SymbolChanges{$Interface}{$Kind}}))
12664 {
12665 my $Priority = getProblemSeverity($Level, $Kind);
12666 if($Priority ne $TargetPriority) {
12667 delete($SymbolChanges{$Interface}{$Kind}{$Location});
12668 }
12669 }
12670 if(not keys(%{$SymbolChanges{$Interface}{$Kind}})) {
12671 delete($SymbolChanges{$Interface}{$Kind});
12672 }
12673 if(not keys(%{$SymbolChanges{$Interface}})) {
12674 delete($SymbolChanges{$Interface});
12675 }
12676 }
12677 }
12678 }
12679 if($ReportFormat eq "xml")
12680 { # XML
12681 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%FuncHeaderLib))
12682 {
12683 $INTERFACE_PROBLEMS .= " <header name=\"$HeaderName\">\n";
12684 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$FuncHeaderLib{$HeaderName}}))
12685 {
12686 $INTERFACE_PROBLEMS .= " <library name=\"$DyLib\">\n";
12687 foreach my $Symbol (sort {lc($tr_name{$a}) cmp lc($tr_name{$b})} keys(%SymbolChanges))
12688 {
12689 $INTERFACE_PROBLEMS .= " <symbol name=\"$Symbol\">\n";
12690 foreach my $Kind (keys(%{$SymbolChanges{$Symbol}}))
12691 {
12692 foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
12693 {
12694 my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
12695 $Problem{"Param_Pos"} = numToStr($Problem{"Param_Pos"} + 1);
12696 $INTERFACE_PROBLEMS .= " <problem id=\"$Kind\">\n";
12697 my $Change = $CompatRules{$Level}{$Kind}{"Change"};
12698 $INTERFACE_PROBLEMS .= " <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
12699 my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
12700 $INTERFACE_PROBLEMS .= " <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
12701 my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
12702 $INTERFACE_PROBLEMS .= " <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
12703 $INTERFACE_PROBLEMS .= " </problem>\n";
12704 }
12705 }
12706 $INTERFACE_PROBLEMS .= " </symbol>\n";
12707 }
12708 $INTERFACE_PROBLEMS .= " </library>\n";
12709 }
12710 $INTERFACE_PROBLEMS .= " </header>\n";
12711 }
12712 $INTERFACE_PROBLEMS = "<problems_with_symbols severity=\"$TargetPriority\">\n".$INTERFACE_PROBLEMS."</problems_with_symbols>\n\n";
12713 }
12714 else
12715 { # HTML
12716 my $ProblemsNum = 0;
12717 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%FuncHeaderLib))
12718 {
12719 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$FuncHeaderLib{$HeaderName}}))
12720 {
12721 my ($HEADER_LIB_REPORT, %NameSpace_Interface, %NewSignature) = ();
12722 foreach my $Interface (keys(%{$FuncHeaderLib{$HeaderName}{$DyLib}})) {
12723 $NameSpace_Interface{get_IntNameSpace($Interface, 1)}{$Interface} = 1;
12724 }
12725 foreach my $NameSpace (sort keys(%NameSpace_Interface))
12726 {
12727 my $NS_REPORT = "";
12728 my @SortedInterfaces = sort {lc($tr_name{$a}) cmp lc($tr_name{$b})} keys(%{$NameSpace_Interface{$NameSpace}});
12729 foreach my $Interface (@SortedInterfaces)
12730 {
12731 my $Signature = get_Signature($Interface, 1);
12732 my $InterfaceProblemsReport = "";
12733 my $ProblemNum = 1;
12734 foreach my $Kind (keys(%{$SymbolChanges{$Interface}}))
12735 {
12736 foreach my $Location (sort keys(%{$SymbolChanges{$Interface}{$Kind}}))
12737 {
12738 my %Problem = %{$SymbolChanges{$Interface}{$Kind}{$Location}};
12739 $Problem{"Param_Pos"} = numToStr($Problem{"Param_Pos"} + 1);
12740 if($Problem{"New_Signature"}) {
12741 $NewSignature{$Interface} = $Problem{"New_Signature"};
12742 }
12743 if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
12744 {
12745 my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
12746 $InterfaceProblemsReport .= "<tr><th>$ProblemNum</th><td align='left' valign='top'>".$Change."</td><td align='left' valign='top'>".$Effect."</td></tr>\n";
12747 $ProblemNum += 1;
12748 $ProblemsNum += 1;
12749 }
12750 }
12751 }
12752 $ProblemNum -= 1;
12753 if($InterfaceProblemsReport)
12754 {
12755 if($Signature) {
12756 $NS_REPORT .= $ContentSpanStart."<span class='extendable'>[+]</span> ".highLight_Signature_Italic_Color($Signature)." ($ProblemNum)".$ContentSpanEnd."<br/>\n$ContentDivStart\n";
12757 if($Interface=~/\A(_Z|\?)/
12758 and not $NewSignature{$Interface}) {
12759 $NS_REPORT .= "<span class='mangled'>&#160;&#160;[ symbol: <b>$Interface</b> ]</span><br/>\n";
12760 }
12761 }
12762 else {
12763 $NS_REPORT .= $ContentSpanStart."<span class='extendable'>[+]</span> ".$Interface." ($ProblemNum)".$ContentSpanEnd."<br/>\n$ContentDivStart\n";
12764 }
12765 if($NewSignature{$Interface}) {# argument list changed to
12766 $NS_REPORT .= "\n<span class='new_signature_label'>changed to:</span><br/><span class='new_signature'>".highLight_Signature_Italic_Color($NewSignature{$Interface})."</span>\n";
12767 }
12768 $NS_REPORT .= "<table cellpadding='3' cellspacing='0' class='problems_table'><tr><th width='2%'></th><th width='47%'>Change</th><th>Effect</th></tr>$InterfaceProblemsReport</table><br/>$ContentDivEnd\n";
12769 $NS_REPORT = insertIDs($NS_REPORT);
12770 if($NameSpace) {
12771 $NS_REPORT=~s/(\W|\A)\Q$NameSpace\E\:\:(\w)/$1$2/g;
12772 }
12773 }
12774 }
12775 if($NS_REPORT) {
12776 $HEADER_LIB_REPORT .= (($NameSpace)?"<span class='ns_title'>namespace</span> <span class='ns'>$NameSpace</span>"."<br/>\n":"").$NS_REPORT;
12777 }
12778 }
12779 if($HEADER_LIB_REPORT)
12780 {
12781 my $FDyLib=$DyLib.($DyLib!~/\.\w+\Z/?" (.$LIB_EXT)":"");
12782 if($HeaderName and $DyLib) {
12783 $INTERFACE_PROBLEMS .= "<span class='h_name'>$HeaderName</span>, <span class='lib_name'>$FDyLib</span><br/>\n".$HEADER_LIB_REPORT."<br/>";
12784 }
12785 elsif($HeaderName) {
12786 $INTERFACE_PROBLEMS .= "<span class='h_name'>$HeaderName</span><br/>\n".$HEADER_LIB_REPORT."<br/>";
12787 }
12788 elsif($DyLib) {
12789 $INTERFACE_PROBLEMS .= "<span class='lib_name'>$FDyLib</span><br/>\n".$HEADER_LIB_REPORT."<br/>";
12790 }
12791 else {
12792 $INTERFACE_PROBLEMS .= $HEADER_LIB_REPORT."<br/>";
12793 }
12794 }
12795 }
12796 }
12797 if($INTERFACE_PROBLEMS)
12798 {
12799 my $Title = "Problems with Symbols, $TargetPriority Severity";
12800 my $Anchor = "Interface_Problems_$TargetPriority";
12801 if($TargetPriority eq "Safe")
12802 { # Safe Changes
12803 $Title = "Other Changes in Symbols";
12804 $Anchor = "Other_Changes_In_Symbols";
12805 }
12806 $INTERFACE_PROBLEMS = "<a name=\'$Anchor\'></a>\n<h2>$Title ($ProblemsNum)</h2><hr/>\n".$INTERFACE_PROBLEMS."<a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
12807 }
12808 }
12809 return $INTERFACE_PROBLEMS;
12810}
12811
12812sub get_Report_TypeProblems($$)
12813{
12814 my ($TargetPriority, $Level) = @_;
12815 my ($TYPE_PROBLEMS, %TypeHeader, %TypeChanges, %TypeType) = ();
12816 foreach my $Interface (sort keys(%CompatProblems))
12817 {
12818 foreach my $Kind (keys(%{$CompatProblems{$Interface}}))
12819 {
12820 if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
12821 {
12822 foreach my $Location (sort {cmp_locations($b, $a)} sort keys(%{$CompatProblems{$Interface}{$Kind}}))
12823 {
12824 my $TypeName = $CompatProblems{$Interface}{$Kind}{$Location}{"Type_Name"};
12825 my $Target = $CompatProblems{$Interface}{$Kind}{$Location}{"Target"};
12826 my $Priority = getProblemSeverity($Level, $Kind);
12827 if($Priority eq "Safe"
12828 and $TargetPriority ne "Safe") {
12829 next;
12830 }
12831 if(not $TypeType{$TypeName}
12832 or $TypeType{$TypeName} eq "Struct")
12833 { # register type of the type, select "class" if type has "class"- and "struct"-type changes
12834 $TypeType{$TypeName} = $CompatProblems{$Interface}{$Kind}{$Location}{"Type_Type"};
12835 }
12836 if(cmp_priority($Type_MaxPriority{$Level}{$TypeName}{$Kind}{$Target}, $Priority))
12837 { # select a problem with the highest priority
12838 next;
12839 }
12840 %{$TypeChanges{$TypeName}{$Kind}{$Location}} = %{$CompatProblems{$Interface}{$Kind}{$Location}};
12841 }
12842 }
12843 }
12844 }
12845 my %Kinds_Locations = ();
12846 foreach my $TypeName (keys(%TypeChanges))
12847 {
12848 my %Kinds_Target = ();
12849 foreach my $Kind (sort keys(%{$TypeChanges{$TypeName}}))
12850 {
12851 foreach my $Location (sort {cmp_locations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
12852 {
12853 my $Priority = getProblemSeverity($Level, $Kind);
12854 if($Priority ne $TargetPriority)
12855 { # other priority
12856 delete($TypeChanges{$TypeName}{$Kind}{$Location});
12857 next;
12858 }
12859 $Kinds_Locations{$TypeName}{$Kind}{$Location} = 1;
12860 my $Target = $TypeChanges{$TypeName}{$Kind}{$Location}{"Target"};
12861 if($Kinds_Target{$Kind}{$Target})
12862 { # duplicate target
12863 delete($TypeChanges{$TypeName}{$Kind}{$Location});
12864 next;
12865 }
12866 $Kinds_Target{$Kind}{$Target} = 1;
12867 my $HeaderName = get_TypeAttr($TName_Tid{1}{$TypeName}, 1, "Header");
12868 $TypeHeader{$HeaderName}{$TypeName} = 1;
12869 }
12870 if(not keys(%{$TypeChanges{$TypeName}{$Kind}})) {
12871 delete($TypeChanges{$TypeName}{$Kind});
12872 }
12873 }
12874 }
12875 if($ReportFormat eq "xml")
12876 { # XML
12877 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%TypeHeader))
12878 {
12879 $TYPE_PROBLEMS .= " <header name=\"$HeaderName\">\n";
12880 foreach my $TypeName (keys(%{$TypeHeader{$HeaderName}}))
12881 {
12882 $TYPE_PROBLEMS .= " <type name=\"".htmlSpecChars($TypeName)."\">\n";
12883 foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}}))
12884 {
12885 foreach my $Location (sort {cmp_locations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
12886 {
12887 my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}};
12888 $TYPE_PROBLEMS .= " <problem id=\"$Kind\">\n";
12889 my $Change = $CompatRules{$Level}{$Kind}{"Change"};
12890 $TYPE_PROBLEMS .= " <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
12891 my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
12892 $TYPE_PROBLEMS .= " <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
12893 my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
12894 $TYPE_PROBLEMS .= " <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
12895 $TYPE_PROBLEMS .= " </problem>\n";
12896 }
12897 }
12898 $TYPE_PROBLEMS .= getAffectedInterfaces($Level, $TypeName, $Kinds_Locations{$TypeName});
12899 if(grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) {
12900 $TYPE_PROBLEMS .= showVTables($TypeName);
12901 }
12902 $TYPE_PROBLEMS .= " </type>\n";
12903 }
12904 $TYPE_PROBLEMS .= " </header>\n";
12905 }
12906 $TYPE_PROBLEMS = "<problems_with_types severity=\"$TargetPriority\">\n".$TYPE_PROBLEMS."</problems_with_types>\n\n";
12907 }
12908 else
12909 { # HTML
12910 my $ProblemsNum = 0;
12911 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%TypeHeader))
12912 {
12913 my ($HEADER_REPORT, %NameSpace_Type) = ();
12914 foreach my $TypeName (keys(%{$TypeHeader{$HeaderName}})) {
12915 $NameSpace_Type{parse_TypeNameSpace($TypeName, 1)}{$TypeName} = 1;
12916 }
12917 foreach my $NameSpace (sort keys(%NameSpace_Type))
12918 {
12919 my $NS_REPORT = "";
12920 my @SortedTypes = sort {lc($TypeType{$a}." ".$a) cmp lc($TypeType{$b}." ".$b)} keys(%{$NameSpace_Type{$NameSpace}});
12921 foreach my $TypeName (@SortedTypes)
12922 {
12923 my $ProblemNum = 1;
12924 my $TYPE_REPORT = "";
12925 foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}}))
12926 {
12927 foreach my $Location (sort {cmp_locations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
12928 {
12929 my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}};
12930 if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
12931 {
12932 my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
12933 $TYPE_REPORT .= "<tr><th>$ProblemNum</th><td align='left' valign='top'>".$Change."</td><td align='left' valign='top'>$Effect</td></tr>\n";
12934 $ProblemNum += 1;
12935 $ProblemsNum += 1;
12936 }
12937 }
12938 }
12939 $ProblemNum -= 1;
12940 if($TYPE_REPORT)
12941 {
12942 my $Affected = getAffectedInterfaces($Level, $TypeName, $Kinds_Locations{$TypeName});
12943 my $ShowVTables = "";
12944 if(grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) {
12945 $ShowVTables = showVTables($TypeName);
12946 }
12947 $NS_REPORT .= $ContentSpanStart."<span class='extendable'>[+]</span> <span class='ttype'>".lc($TypeType{$TypeName})."</span> ".htmlSpecChars($TypeName)." ($ProblemNum)".$ContentSpanEnd;
12948 $NS_REPORT .= "<br/>\n".$ContentDivStart."<table cellpadding='3' cellspacing='0' class='problems_table'><tr>\n";
12949 $NS_REPORT .= "<th width='2%'></th><th width='47%'>Change</th>\n";
12950 $NS_REPORT .= "<th>Effect</th></tr>".$TYPE_REPORT."</table>\n";
12951 $NS_REPORT .= $ShowVTables.$Affected."<br/><br/>".$ContentDivEnd."\n";
12952 $NS_REPORT = insertIDs($NS_REPORT);
12953 if($NameSpace) {
12954 $NS_REPORT=~s/(\W|\A)\Q$NameSpace\E\:\:(\w|\~)/$1$2/g;
12955 }
12956 }
12957 }
12958 if($NS_REPORT) {
12959 $HEADER_REPORT .= (($NameSpace)?"<span class='ns_title'>namespace</span> <span class='ns'>$NameSpace</span>"."<br/>\n":"").$NS_REPORT;
12960 }
12961 }
12962 if($HEADER_REPORT) {
12963 $TYPE_PROBLEMS .= "<span class='h_name'>$HeaderName</span><br/>\n".$HEADER_REPORT."<br/>";
12964 }
12965 }
12966 if($TYPE_PROBLEMS)
12967 {
12968 my $Title = "Problems with Data Types, $TargetPriority Severity";
12969 my $Anchor = "Type_Problems_$TargetPriority";
12970 if($TargetPriority eq "Safe")
12971 { # Safe Changes
12972 $Title = "Other Changes in Data Types";
12973 $Anchor = "Other_Changes_In_Types";
12974 }
12975 $TYPE_PROBLEMS = "<a name=\'$Anchor\'></a>\n<h2>$Title ($ProblemsNum)</h2><hr/>\n".$TYPE_PROBLEMS."<a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
12976 }
12977 }
12978 return $TYPE_PROBLEMS;
12979}
12980
12981sub showVTables($)
12982{
12983 my $TypeName = $_[0];
12984 my $TypeId1 = $TName_Tid{1}{$TypeName};
12985 my %Type1 = get_Type($Tid_TDid{1}{$TypeId1}, $TypeId1, 1);
12986 if(defined $Type1{"VTable"}
12987 and keys(%{$Type1{"VTable"}}))
12988 {
12989 my $TypeId2 = $TName_Tid{2}{$TypeName};
12990 my %Type2 = get_Type($Tid_TDid{2}{$TypeId2}, $TypeId2, 2);
12991 if(defined $Type2{"VTable"}
12992 and keys(%{$Type2{"VTable"}}))
12993 {
12994 my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
12995 my %Entries = ();
12996 foreach (sort {int($a)<=>int($b)} (keys(%Indexes)))
12997 {
12998 $Entries{$_}{"E1"} = simpleVEntry($Type1{"VTable"}{$_});
12999 $Entries{$_}{"E2"} = simpleVEntry($Type2{"VTable"}{$_});
13000 }
13001 my $VTABLES = "";
13002 if($ReportFormat eq "xml")
13003 { # XML
13004 $VTABLES .= " <vtable>\n";
13005 foreach (sort {int($a)<=>int($b)} (keys(%Entries)))
13006 {
13007 $VTABLES .= " <entry offset=\"$_\">\n";
13008 $VTABLES .= " <old>".htmlSpecChars($Entries{$_}{"E1"})."</old>\n";
13009 $VTABLES .= " <new>".htmlSpecChars($Entries{$_}{"E2"})."</new>\n";
13010 $VTABLES .= " </entry>\n";
13011 }
13012 $VTABLES .= " </vtable>\n\n";
13013 }
13014 else
13015 { # HTML
13016 $VTABLES .= "<table cellpadding='3' cellspacing='0' class='virtual_table'>";
13017 $VTABLES .= "<tr><th width='2%'>Offset</th>";
13018 $VTABLES .= "<th width='45%'>Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries</th>";
13019 $VTABLES .= "<th>Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries</th></tr>";
13020 foreach (sort {int($a)<=>int($b)} (keys(%Entries)))
13021 {
13022 my ($Color1, $Color2) = ("", "");
13023 if($Entries{$_}{"E1"} ne $Entries{$_}{"E2"})
13024 {
13025 if($Entries{$_}{"E1"})
13026 {
13027 $Color1 = " class='vtable_red'";
13028 $Color2 = " class='vtable_red'";
13029 }
13030 else {
13031 $Color2 = " class='vtable_yellow'";
13032 }
13033 }
13034 $VTABLES .= "<tr><th>$_</th>\n";
13035 $VTABLES .= "<td$Color1>".htmlSpecChars($Entries{$_}{"E1"})."</td>\n";
13036 $VTABLES .= "<td$Color2>".htmlSpecChars($Entries{$_}{"E2"})."</td></tr>\n";
13037 }
13038 $VTABLES .= "</table><br/>\n";
13039 $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd;
13040 $VTABLES = "<span style='padding-left:15px;'>".$ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."</span><br/>\n".$VTABLES;
13041 }
13042 return $VTABLES;
13043 }
13044 }
13045 return "";
13046}
13047
13048sub simpleVEntry($)
13049{
13050 my $VEntry = $_[0];
13051 $VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks
13052 $VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo
13053 if($VEntry=~/\A_ZThn.+\Z/) {
13054 $VEntry = "non-virtual thunk";
13055 }
13056 $VEntry=~s/\A\(int \(\*\)\(...\)\)([^\(\d])/$1/i;
13057 # support for old GCC versions
13058 $VEntry=~s/\A0u\Z/(int (*)(...))0/;
13059 $VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/;
13060 $VEntry=~s/\A&_Z\Z/& _Z/;
13061 # templates
13062 if($VEntry=~s/ \[with (\w+) = (.+?)(, [^=]+ = .+|])\Z//g)
13063 { # std::basic_streambuf<_CharT, _Traits>::imbue [with _CharT = char, _Traits = std::char_traits<char>]
13064 # become std::basic_streambuf<char, ...>::imbue
13065 my ($Pname, $Pval) = ($1, $2);
13066 if($Pname eq "_CharT" and $VEntry=~/\Astd::/)
13067 { # stdc++ typedefs
13068 $VEntry=~s/<$Pname(, [^<>]+|)>/<$Pval>/g;
13069 # FIXME: simplify names using stdcxx typedefs (StdCxxTypedef)
13070 # The typedef info should be added to ABI dumps
13071 }
13072 else
13073 {
13074 $VEntry=~s/<$Pname>/<$Pval>/g;
13075 $VEntry=~s/<$Pname, [^<>]+>/<$Pval, ...>/g;
13076 }
13077 }
13078 $VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors
13079 return $VEntry;
13080}
13081
13082sub getAffectedInterfaces($$$)
13083{
13084 my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
13085 my (%INumber, %IProblems) = ();
13086 my $LIMIT = 1000;
13087 foreach my $Interface (sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%CompatProblems))
13088 {
13089 last if(keys(%INumber)>$LIMIT);
13090 if(($Interface=~/C2E|D2E|D0E/))
13091 { # duplicated problems for C2 constructors, D2 and D0 destructors
13092 next;
13093 }
13094 my ($MinPath_Length, $ProblemLocation_Last) = ();
13095 my $MaxPriority = 0;
13096 my $Signature = get_Signature($Interface, 1);
13097 foreach my $Kind (keys(%{$CompatProblems{$Interface}}))
13098 {
13099 foreach my $Location (keys(%{$CompatProblems{$Interface}{$Kind}}))
13100 {
13101 if(not defined $Kinds_Locations->{$Kind}
13102 or not $Kinds_Locations->{$Kind}{$Location}) {
13103 next;
13104 }
13105 if($Interface=~/\A([^\@\$\?]+)[\@\$]+/ and defined $CompatProblems{$1}
13106 and defined $CompatProblems{$1}{$Kind}{$Location})
13107 { # duplicated problems for versioned symbols
13108 next;
13109 }
13110 my $Type_Name = $CompatProblems{$Interface}{$Kind}{$Location}{"Type_Name"};
13111 next if($Type_Name ne $Target_TypeName);
13112
13113 my $Position = $CompatProblems{$Interface}{$Kind}{$Location}{"Param_Pos"};
13114 my $Param_Name = $CompatProblems{$Interface}{$Kind}{$Location}{"Param_Name"};
13115 my $Priority = $CompatProblems{$Interface}{$Kind}{$Location}{"Priority"};
13116 $INumber{$Interface} = 1;
13117 my $Path_Length = 0;
13118 my $ProblemLocation = $Location;
13119 if($Type_Name) {
13120 $ProblemLocation=~s/->\Q$Type_Name\E\Z//g;
13121 }
13122 while($ProblemLocation=~/\-\>/g){$Path_Length += 1;}
13123 if($MinPath_Length eq "" or ($Path_Length<=$MinPath_Length and $Priority_Value{$Priority}>$MaxPriority)
13124 or (cmp_locations($ProblemLocation, $ProblemLocation_Last) and $Priority_Value{$Priority}==$MaxPriority))
13125 {
13126 $MinPath_Length = $Path_Length;
13127 $MaxPriority = $Priority_Value{$Priority};
13128 $ProblemLocation_Last = $ProblemLocation;
13129 %{$IProblems{$Interface}} = (
13130 "Description"=>getAffectDescription($Level, $Interface, $Kind, $Location),
13131 "Priority"=>$MaxPriority,
13132 "Signature"=>$Signature,
13133 "Position"=>$Position,
13134 "Param_Name"=>$Param_Name,
13135 "Location"=>$Location
13136 );
13137 }
13138 }
13139 }
13140 }
13141 my @Interfaces = keys(%IProblems);
13142 @Interfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} @Interfaces;
13143 @Interfaces = sort {$IProblems{$b}{"Priority"}<=>$IProblems{$a}{"Priority"}} @Interfaces;
13144 my $Affected = "";
13145 if($ReportFormat eq "xml")
13146 { # XML
13147 $Affected .= " <affected>\n";
13148 foreach my $Interface (@Interfaces)
13149 {
13150 my $Param_Name = $IProblems{$Interface}{"Param_Name"};
13151 my $Description = $IProblems{$Interface}{"Description"};
13152 my $Location = $IProblems{$Interface}{"Location"};
13153 my $Target = "";
13154 if($Param_Name) {
13155 $Target = " affected=\"param\" param_name=\"$Param_Name\"";
13156 }
13157 elsif($Location=~/\Aretval(\-|\Z)/i) {
13158 $Target = " affected=\"retval\"";
13159 }
13160 elsif($Location=~/\Athis(\-|\Z)/i) {
13161 $Target = " affected=\"this\"";
13162 }
13163 $Affected .= " <symbol$Target name=\"$Interface\">\n";
13164 $Affected .= " <comment>".htmlSpecChars($Description)."</comment>\n";
13165 $Affected .= " </symbol>\n";
13166 }
13167 $Affected .= " </affected>\n";
13168 }
13169 else
13170 { # HTML
13171 foreach my $Interface (@Interfaces)
13172 {
13173 my $Description = $IProblems{$Interface}{"Description"};
13174 my $Signature = $IProblems{$Interface}{"Signature"};
13175 my $Pos = $IProblems{$Interface}{"Position"};
13176 $Affected .= "<span class='iname_b'>".highLight_Signature_PPos_Italic($Signature, $Pos, 1, 0, 0)."</span><br/>"."<div class='affect'>".htmlSpecChars($Description)."</div>\n";
13177 }
13178 $Affected = "<div class='affected'>".$Affected."</div>";
13179 if(keys(%INumber)>$LIMIT) {
13180 $Affected .= "and others ...<br/>";
13181 }
13182 if($Affected)
13183 {
13184 $Affected = $ContentDivStart.$Affected.$ContentDivEnd;
13185 my $AHeader = $ContentSpanStart_Affected."[+] affected symbols (".(keys(%INumber)>$LIMIT?"more than $LIMIT":keys(%INumber)).")".$ContentSpanEnd;
13186 $AHeader = "<span style='padding-left:15px'>".$AHeader."</span>";
13187 $Affected = $AHeader.$Affected;
13188 }
13189 }
13190 return $Affected;
13191}
13192
13193sub cmp_locations($$)
13194{
13195 my ($Location1, $Location2) = @_;
13196 if($Location2=~/(\A|\W)(retval|this)(\W|\Z)/
13197 and $Location1!~/(\A|\W)(retval|this)(\W|\Z)/ and $Location1!~/\-\>/) {
13198 return 1;
13199 }
13200 if($Location2=~/(\A|\W)(retval|this)(\W|\Z)/ and $Location2=~/\-\>/
13201 and $Location1!~/(\A|\W)(retval|this)(\W|\Z)/ and $Location1=~/\-\>/) {
13202 return 1;
13203 }
13204 return 0;
13205}
13206
13207sub getAffectDescription($$$$)
13208{
13209 my ($Level, $Interface, $Kind, $Location) = @_;
13210 my %Problem = %{$CompatProblems{$Interface}{$Kind}{$Location}};
13211 my $PPos = numToStr($Problem{"Param_Pos"} + 1);
13212 my @Sentence = ();
13213 $Location=~s/\A(.*)\-\>.+?\Z/$1/;
13214 if($Kind eq "Overridden_Virtual_Method"
13215 or $Kind eq "Overridden_Virtual_Method_B") {
13216 push(@Sentence, "The method '".$Problem{"New_Value"}."' will be called instead of this method.");
13217 }
13218 elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
13219 {
13220 if($Location eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/)
13221 {
13222 my $METHOD_TYPE = $CompleteSignature{1}{$Interface}{"Constructor"}?"constructor":"method";
13223 my $ClassName = get_TypeName($CompleteSignature{1}{$Interface}{"Class"}, 1);
13224 if($ClassName eq $Problem{"Type_Name"}) {
13225 push(@Sentence, "This $METHOD_TYPE is from \'".$Problem{"Type_Name"}."\' class.");
13226 }
13227 else {
13228 push(@Sentence, "This $METHOD_TYPE is from derived class \'".$ClassName."\'.");
13229 }
13230 }
13231 else
13232 {
13233 if($Location=~/retval/)
13234 { # return value
13235 if($Location=~/\-\>/) {
13236 push(@Sentence, "Field \'".$Location."\' in return value");
13237 }
13238 else {
13239 push(@Sentence, "Return value");
13240 }
13241 if($Problem{"InitialType_Type"} eq "Pointer") {
13242 push(@Sentence, "(pointer)");
13243 }
13244 elsif($Problem{"InitialType_Type"} eq "Ref") {
13245 push(@Sentence, "(reference)");
13246 }
13247 }
13248 elsif($Location=~/this/)
13249 { # "this" pointer
13250 if($Location=~/\-\>/) {
13251 push(@Sentence, "Field \'".$Location."\' in the object of this method");
13252 }
13253 else {
13254 push(@Sentence, "\'this\' pointer");
13255 }
13256 }
13257 else
13258 { # parameters
13259 if($Location=~/\-\>/) {
13260 push(@Sentence, "Field \'".$Location."\' in $PPos parameter");
13261 }
13262 else {
13263 push(@Sentence, "$PPos parameter");
13264 }
13265 if($Problem{"Param_Name"}) {
13266 push(@Sentence, "\'".$Problem{"Param_Name"}."\'");
13267 }
13268 if($Problem{"InitialType_Type"} eq "Pointer") {
13269 push(@Sentence, "(pointer)");
13270 }
13271 elsif($Problem{"InitialType_Type"} eq "Ref") {
13272 push(@Sentence, "(reference)");
13273 }
13274 }
13275 if($Location eq "this") {
13276 push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
13277 }
13278 elsif($Problem{"Start_Type_Name"} eq $Problem{"Type_Name"}) {
13279 push(@Sentence, "has type \'".$Problem{"Type_Name"}."\'.");
13280 }
13281 else {
13282 push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
13283 }
13284 }
13285 }
13286 if($ExtendedFuncs{$Interface}) {
13287 push(@Sentence, " This is a symbol from an artificial external library that may use the \'$TargetLibraryName\' library and change its ABI after recompiling.");
13288 }
13289 return join(" ", @Sentence);
13290}
13291
13292sub get_XmlSign($$)
13293{
13294 my ($Symbol, $LibVersion) = @_;
13295 my $Info = $CompleteSignature{$LibVersion}{$Symbol};
13296 my $Report = "";
13297 foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Info->{"Param"}}))
13298 {
13299 my $Name = $Info->{"Param"}{$Pos}{"name"};
13300 my $TypeName = get_TypeName($Info->{"Param"}{$Pos}{"type"}, $LibVersion);
13301 foreach my $Typedef (keys(%ChangedTypedef))
13302 {
13303 my $Base = $Typedef_BaseName{$LibVersion}{$Typedef};
13304 $TypeName=~s/(\A|\W)\Q$Typedef\E(\W|\Z)/$1$Base$2/g;
13305 }
13306 $Report .= " <param pos=\"$Pos\">\n";
13307 $Report .= " <name>".$Name."</name>\n";
13308 $Report .= " <type>".htmlSpecChars($TypeName)."</type>\n";
13309 $Report .= " </param>\n";
13310 }
13311 if(my $Return = $Info->{"Return"})
13312 {
13313 my $RTName = get_TypeName($Return, $LibVersion);
13314 $Report .= " <retval>\n";
13315 $Report .= " <type>".htmlSpecChars($RTName)."</type>\n";
13316 $Report .= " </retval>\n";
13317 }
13318 return $Report;
13319}
13320
13321sub get_Report_SymbolsInfo()
13322{
13323 my $Report = "<symbols_info>\n";
13324 foreach my $Symbol (sort keys(%CompatProblems))
13325 {
13326 if($Symbol=~/\A([^\@\$\?]+)[\@\$]+/
13327 and defined $CompatProblems{$1}) {
13328 next;
13329 }
13330 $Report .= " <symbol name=\"$Symbol\">\n";
13331 my ($S1, $P1, $S2, $P2) = ();
13332 if(not $AddedInt{$Symbol})
13333 {
13334 if(defined $CompleteSignature{1}{$Symbol}
13335 and defined $CompleteSignature{1}{$Symbol}{"Header"})
13336 {
13337 $P1 = get_XmlSign($Symbol, 1);
13338 $S1 = get_Signature($Symbol, 1);
13339 }
13340 elsif($Symbol=~/\A(_Z|\?)/) {
13341 $S1 = $tr_name{$Symbol};
13342 }
13343 }
13344 if(not $RemovedInt{$Symbol})
13345 {
13346 if(defined $CompleteSignature{2}{$Symbol}
13347 and defined $CompleteSignature{2}{$Symbol}{"Header"})
13348 {
13349 $P2 = get_XmlSign($Symbol, 2);
13350 $S2 = get_Signature($Symbol, 2);
13351 }
13352 elsif($Symbol=~/\A(_Z|\?)/) {
13353 $S2 = $tr_name{$Symbol};
13354 }
13355 }
13356 if($S1)
13357 {
13358 $Report .= " <old signature=\"".htmlSpecChars($S1)."\">\n";
13359 $Report .= $P1;
13360 $Report .= " </old>\n";
13361 }
13362 if($S2 and $S2 ne $S1)
13363 {
13364 $Report .= " <new signature=\"".htmlSpecChars($S2)."\">\n";
13365 $Report .= $P2;
13366 $Report .= " </new>\n";
13367 }
13368 $Report .= " </symbol>\n";
13369 }
13370 $Report .= "</symbols_info>\n";
13371 return $Report;
13372}
13373
13374sub createHtmlReport($)
13375{
13376 my $Path = $_[0];
13377 my $Report = "";
13378 if($ReportFormat eq "xml")
13379 { # XML
13380 $Report .= "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
13381 $Report .= "<report version=\"$XML_REPORT_VERSION\">\n\n";
13382 my ($Summary, $MetaData) = get_Summary("Binary");
13383 $Report .= $Summary."\n";
13384 $Report .= get_Report_Added("Binary").get_Report_Removed("Binary");
13385 $Report .= get_Report_Problems("High", "Binary").get_Report_Problems("Medium", "Binary").get_Report_Problems("Low", "Binary").get_Report_Problems("Safe", "Binary");
13386 $Report .= get_Report_SymbolsInfo();
13387 $Report .= "</report>\n";
13388 }
13389 else
13390 { # HTML
13391 my $CssStyles = readStyles("CompatReport.css");
13392 my ($Summary, $MetaData) = get_Summary("Binary");
13393 my $Title = "$TargetLibraryFName: ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." binary compatibility report";
13394 my $Keywords = "$TargetLibraryFName, binary compatibility, API, report";
13395 my $Description = "Binary compatibility report for the $TargetLibraryFName $TargetComponent between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
13396 if(getArch(1) eq getArch(2)
13397 and getArch(1) ne "unknown") {
13398 $Description .= " on ".showArch(getArch(1));
13399 }
13400 $Report .= "<!-\- $MetaData -\->\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles."\n".$JScripts)."\n<body>\n<div><a name='Top'></a>\n";
13401 $Report .= get_Report_Header()."\n".$Summary."\n";
13402 $Report .= get_Report_Added("Binary").get_Report_Removed("Binary");
13403 $Report .= get_Report_Problems("High", "Binary").get_Report_Problems("Medium", "Binary").get_Report_Problems("Low", "Binary").get_Report_Problems("Safe", "Binary");
13404 $Report .= get_SourceInfo();
13405 $Report .= "</div>\n<br/><br/><br/><hr/>\n";
13406 $Report .= getReportFooter($TargetLibraryFName);
13407 $Report .= "\n<div style='height:999px;'></div>\n</body></html>";
13408 }
13409 if($StdOut)
13410 { # --stdout option
13411 print STDOUT $Report;
13412 }
13413 else {
13414 writeFile($Path, $Report);
13415 }
13416}
13417
13418sub getReportFooter($)
13419{
13420 my $LibName = $_[0];
13421 my $Footer = "<div style='width:100%;font-size:11px;' align='right'><i>Generated on ".(localtime time); # report date
13422 $Footer .= " for <span style='font-weight:bold'>$LibName</span>"; # tested library/system name
13423 $Footer .= " by <a href='".$HomePage{"Dev"}."'>ABI Compliance Checker</a>"; # tool name
13424 my $ToolSummary = "<br/>A tool for checking backward binary compatibility of a shared C/C++ library API&#160;&#160;";
13425 $Footer .= " $TOOL_VERSION &#160;$ToolSummary</i></div>"; # tool version
13426 return $Footer;
13427}
13428
13429sub get_Report_Problems($$)
13430{
13431 my ($Priority, $Level) = @_;
13432 my $Report = get_Report_TypeProblems($Priority, $Level).get_Report_InterfaceProblems($Priority, $Level);
13433 if($Priority eq "Low")
13434 {
13435 $Report .= get_Report_ChangedConstants();
13436 if($ReportFormat eq "html") {
13437 if($CheckImpl and $Level eq "Binary") {
13438 $Report .= get_Report_Impl();
13439 }
13440 }
13441 }
13442 if($ReportFormat eq "html")
13443 {
13444 if($Report)
13445 { # add anchor
13446 if($Priority eq "Safe") {
13447 $Report = "<a name=\'Other_Changes\'></a>".$Report;
13448 }
13449 else {
13450 $Report = "<a name=\'".$Priority."_Risk_Problems\'></a>".$Report;
13451 }
13452 }
13453 }
13454 return $Report;
13455}
13456
13457sub composeHTML_Head($$$$)
13458{
13459 my ($Title, $Keywords, $Description, $OtherInHead) = @_;
13460 return "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>
13461 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />
13462 <meta name=\"keywords\" content=\"$Keywords\" />
13463 <meta name=\"description\" content=\"$Description\" />
13464 <title>\n $Title\n </title>\n$OtherInHead\n</head>";
13465}
13466
13467sub insertIDs($)
13468{
13469 my $Text = $_[0];
13470 while($Text=~/CONTENT_ID/)
13471 {
13472 if(int($Content_Counter)%2) {
13473 $ContentID -= 1;
13474 }
13475 $Text=~s/CONTENT_ID/c_$ContentID/;
13476 $ContentID += 1;
13477 $Content_Counter += 1;
13478 }
13479 return $Text;
13480}
13481
13482sub checkPreprocessedUnit($)
13483{
13484 my $Path = $_[0];
13485 my $CurHeader = "";
13486 open(PREPROC, "$Path") || die ("can't open file \'$Path\': $!\n");
13487 while(<PREPROC>)
13488 {# detecting public and private constants
13489 next if(not /\A#/);
13490 chomp($_);
13491 if(/#[ \t]+\d+[ \t]+\"(.+)\"/) {
13492 $CurHeader=path_format($1, $OSgroup);
13493 }
13494 if(not $Include_Neighbors{$Version}{get_filename($CurHeader)}
13495 and not $Registered_Headers{$Version}{$CurHeader})
13496 { # not a target
13497 next;
13498 }
13499 if(not is_target_header(get_filename($CurHeader)))
13500 { # user-defined header
13501 next;
13502 }
13503 if(/\#[ \t]*define[ \t]+([_A-Z0-9]+)[ \t]+(.+)[ \t]*\Z/)
13504 {
13505 my ($Name, $Value) = ($1, $2);
13506 if(not $Constants{$Version}{$Name}{"Access"})
13507 {
13508 $Constants{$Version}{$Name}{"Access"} = "public";
13509 $Constants{$Version}{$Name}{"Value"} = $Value;
13510 $Constants{$Version}{$Name}{"Header"} = get_filename($CurHeader);
13511 }
13512 }
13513 elsif(/\#[ \t]*undef[ \t]+([_A-Z]+)[ \t]*/) {
13514 $Constants{$Version}{$1}{"Access"} = "private";
13515 }
13516 }
13517 close(PREPROC);
13518 foreach my $Constant (keys(%{$Constants{$Version}}))
13519 {
13520 if($Constants{$Version}{$Constant}{"Access"} eq "private" or $Constant=~/_h\Z/i
13521 or isBuiltIn($Constants{$Version}{$Constant}{"Header"}))
13522 { # skip private constants
13523 delete($Constants{$Version}{$Constant});
13524 }
13525 else {
13526 delete($Constants{$Version}{$Constant}{"Access"});
13527 }
13528 }
13529}
13530
13531my %IgnoreConstant=(
13532 "VERSION"=>1,
13533 "VERSIONCODE"=>1,
13534 "VERNUM"=>1,
13535 "VERS_INFO"=>1,
13536 "PATCHLEVEL"=>1,
13537 "INSTALLPREFIX"=>1,
13538 "VBUILD"=>1,
13539 "VPATCH"=>1,
13540 "VMINOR"=>1,
13541 "BUILD_STRING"=>1,
13542 "BUILD_TIME"=>1,
13543 "PACKAGE_STRING"=>1,
13544 "PRODUCTION"=>1,
13545 "CONFIGURE_COMMAND"=>1,
13546 "INSTALLDIR"=>1,
13547 "BINDIR"=>1,
13548 "CONFIG_FILE_PATH"=>1,
13549 "DATADIR"=>1,
13550 "EXTENSION_DIR"=>1,
13551 "INCLUDE_PATH"=>1,
13552 "LIBDIR"=>1,
13553 "LOCALSTATEDIR"=>1,
13554 "SBINDIR"=>1,
13555 "SYSCONFDIR"=>1,
13556 "RELEASE"=>1,
13557 "SOURCE_ID"=>1,
13558 "SUBMINOR"=>1,
13559 "MINOR"=>1,
13560 "MINNOR"=>1,
13561 "MINORVERSION"=>1,
13562 "MAJOR"=>1,
13563 "MAJORVERSION"=>1,
13564 "MICRO"=>1,
13565 "MICROVERSION"=>1,
13566 "BINARY_AGE"=>1,
13567 "INTERFACE_AGE"=>1,
13568 "CORE_ABI"=>1,
13569 "PATCH"=>1,
13570 "COPYRIGHT"=>1,
13571 "TIMESTAMP"=>1,
13572 "REVISION"=>1,
13573 "PACKAGE_TAG"=>1,
13574 "PACKAGEDATE"=>1,
13575 "NUMVERSION"=>1
13576);
13577
13578sub mergeConstants()
13579{
13580 foreach my $Constant (keys(%{$Constants{1}}))
13581 {
13582 if($SkipConstants{1}{$Constant})
13583 { # skipped by the user
13584 next;
13585 }
13586 if($Constants{2}{$Constant}{"Value"} eq "")
13587 { # empty value
13588 next;
13589 }
13590 if(not is_target_header($Constants{1}{$Constant}{"Header"}))
13591 { # user-defined header
13592 next;
13593 }
13594 my ($Old_Value, $New_Value, $Old_Value_Pure, $New_Value_Pure);
13595 $Old_Value = $Old_Value_Pure = uncover_constant(1, $Constant);
13596 $New_Value = $New_Value_Pure = uncover_constant(2, $Constant);
13597 $Old_Value_Pure=~s/(\W)\s+/$1/g;
13598 $Old_Value_Pure=~s/\s+(\W)/$1/g;
13599 $New_Value_Pure=~s/(\W)\s+/$1/g;
13600 $New_Value_Pure=~s/\s+(\W)/$1/g;
13601 next if($New_Value_Pure eq "" or $Old_Value_Pure eq "");
13602 if($New_Value_Pure ne $Old_Value_Pure)
13603 { # different values
13604 if(grep {$Constant=~/(\A|_)$_(_|\Z)/} keys(%IgnoreConstant))
13605 { # ignore library version
13606 next;
13607 }
13608 if($Constant=~/(\A|_)(lib|open|)$TargetLibraryShortName(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i)
13609 { # ignore library version
13610 next;
13611 }
13612 if($Old_Value=~/\A('|"|)[\/\\]\w+([\/\\]|:|('|"|)\Z)/ or $Old_Value=~/[\/\\]\w+[\/\\]\w+/)
13613 { # ignoring path defines:
13614 # /lib64:/usr/lib64:/lib:/usr/lib:/usr/X11R6/lib/Xaw3d ...
13615 next;
13616 }
13617 if($Old_Value=~/\A\(*[a-z_]+(\s+|\|)/i)
13618 { # ignore source defines:
13619 # static int gcry_pth_init ( void) { return ...
13620 # (RE_BACKSLASH_ESCAPE_IN_LISTS | RE...
13621 next;
13622 }
13623 if(convert_integer($Old_Value) eq convert_integer($New_Value))
13624 { # 0x0001 and 0x1, 0x1 and 1 equal constants
13625 next;
13626 }
13627 if($Old_Value eq "0" and $New_Value eq "NULL")
13628 { # 0 => NULL
13629 next;
13630 }
13631 if($Old_Value eq "NULL" and $New_Value eq "0")
13632 { # NULL => 0
13633 next;
13634 }
13635 %{$ProblemsWithConstants{$Constant}} = (
13636 "Target"=>$Constant,
13637 "Old_Value"=>$Old_Value,
13638 "New_Value"=>$New_Value );
13639 }
13640 }
13641}
13642
13643sub convert_integer($)
13644{
13645 my $Value = $_[0];
13646 if($Value=~/\A0x[a-f0-9]+\Z/)
13647 {# hexadecimal
13648 return hex($Value);
13649 }
13650 elsif($Value=~/\A0[0-7]+\Z/)
13651 {# octal
13652 return oct($Value);
13653 }
13654 elsif($Value=~/\A0b[0-1]+\Z/)
13655 {# binary
13656 return oct($Value);
13657 }
13658 else {
13659 return $Value;
13660 }
13661}
13662
13663sub uncover_constant($$)
13664{
13665 my ($LibVersion, $Constant) = @_;
13666 return "" if(not $LibVersion or not $Constant);
13667 return $Constant if(isCyclical(\@RecurConstant, $Constant));
13668 if($Cache{"uncover_constant"}{$LibVersion}{$Constant} ne "") {
13669 return $Cache{"uncover_constant"}{$LibVersion}{$Constant};
13670 }
13671 my $Value = $Constants{$LibVersion}{$Constant}{"Value"};
13672 if($Value=~/\A[A-Z0-9_]+\Z/ and $Value=~/[A-Z]/)
13673 {
13674 push(@RecurConstant, $Constant);
13675 if((my $Uncovered = uncover_constant($LibVersion, $Value)) ne "") {
13676 $Value = $Uncovered;
13677 }
13678 pop(@RecurConstant);
13679 }
13680 # FIXME: uncover $Value using all the enum constants
13681 # USECASE: change of define NC_LONG from NC_INT (enum value) to NC_INT (define)
13682 $Cache{"uncover_constant"}{$LibVersion}{$Constant} = $Value;
13683 return $Value;
13684}
13685
13686sub getSymbols($)
13687{
13688 my $LibVersion = $_[0];
13689 my @DyLibPaths = getSoPaths($LibVersion);
13690 if($#DyLibPaths==-1 and not $CheckHeadersOnly)
13691 {
13692 if($LibVersion==1)
13693 {
13694 printMsg("WARNING", "checking headers only");
13695 $CheckHeadersOnly = 1;
13696 }
13697 else {
13698 exitStatus("Error", "$SLIB_TYPE libraries are not found in ".$Descriptor{$LibVersion}{"Version"});
13699 }
13700 }
13701 my %GroupNames = map {parse_libname(get_filename($_), "name+ext", $OStarget)=>1} @DyLibPaths;
13702 foreach my $DyLibPath (sort {length($a)<=>length($b)} @DyLibPaths) {
13703 getSymbols_Lib($LibVersion, $DyLibPath, 0, \%GroupNames, "+Weak");
13704 }
13705}
13706
13707sub get_VTableSymbolSize($$)
13708{
13709 my ($ClassName, $LibVersion) = @_;
13710 return 0 if(not $ClassName);
13711 my $Symbol = $ClassVTable{$LibVersion}{$ClassName};
13712 if(defined $Symbol_Library{$LibVersion}{$Symbol}
13713 and my $DyLib = $Symbol_Library{$LibVersion}{$Symbol})
13714 { # bind class name and v-table size
13715 if(defined $Library_Symbol{$LibVersion}{$DyLib}{$Symbol}
13716 and my $Size = -$Library_Symbol{$LibVersion}{$DyLib}{$Symbol})
13717 { # size from the shared library
13718 if($Size>=12) {
13719# 0 (int (*)(...))0
13720# 4 (int (*)(...))(& _ZTIN7mysqlpp8DateTimeE)
13721# 8 mysqlpp::DateTime::~DateTime
13722 return $Size;
13723 }
13724 else {
13725 return 0;
13726 }
13727 }
13728 }
13729}
13730
13731sub canonifyName($)
13732{ # make TIFFStreamOpen(char const*, std::basic_ostream<char, std::char_traits<char> >*)
13733 # to be TIFFStreamOpen(char const*, std::basic_ostream<char>*)
13734 my $Name = $_[0];
13735 my $Rem = "std::(allocator|less|char_traits|regex_traits)";
13736 if($Name=~/([^<>,]+),\s*$Rem<([^<>,]+)>\s*/)
13737 {
13738 if($1 eq $3)
13739 {
13740 my $P = $1;
13741 while($Name=~s/\Q$P\E,\s*$Rem<\Q$P\E>\s*/$P/g){};
13742 }
13743 }
13744 return $Name;
13745}
13746
13747sub translateSymbols(@_$)
13748{
13749 my $LibVersion = pop(@_);
13750 my (@MnglNames1, @MnglNames2, @UnMnglNames) = ();
13751 foreach my $Interface (sort @_)
13752 {
13753 if($Interface=~/\A_Z/)
13754 {
13755 next if($tr_name{$Interface});
13756 $Interface=~s/[\@\$]+(.*)\Z//;
13757 push(@MnglNames1, $Interface);
13758 }
13759 elsif($Interface=~/\A\?/) {
13760 push(@MnglNames2, $Interface);
13761 }
13762 else
13763 { # not mangled
13764 $tr_name{$Interface} = $Interface;
13765 $mangled_name_gcc{$Interface} = $Interface;
13766 $mangled_name{$LibVersion}{$Interface} = $Interface;
13767 }
13768 }
13769 if($#MnglNames1 > -1)
13770 { # GCC names
13771 @UnMnglNames = reverse(unmangleArray(@MnglNames1));
13772 foreach my $MnglName (@MnglNames1)
13773 {
13774 my $Unmangled = $tr_name{$MnglName} = formatName(canonifyName(pop(@UnMnglNames)));
13775 if(not $mangled_name_gcc{$Unmangled}) {
13776 $mangled_name_gcc{$Unmangled} = $MnglName;
13777 }
13778 if($MnglName=~/\A_ZTV/ and $Unmangled=~/vtable for (.+)/)
13779 { # bind class name and v-table symbol
13780 $ClassVTable{$LibVersion}{$1} = $MnglName;
13781 $VTableClass{$LibVersion}{$MnglName} = $1;
13782 }
13783 }
13784 }
13785 if($#MnglNames2 > -1)
13786 { # MSVC names
13787 @UnMnglNames = reverse(unmangleArray(@MnglNames2));
13788 foreach my $MnglName (@MnglNames2)
13789 {
13790 $tr_name{$MnglName} = formatName(pop(@UnMnglNames));
13791 $mangled_name{$LibVersion}{$tr_name{$MnglName}} = $MnglName;
13792 }
13793 }
13794 return \%tr_name;
13795}
13796
13797sub link_symbol($$$)
13798{
13799 my ($Symbol, $RunWith, $Deps) = @_;
13800 if(link_symbol_internal($Symbol, $RunWith, \%Symbol_Library)) {
13801 return 1;
13802 }
13803 if($Deps eq "+Deps")
13804 { # check the dependencies
13805 if(link_symbol_internal($Symbol, $RunWith, \%DepSymbols)) {
13806 return 1;
13807 }
13808 }
13809 return 0;
13810}
13811
13812sub link_symbol_internal($$$)
13813{
13814 my ($Symbol, $RunWith, $Where) = @_;
13815 return 0 if(not $Where or not $Symbol);
13816 if($Where->{$RunWith}{$Symbol})
13817 { # the exact match by symbol name
13818 return 1;
13819 }
13820 if(my $VSym = $SymVer{$RunWith}{$Symbol})
13821 { # indirect symbol version, i.e.
13822 # foo_old and its symlink foo@v (or foo@@v)
13823 # foo_old may be in .symtab table
13824 if($Where->{$RunWith}{$VSym}) {
13825 return 1;
13826 }
13827 }
13828 my ($Sym, $Del, $Ver) = separate_symbol($Symbol);
13829 if($Sym and $Ver)
13830 { # search for the symbol with the same version
13831 # or without version
13832 if($Where->{$RunWith}{$Sym})
13833 { # old: foo@v|foo@@v
13834 # new: foo
13835 return 1;
13836 }
13837 if($Where->{$RunWith}{$Sym."\@".$Ver})
13838 { # old: foo|foo@@v
13839 # new: foo@v
13840 return 1;
13841 }
13842 if($Where->{$RunWith}{$Sym."\@\@".$Ver})
13843 { # old: foo|foo@v
13844 # new: foo@@v
13845 return 1;
13846 }
13847 }
13848 return 0;
13849}
13850
13851sub getSymbols_App($)
13852{
13853 my $Path = $_[0];
13854 return () if(not $Path or not -f $Path);
13855 my @Imported = ();
13856 if($OSgroup eq "macos")
13857 {
13858 my $OtoolCmd = get_CmdPath("otool");
13859 if(not $OtoolCmd) {
13860 exitStatus("Not_Found", "can't find \"otool\"");
13861 }
13862 open(APP, "$OtoolCmd -IV $Path 2>$TMP_DIR/null |");
13863 while(<APP>) {
13864 if(/[^_]+\s+_?([\w\$]+)\s*\Z/) {
13865 push(@Imported, $1);
13866 }
13867 }
13868 close(APP);
13869 }
13870 elsif($OSgroup eq "windows")
13871 {
13872 my $DumpBinCmd = get_CmdPath("dumpbin");
13873 if(not $DumpBinCmd) {
13874 exitStatus("Not_Found", "can't find \"dumpbin.exe\"");
13875 }
13876 open(APP, "$DumpBinCmd /IMPORTS $Path 2>$TMP_DIR/null |");
13877 while(<APP>) {
13878 if(/\s*\w+\s+\w+\s+\w+\s+([\w\?\@]+)\s*/) {
13879 push(@Imported, $1);
13880 }
13881 }
13882 close(APP);
13883 }
13884 else
13885 {
13886 my $ReadelfCmd = get_CmdPath("readelf");
13887 if(not $ReadelfCmd) {
13888 exitStatus("Not_Found", "can't find \"readelf\"");
13889 }
13890 open(APP, "$ReadelfCmd -WhlSsdA $Path 2>$TMP_DIR/null |");
13891 my $symtab=0; # indicates that we are processing 'symtab' section of 'readelf' output
13892 while(<APP>)
13893 {
13894 if( /'.dynsym'/ ) {
13895 $symtab=0;
13896 }
13897 elsif($symtab == 1) {
13898 # do nothing with symtab (but there are some plans for the future)
13899 next;
13900 }
13901 elsif( /'.symtab'/ ) {
13902 $symtab=1;
13903 }
13904 elsif(my ($fullname, $idx, $Ndx, $type, $size, $bind) = readline_ELF($_))
13905 {
13906 if( $Ndx eq "UND" ) {
13907 #only imported symbols
13908 push(@Imported, $fullname);
13909 }
13910 }
13911 }
13912 close(APP);
13913 }
13914 return @Imported;
13915}
13916
13917sub readline_ELF($)
13918{
13919 if($_[0]=~/\s*\d+:\s+(\w*)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s([^\s]+)/)
13920 { # the line of 'readelf' output corresponding to the interface
13921 # symbian-style: _ZN12CCTTokenType4NewLE4TUid3RFs@@ctfinder{000a0000}[102020e5].dll
13922 my ($value, $size, $type, $bind,
13923 $vis, $Ndx, $fullname)=($1, $2, $3, $4, $5, $6, $7);
13924 if($bind!~/\A(WEAK|GLOBAL)\Z/) {
13925 return ();
13926 }
13927 if($type!~/\A(FUNC|IFUNC|OBJECT|COMMON)\Z/) {
13928 return ();
13929 }
13930 if($vis!~/\A(DEFAULT|PROTECTED)\Z/) {
13931 return ();
13932 }
13933 if($Ndx eq "ABS" and $value!~/\D|1|2|3|4|5|6|7|8|9/) {
13934 return ();
13935 }
13936 if($OStarget eq "symbian")
13937 {
13938 if($fullname=~/_\._\.absent_export_\d+/)
13939 {# "_._.absent_export_111"@@libstdcpp{00010001}[10282872].dll
13940 return ();
13941 }
13942 my @Elems = separate_symbol($fullname);
13943 $fullname = $Elems[0];# remove internal version, {00020001}[10011235].dll
13944 }
13945 return ($fullname, $value, $Ndx, $type, $size, $bind);
13946 }
13947 else {
13948 return ();
13949 }
13950}
13951
13952sub getSymbols_Lib($$$$$)
13953{
13954 my ($LibVersion, $Lib_Path, $IsNeededLib, $GroupNames, $Weak) = @_;
13955 return if(not $Lib_Path or not -f $Lib_Path);
13956 my ($Lib_Dir, $Lib_Name) = separate_path(resolve_symlink($Lib_Path));
13957 return if($CheckedDyLib{$LibVersion}{$Lib_Name} and $IsNeededLib);
13958 return if(isCyclical(\@RecurLib, $Lib_Name) or $#RecurLib>=1);
13959 $CheckedDyLib{$LibVersion}{$Lib_Name} = 1;
13960 getImplementations($LibVersion, $Lib_Path) if($CheckImpl and not $IsNeededLib);
13961 push(@RecurLib, $Lib_Name);
13962 my (%Value_Interface, %Interface_Value, %NeededLib) = ();
13963 if(not $IsNeededLib)
13964 { # libstdc++ and libc are always used by other libs
13965 # if you test one of these libs then you not need
13966 # to find them in the system for reusing
13967 if(parse_libname($Lib_Name, "short", $OStarget) eq "libstdc++")
13968 { # libstdc++.so.6
13969 $STDCXX_TESTING = 1;
13970 }
13971 if(parse_libname($Lib_Name, "short", $OStarget) eq "libc")
13972 { # libc-2.11.3.so
13973 $GLIBC_TESTING = 1;
13974 }
13975 }
13976 if($OStarget eq "macos")
13977 { # Mac OS X: *.dylib, *.a
13978 my $OtoolCmd = get_CmdPath("otool");
13979 if(not $OtoolCmd) {
13980 exitStatus("Not_Found", "can't find \"otool\"");
13981 }
13982 open(LIB, "$OtoolCmd -TV $Lib_Path 2>$TMP_DIR/null |");
13983 while(<LIB>)
13984 {
13985 if(/[^_]+\s+_([\w\$]+)\s*\Z/)
13986 {
13987 my $realname = $1;
13988 if($IsNeededLib and $GroupNames
13989 and not $GroupNames->{parse_libname($Lib_Name, "name+ext", $OStarget)}) {
13990 $DepSymbols{$LibVersion}{$realname} = 1;
13991 }
13992 if(not $IsNeededLib)
13993 {
13994 $Symbol_Library{$LibVersion}{$realname} = $Lib_Name;
13995 $Library_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
13996 if($COMMON_LANGUAGE{$LibVersion} ne "C++"
13997 and $realname=~/\A(_Z|\?)/) {
13998 setLanguage($LibVersion, "C++");
13999 }
14000 if($CheckObjectsOnly
14001 and $LibVersion==1) {
14002 $CheckedSymbols{$realname} = 1;
14003 }
14004 }
14005 }
14006 }
14007 close(LIB);
14008 if($LIB_TYPE eq "dynamic")
14009 { # dependencies
14010 open(LIB, "$OtoolCmd -L $Lib_Path 2>$TMP_DIR/null |");
14011 while(<LIB>) {
14012 if(/\s*([\/\\].+\.$LIB_EXT)\s*/
14013 and $1 ne $Lib_Path) {
14014 $NeededLib{$1} = 1;
14015 }
14016 }
14017 close(LIB);
14018 }
14019 }
14020 elsif($OStarget eq "windows")
14021 { # Windows *.dll, *.lib
14022 my $DumpBinCmd = get_CmdPath("dumpbin");
14023 if(not $DumpBinCmd) {
14024 exitStatus("Not_Found", "can't find \"dumpbin\"");
14025 }
14026 open(LIB, "$DumpBinCmd /EXPORTS \"$Lib_Path\" 2>$TMP_DIR/null |");
14027 while(<LIB>)
14028 { # 1197 4AC 0000A620 SetThreadStackGuarantee
14029 # 1198 4AD SetThreadToken (forwarded to ...)
14030 # 3368 _o2i_ECPublicKey
14031 if(/\A\s*\d+\s+[a-f\d]+\s+[a-f\d]+\s+([\w\?\@]+)\s*\Z/i
14032 or /\A\s*\d+\s+[a-f\d]+\s+([\w\?\@]+)\s*\(\s*forwarded\s+/
14033 or /\A\s*\d+\s+_([\w\?\@]+)\s*\Z/)
14034 { # dynamic, static and forwarded symbols
14035 my $realname = $1;
14036 if($IsNeededLib and not $GroupNames->{parse_libname($Lib_Name, "name+ext", $OStarget)}) {
14037 $DepSymbols{$LibVersion}{$realname} = 1;
14038 }
14039 if(not $IsNeededLib)
14040 {
14041 $Symbol_Library{$LibVersion}{$realname} = $Lib_Name;
14042 $Library_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
14043 if($COMMON_LANGUAGE{$LibVersion} ne "C++"
14044 and $realname=~/\A(_Z|\?)/) {
14045 setLanguage($LibVersion, "C++");
14046 }
14047 if($CheckObjectsOnly
14048 and $LibVersion==1) {
14049 $CheckedSymbols{$realname} = 1;
14050 }
14051 }
14052 }
14053 }
14054 close(LIB);
14055 if($LIB_TYPE eq "dynamic")
14056 { # dependencies
14057 open(LIB, "$DumpBinCmd /DEPENDENTS $Lib_Path 2>$TMP_DIR/null |");
14058 while(<LIB>) {
14059 if(/\s*([^\s]+?\.$LIB_EXT)\s*/i
14060 and $1 ne $Lib_Path) {
14061 $NeededLib{path_format($1, $OSgroup)} = 1;
14062 }
14063 }
14064 close(LIB);
14065 }
14066 }
14067 else
14068 { # Unix; *.so, *.a
14069 # Symbian: *.dso, *.lib
14070 my $ReadelfCmd = get_CmdPath("readelf");
14071 if(not $ReadelfCmd) {
14072 exitStatus("Not_Found", "can't find \"readelf\"");
14073 }
14074 open(LIB, "$ReadelfCmd -WhlSsdA $Lib_Path 2>$TMP_DIR/null |");
14075 my $symtab=0; # indicates that we are processing 'symtab' section of 'readelf' output
14076 while(<LIB>)
14077 {
14078 if($LIB_TYPE eq "dynamic")
14079 { # dynamic library specifics
14080 if(/NEEDED.+\[([^\[\]]+)\]/)
14081 { # dependencies:
14082 # 0x00000001 (NEEDED) Shared library: [libc.so.6]
14083 $NeededLib{$1} = 1;
14084 next;
14085 }
14086 if(/'\.dynsym'/)
14087 { # dynamic table
14088 $symtab=0;
14089 next;
14090 }
14091 if($symtab == 1)
14092 { # do nothing with symtab
14093 next;
14094 }
14095 if(/'\.symtab'/)
14096 { # symbol table
14097 $symtab=1;
14098 next;
14099 }
14100 }
14101 if(not $LIB_ARCH{$LibVersion})
14102 {
14103 if(/Machine:.*?([\w\-]+)\s*\Z/)
14104 { # architecture
14105 $LIB_ARCH{$LibVersion}=$1;
14106 next;
14107 }
14108 }
14109 if(my ($fullname, $idx, $Ndx, $type, $size, $bind) = readline_ELF($_))
14110 { # read ELF entry
14111 if( $Ndx eq "UND" )
14112 { # ignore interfaces that are imported from somewhere else
14113 next;
14114 }
14115 if($bind eq "WEAK"
14116 and $Weak eq "-Weak")
14117 { # skip WEAK symbols
14118 next;
14119 }
14120 my ($realname, $version_spec, $version) = separate_symbol($fullname);
14121 if($type eq "OBJECT")
14122 { # global data
14123 $CompleteSignature{$LibVersion}{$fullname}{"Object"} = 1;
14124 $CompleteSignature{$LibVersion}{$realname}{"Object"} = 1;
14125 }
14126 if($IsNeededLib and not $GroupNames->{parse_libname($Lib_Name, "name+ext", $OStarget)}) {
14127 $DepSymbols{$LibVersion}{$fullname} = 1;
14128 }
14129 if(not $IsNeededLib)
14130 {
14131 $Symbol_Library{$LibVersion}{$fullname} = $Lib_Name;
14132 $Library_Symbol{$LibVersion}{$Lib_Name}{$fullname} = ($type eq "OBJECT")?-$size:1;
14133 if($LIB_EXT eq "so")
14134 { # value
14135 $Interface_Value{$LibVersion}{$fullname} = $idx;
14136 $Value_Interface{$LibVersion}{$idx}{$fullname} = 1;
14137 }
14138 if($COMMON_LANGUAGE{$LibVersion} ne "C++"
14139 and $realname=~/\A(_Z|\?)/) {
14140 setLanguage($LibVersion, "C++");
14141 }
14142 if($CheckObjectsOnly
14143 and $LibVersion==1) {
14144 $CheckedSymbols{$fullname} = 1;
14145 }
14146 }
14147 }
14148 }
14149 close(LIB);
14150 }
14151 if(not $IsNeededLib and $LIB_EXT eq "so")
14152 { # get symbol versions
14153 foreach my $Symbol (keys(%{$Symbol_Library{$LibVersion}}))
14154 {
14155 next if($Symbol!~/\@/);
14156 my $Interface_SymName = "";
14157 foreach my $Symbol_SameValue (keys(%{$Value_Interface{$LibVersion}{$Interface_Value{$LibVersion}{$Symbol}}}))
14158 {
14159 if($Symbol_SameValue ne $Symbol
14160 and $Symbol_SameValue!~/\@/)
14161 {
14162 $SymVer{$LibVersion}{$Symbol_SameValue} = $Symbol;
14163 $Interface_SymName = $Symbol_SameValue;
14164 last;
14165 }
14166 }
14167 if(not $Interface_SymName)
14168 {
14169 if($Symbol=~/\A([^\@\$\?]*)[\@\$]+([^\@\$]*)\Z/
14170 and not $SymVer{$LibVersion}{$1}) {
14171 $SymVer{$LibVersion}{$1} = $Symbol;
14172 }
14173 }
14174 }
14175 }
14176 foreach my $DyLib (sort keys(%NeededLib))
14177 {
14178 my $DepPath = find_lib_path($LibVersion, $DyLib);
14179 if($DepPath and -f $DepPath) {
14180 getSymbols_Lib($LibVersion, $DepPath, 1, $GroupNames, "+Weak");
14181 }
14182 }
14183 pop(@RecurLib);
14184 return $Library_Symbol{$LibVersion};
14185}
14186
14187sub get_path_prefixes($)
14188{
14189 my $Path = $_[0];
14190 my ($Dir, $Name) = separate_path($Path);
14191 my %Prefixes = ();
14192 foreach my $Prefix (reverse(split(/[\/\\]+/, $Dir)))
14193 {
14194 $Prefixes{$Name} = 1;
14195 $Name = joinPath($Prefix, $Name);
14196 last if(keys(%Prefixes)>5 or $Prefix eq "include");
14197 }
14198 return keys(%Prefixes);
14199}
14200
14201sub detectSystemHeaders()
14202{
14203 my @SysHeaders = ();
14204 foreach my $DevelPath (keys(%{$SystemPaths{"include"}}))
14205 {
14206 next if(not -d $DevelPath);
14207 # search for all header files in the /usr/include
14208 # with or without extension (ncurses.h, QtCore, ...)
14209 @SysHeaders = (@SysHeaders, cmd_find($DevelPath,"f","",""));
14210 foreach my $Link (cmd_find($DevelPath,"l","",""))
14211 { # add symbolic links
14212 if(-f $Link) {
14213 push(@SysHeaders, $Link);
14214 }
14215 }
14216 }
14217 foreach my $DevelPath (keys(%{$SystemPaths{"lib"}}))
14218 {
14219 next if(not -d $DevelPath);
14220 # search for config headers in the /usr/lib
14221 @SysHeaders = (@SysHeaders, cmd_find($DevelPath,"f","*.h",""));
14222 foreach my $Dir (cmd_find($DevelPath,"d","include",""))
14223 { # search for all include directories
14224 # this is for headers that are installed to /usr/lib
14225 # Example: Qt4 headers in Mandriva (/usr/lib/qt4/include/)
14226 if($Dir=~/\/(gcc|jvm|syslinux|kdb)\//) {
14227 next;
14228 }
14229 @SysHeaders = (@SysHeaders, cmd_find($Dir,"f","",""));
14230 }
14231 }
14232 foreach my $Path (@SysHeaders)
14233 {
14234 foreach my $Part (get_path_prefixes($Path)) {
14235 $SystemHeaders{$Part}{$Path}=1;
14236 }
14237 }
14238}
14239
14240sub detectSystemObjects()
14241{
14242 foreach my $DevelPath (keys(%{$SystemPaths{"lib"}}))
14243 {
14244 next if(not -d $DevelPath);
14245 foreach my $Path (find_libs($DevelPath,"",""))
14246 { # search for shared libraries in the /usr/lib (including symbolic links)
14247 $SystemObjects{parse_libname(get_filename($Path), "name+ext", $OStarget)}{$Path}=1;
14248 }
14249 }
14250}
14251
14252sub find_lib_path($$)
14253{
14254 my ($LibVersion, $DyLib) = @_;
14255 return "" if(not $DyLib or not $LibVersion);
14256 return $DyLib if(is_abs($DyLib));
14257 if(defined $Cache{"find_lib_path"}{$LibVersion}{$DyLib}) {
14258 return $Cache{"find_lib_path"}{$LibVersion}{$DyLib};
14259 }
14260 if(my @Paths = sort keys(%{$InputObject_Paths{$LibVersion}{$DyLib}})) {
14261 return ($Cache{"find_lib_path"}{$LibVersion}{$DyLib} = $Paths[0]);
14262 }
14263 elsif(my $DefaultPath = $DyLib_DefaultPath{$DyLib}) {
14264 return ($Cache{"find_lib_path"}{$LibVersion}{$DyLib} = $DefaultPath);
14265 }
14266 else
14267 {
14268 foreach my $Dir (sort keys(%DefaultLibPaths), sort keys(%{$SystemPaths{"lib"}}))
14269 { # search in default linker paths and then in all system paths
14270 if(-f $Dir."/".$DyLib) {
14271 return ($Cache{"find_lib_path"}{$LibVersion}{$DyLib} = joinPath($Dir,$DyLib));
14272 }
14273 }
14274 detectSystemObjects() if(not keys(%SystemObjects));
14275 if(my @AllObjects = keys(%{$SystemObjects{$DyLib}})) {
14276 return ($Cache{"find_lib_path"}{$LibVersion}{$DyLib} = $AllObjects[0]);
14277 }
14278 my $ShortName = parse_libname($DyLib, "name+ext", $OStarget);
14279 if($ShortName ne $DyLib
14280 and my $Path = find_lib_path($ShortName))
14281 { # FIXME: check this case
14282 return ($Cache{"find_lib_path"}{$LibVersion}{$DyLib} = $Path);
14283 }
14284 return ($Cache{"find_lib_path"}{$LibVersion}{$DyLib} = "");
14285 }
14286}
14287
14288sub getSoPaths($)
14289{
14290 my $LibVersion = $_[0];
14291 my @SoPaths = ();
14292 foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Libs"}))
14293 {
14294 if(not -e $Dest) {
14295 exitStatus("Access_Error", "can't access \'$Dest\'");
14296 }
14297 my @SoPaths_Dest = getSOPaths_Dest($Dest, $LibVersion);
14298 foreach (@SoPaths_Dest) {
14299 push(@SoPaths, $_);
14300 }
14301 }
14302 return @SoPaths;
14303}
14304
14305sub skip_lib($$)
14306{
14307 my ($Path, $LibVersion) = @_;
14308 return 1 if(not $Path or not $LibVersion);
14309 my $LibName = get_filename($Path);
14310 if($SkipLibs{$LibVersion}{"Name"}{$LibName}) {
14311 return 1;
14312 }
14313 my $ShortName = parse_libname($LibName, "name+ext", $OStarget);
14314 if($SkipLibs{$LibVersion}{"Name"}{$ShortName}) {
14315 return 1;
14316 }
14317 foreach my $Dir (keys(%{$SkipLibs{$LibVersion}{"Path"}}))
14318 {
14319 if($Path=~/\Q$Dir\E([\/\\]|\Z)/) {
14320 return 1;
14321 }
14322 }
14323 foreach my $Pattern (keys(%{$SkipLibs{$LibVersion}{"Pattern"}}))
14324 {
14325 if($LibName=~/$Pattern/) {
14326 return 1;
14327 }
14328 if($Pattern=~/[\/\\]/ and $Path=~/$Pattern/) {
14329 return 1;
14330 }
14331 }
14332 return 0;
14333}
14334
14335sub skip_header($$)
14336{ # returns:
14337 # 1 - if header should NOT be included and checked
14338 # 2 - if header should NOT be included, but should be checked
14339 my ($Path, $LibVersion) = @_;
14340 return 1 if(not $Path or not $LibVersion);
14341 my $HeaderName = get_filename($Path);
14342 if(my $Kind = $SkipHeaders{$LibVersion}{"Name"}{$HeaderName}) {
14343 return $Kind;
14344 }
14345 foreach my $Dir (keys(%{$SkipHeaders{$LibVersion}{"Path"}}))
14346 {
14347 if($Path=~/\Q$Dir\E([\/\\]|\Z)/) {
14348 return $SkipHeaders{$LibVersion}{"Path"}{$Dir};
14349 }
14350 }
14351 foreach my $Pattern (keys(%{$SkipHeaders{$LibVersion}{"Pattern"}}))
14352 {
14353 if($HeaderName=~/$Pattern/) {
14354 return $SkipHeaders{$LibVersion}{"Pattern"}{$Pattern};
14355 }
14356 if($Pattern=~/[\/\\]/ and $Path=~/$Pattern/) {
14357 return $SkipHeaders{$LibVersion}{"Pattern"}{$Pattern};
14358 }
14359 }
14360 return 0;
14361}
14362
14363sub register_objects($$)
14364{
14365 my ($Dir, $LibVersion) = @_;
14366 if($SystemPaths{"lib"}{$Dir})
14367 { # system directory
14368 return;
14369 }
14370 if($RegisteredObjDirs{$LibVersion}{$Dir})
14371 { # already registered
14372 return;
14373 }
14374 foreach my $Path (find_libs($Dir,"",1))
14375 {
14376 next if(ignore_path($Path));
14377 next if(skip_lib($Path, $LibVersion));
14378 $InputObject_Paths{$LibVersion}{get_filename($Path)}{$Path} = 1;
14379 }
14380 $RegisteredObjDirs{$LibVersion}{$Dir} = 1;
14381}
14382
14383sub getSOPaths_Dest($$)
14384{
14385 my ($Dest, $LibVersion) = @_;
14386 if(skip_lib($Dest, $LibVersion)) {
14387 return ();
14388 }
14389 if(-f $Dest)
14390 {
14391 if(not parse_libname($Dest, "name", $OStarget)) {
14392 exitStatus("Error", "incorrect format of library (should be *.$LIB_EXT): \'$Dest\'");
14393 }
14394 $InputObject_Paths{$LibVersion}{get_filename($Dest)}{$Dest} = 1;
14395 register_objects(get_dirname($Dest), $LibVersion);
14396 return ($Dest);
14397 }
14398 elsif(-d $Dest)
14399 {
14400 $Dest=~s/[\/\\]+\Z//g;
14401 my @AllObjects = ();
14402 if($SystemPaths{"lib"}{$Dest})
14403 { # you have specified /usr/lib as the search directory (<libs>) in the XML descriptor
14404 # and the real name of the library by -l option (bz2, stdc++, Xaw, ...)
14405 foreach my $Path (cmd_find($Dest,"","*".esc($TargetLibraryName)."*\.$LIB_EXT*",2))
14406 { # all files and symlinks that match the name of a library
14407 if(get_filename($Path)=~/\A(|lib)\Q$TargetLibraryName\E[\d\-]*\.$LIB_EXT[\d\.]*\Z/i)
14408 {
14409 $InputObject_Paths{$LibVersion}{get_filename($Path)}{$Path} = 1;
14410 push(@AllObjects, resolve_symlink($Path));
14411 }
14412 }
14413 }
14414 else
14415 { # search for all files and symlinks
14416 foreach my $Path (find_libs($Dest,"",""))
14417 {
14418 next if(ignore_path($Path));
14419 next if(skip_lib($Path, $LibVersion));
14420 $InputObject_Paths{$LibVersion}{get_filename($Path)}{$Path} = 1;
14421 push(@AllObjects, resolve_symlink($Path));
14422 }
14423 if($OSgroup eq "macos")
14424 { # shared libraries on MacOS X may have no extension
14425 foreach my $Path (cmd_find($Dest,"f","",""))
14426 {
14427 next if(ignore_path($Path));
14428 next if(skip_lib($Path, $LibVersion));
14429 if(get_filename($Path)!~/\./
14430 and cmd_file($Path)=~/(shared|dynamic)\s+library/i) {
14431 $InputObject_Paths{$LibVersion}{get_filename($Path)}{$Path} = 1;
14432 push(@AllObjects, resolve_symlink($Path));
14433 }
14434 }
14435 }
14436 }
14437 return @AllObjects;
14438 }
14439 else {
14440 return ();
14441 }
14442}
14443
14444sub isCyclical($$) {
14445 return (grep {$_ eq $_[1]} @{$_[0]});
14446}
14447
14448sub read_symlink($)
14449{
14450 my $Path = $_[0];
14451 return "" if(not $Path);
14452 return "" if(not -f $Path and not -l $Path);
14453 if(defined $Cache{"read_symlink"}{$Path}) {
14454 return $Cache{"read_symlink"}{$Path};
14455 }
14456 if(my $Res = readlink($Path)) {
14457 return ($Cache{"read_symlink"}{$Path} = $Res);
14458 }
14459 elsif(my $ReadlinkCmd = get_CmdPath("readlink")) {
14460 return ($Cache{"read_symlink"}{$Path} = `$ReadlinkCmd -n $Path`);
14461 }
14462 elsif(my $FileCmd = get_CmdPath("file"))
14463 {
14464 my $Info = `$FileCmd $Path`;
14465 if($Info=~/symbolic\s+link\s+to\s+['`"]*([\w\d\.\-\/\\]+)['`"]*/i) {
14466 return ($Cache{"read_symlink"}{$Path} = $1);
14467 }
14468 }
14469 return ($Cache{"read_symlink"}{$Path} = "");
14470}
14471
14472sub resolve_symlink($)
14473{
14474 my $Path = $_[0];
14475 return "" if(not $Path);
14476 return "" if(not -f $Path and not -l $Path);
14477 if(defined $Cache{"resolve_symlink"}{$Path}) {
14478 return $Cache{"resolve_symlink"}{$Path};
14479 }
14480 return $Path if(isCyclical(\@RecurSymlink, $Path));
14481 push(@RecurSymlink, $Path);
14482 if(-l $Path and my $Redirect=read_symlink($Path))
14483 {
14484 if(is_abs($Redirect))
14485 { # absolute path
14486 if($SystemRoot and $SystemRoot ne "/"
14487 and $Path=~/\A\Q$SystemRoot\E\//
14488 and (-f $SystemRoot.$Redirect or -l $SystemRoot.$Redirect))
14489 { # symbolic links from the sysroot
14490 # should be corrected to point to
14491 # the files inside sysroot
14492 $Redirect = $SystemRoot.$Redirect;
14493 }
14494 my $Res = resolve_symlink($Redirect);
14495 pop(@RecurSymlink);
14496 return ($Cache{"resolve_symlink"}{$Path} = $Res);
14497 }
14498 elsif($Redirect=~/\.\.[\/\\]/)
14499 { # relative path
14500 $Redirect = joinPath(get_dirname($Path),$Redirect);
14501 while($Redirect=~s&(/|\\)[^\/\\]+(\/|\\)\.\.(\/|\\)&$1&){};
14502 my $Res = resolve_symlink($Redirect);
14503 pop(@RecurSymlink);
14504 return ($Cache{"resolve_symlink"}{$Path} = $Res);
14505 }
14506 elsif(-f get_dirname($Path)."/".$Redirect)
14507 { # file name in the same directory
14508 my $Res = resolve_symlink(joinPath(get_dirname($Path),$Redirect));
14509 pop(@RecurSymlink);
14510 return ($Cache{"resolve_symlink"}{$Path} = $Res);
14511 }
14512 else
14513 { # broken link
14514 pop(@RecurSymlink);
14515 return ($Cache{"resolve_symlink"}{$Path} = "");
14516 }
14517 }
14518 pop(@RecurSymlink);
14519 return ($Cache{"resolve_symlink"}{$Path} = $Path);
14520}
14521
14522sub generateTemplate()
14523{
14524 writeFile("VERSION.xml", $DescriptorTemplate."\n");
14525 printMsg("INFO", "XML-descriptor template ./VERSION.xml has been generated");
14526}
14527
14528sub detectWordSize()
14529{
14530 return "" if(not $GCC_PATH);
14531 if($Cache{"detectWordSize"}) {
14532 return $Cache{"detectWordSize"};
14533 }
14534 writeFile("$TMP_DIR/empty.h", "");
14535 my $Defines = `$GCC_PATH -E -dD $TMP_DIR/empty.h`;
14536 unlink("$TMP_DIR/empty.h");
14537 my $WSize = 0;
14538 if($Defines=~/ __SIZEOF_POINTER__\s+(\d+)/)
14539 {# GCC 4
14540 $WSize = $1;
14541 }
14542 elsif($Defines=~/ __PTRDIFF_TYPE__\s+(\w+)/)
14543 {# GCC 3
14544 my $PTRDIFF = $1;
14545 if($PTRDIFF=~/long/) {
14546 $WSize = 8;
14547 }
14548 else {
14549 $WSize = 4;
14550 }
14551 }
14552 if(not int($WSize)) {
14553 exitStatus("Error", "can't check WORD size");
14554 }
14555 return ($Cache{"detectWordSize"} = $WSize);
14556}
14557
14558sub majorVersion($)
14559{
14560 my $V = $_[0];
14561 return 0 if(not $V);
14562 my @VParts = split(/\./, $V);
14563 return $VParts[0];
14564}
14565
14566sub cmpVersions($$)
14567{# compare two versions in dotted-numeric format
14568 my ($V1, $V2) = @_;
14569 return 0 if($V1 eq $V2);
14570 return undef if($V1!~/\A\d+[\.\d+]*\Z/);
14571 return undef if($V2!~/\A\d+[\.\d+]*\Z/);
14572 my @V1Parts = split(/\./, $V1);
14573 my @V2Parts = split(/\./, $V2);
14574 for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) {
14575 return -1 if(int($V1Parts[$i]) < int($V2Parts[$i]));
14576 return 1 if(int($V1Parts[$i]) > int($V2Parts[$i]));
14577 }
14578 return -1 if($#V1Parts < $#V2Parts);
14579 return 1 if($#V1Parts > $#V2Parts);
14580 return 0;
14581}
14582
14583sub read_ABI_Dump($$)
14584{
14585 my ($LibVersion, $Path) = @_;
14586 return if(not $LibVersion or not -e $Path);
14587 my $FilePath = "";
14588 if($Path=~/\.abi\Z/)
14589 { # input *.abi
14590 $FilePath = $Path;
14591 }
14592 else
14593 { # input *.abi.tar.gz
14594 $FilePath = unpackDump($Path);
14595 }
14596 if($FilePath!~/\.abi\Z/) {
14597 exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
14598 }
14599 my $Content = readFile($FilePath);
14600 if($Path!~/\.abi\Z/)
14601 { # remove temp file
14602 unlink($FilePath);
14603 }
14604 if($Content!~/};\s*\Z/) {
14605 exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
14606 }
14607 my $LibraryABI = eval($Content);
14608 if(not $LibraryABI) {
14609 exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
14610 }
14611 # new dumps (>=1.22) have a personal versioning
14612 my $DumpVersion = $LibraryABI->{"ABI_DUMP_VERSION"};
14613 my $ToolVersion = $LibraryABI->{"ABI_COMPLIANCE_CHECKER_VERSION"};
14614 if(not $DumpVersion)
14615 { # old dumps (<=1.21.6) have been marked by the tool version
14616 $DumpVersion = $ToolVersion;
14617 }
14618 $UsedDump{$LibVersion}{"V"} = $DumpVersion;
14619 if(majorVersion($DumpVersion) ne majorVersion($ABI_DUMP_VERSION))
14620 { # should be compatible with dumps of the same major version
14621 if(cmpVersions($DumpVersion, $ABI_DUMP_VERSION)>0)
14622 { # Don't know how to parse future dump formats
14623 exitStatus("Dump_Version", "incompatible version $DumpVersion of specified ABI dump (newer than $ABI_DUMP_VERSION)");
14624 }
14625 elsif(cmpVersions($DumpVersion, $TOOL_VERSION)>0 and not $LibraryABI->{"ABI_DUMP_VERSION"})
14626 { # Don't know how to parse future dump formats
14627 exitStatus("Dump_Version", "incompatible version $DumpVersion of specified ABI dump (newer than $TOOL_VERSION)");
14628 }
14629 if($UseOldDumps)
14630 {
14631 if(cmpVersions($DumpVersion, $OLDEST_SUPPORTED_VERSION)<0) {
14632 exitStatus("Dump_Version", "incompatible version $DumpVersion of specified ABI dump (older than $OLDEST_SUPPORTED_VERSION)");
14633 }
14634 }
14635 else
14636 {
14637 my $Msg = "incompatible version $DumpVersion of specified ABI dump (allowed only ".majorVersion($ABI_DUMP_VERSION).".0<=V<=$ABI_DUMP_VERSION)";
14638 if(cmpVersions($DumpVersion, $OLDEST_SUPPORTED_VERSION)>=0) {
14639 $Msg .= "\nUse -old-dumps option to use old-version dumps ($OLDEST_SUPPORTED_VERSION<=V<".majorVersion($ABI_DUMP_VERSION).".0)";
14640 }
14641 exitStatus("Dump_Version", $Msg);
14642 }
14643 }
14644 if($LibraryABI->{"Mode"} eq "Extended")
14645 { # --ext option
14646 $ExtendedCheck = 1;
14647 }
14648 if(my $Lang = $LibraryABI->{"Language"})
14649 {
14650 $UsedDump{$LibVersion}{"L"} = $Lang;
14651 setLanguage($LibVersion, $Lang);
14652 }
14653 $TypeInfo{$LibVersion} = $LibraryABI->{"TypeInfo"};
14654 if(not $TypeInfo{$LibVersion})
14655 { # support for old ABI dumps
14656 $TypeInfo{$LibVersion} = $LibraryABI->{"TypeDescr"};
14657 }
14658 read_Machine_DumpInfo($LibraryABI, $LibVersion);
14659 $SymbolInfo{$LibVersion} = $LibraryABI->{"SymbolInfo"};
14660 if(not $SymbolInfo{$LibVersion})
14661 { # support for old dumps
14662 $SymbolInfo{$LibVersion} = $LibraryABI->{"FuncDescr"};
14663 }
14664 if(not keys(%{$SymbolInfo{$LibVersion}}))
14665 { # validation of old-version dumps
14666 if(not $ExtendedCheck) {
14667 exitStatus("Invalid_Dump", "the input dump d$LibVersion is invalid");
14668 }
14669 }
14670 $Library_Symbol{$LibVersion} = $LibraryABI->{"Symbols"};
14671 if(not $Library_Symbol{$LibVersion})
14672 { # support for old dumps
14673 $Library_Symbol{$LibVersion} = $LibraryABI->{"Interfaces"};
14674 }
14675 $DepSymbols{$LibVersion} = $LibraryABI->{"DepSymbols"};
14676 if(not $DepSymbols{$LibVersion})
14677 { # support for old dumps
14678 $DepSymbols{$LibVersion} = $LibraryABI->{"DepInterfaces"};
14679 }
14680 if(not $DepSymbols{$LibVersion})
14681 { # support for old dumps
14682 # Cannot reconstruct DepSymbols. This may result in false
14683 # positives if the old dump is for library 2. Not a problem if
14684 # old dumps are only from old libraries.
14685 $DepSymbols{$LibVersion} = {};
14686 }
14687 $SymVer{$LibVersion} = $LibraryABI->{"SymbolVersion"};
14688 $Tid_TDid{$LibVersion} = $LibraryABI->{"Tid_TDid"};
14689 $Descriptor{$LibVersion}{"Version"} = $LibraryABI->{"LibraryVersion"};
14690 $SkipTypes{$LibVersion} = $LibraryABI->{"SkipTypes"};
14691 if(not $SkipTypes{$LibVersion})
14692 { # support for old dumps
14693 $SkipTypes{$LibVersion} = $LibraryABI->{"OpaqueTypes"};
14694 }
14695 $SkipSymbols{$LibVersion} = $LibraryABI->{"SkipSymbols"};
14696 if(not $SkipSymbols{$LibVersion})
14697 { # support for old dumps
14698 $SkipSymbols{$LibVersion} = $LibraryABI->{"SkipInterfaces"};
14699 }
14700 if(not $SkipSymbols{$LibVersion})
14701 { # support for old dumps
14702 $SkipSymbols{$LibVersion} = $LibraryABI->{"InternalInterfaces"};
14703 }
14704 $SkipNameSpaces{$LibVersion} = $LibraryABI->{"SkipNameSpaces"};
14705 $TargetHeaders{$LibVersion} = $LibraryABI->{"TargetHeaders"};
14706 foreach my $Path (keys(%{$LibraryABI->{"SkipHeaders"}}))
14707 {
14708 my ($CPath, $Type) = classifyPath($Path);
14709 $SkipHeaders{$LibVersion}{$Type}{$CPath} = $LibraryABI->{"SkipHeaders"}{$Path};
14710 $SkipHeadersList{$LibVersion}{$Path} = $LibraryABI->{"SkipHeaders"}{$Path};
14711 }
14712 read_Headers_DumpInfo($LibraryABI, $LibVersion);
14713 read_Libs_DumpInfo($LibraryABI, $LibVersion);
14714 if(not $Descriptor{$LibVersion}{"Libs"})
14715 { # support for old ABI dumps
14716 if(cmpVersions($DumpVersion, "2.10.1")<0)
14717 {
14718 if(not $TargetHeaders{$LibVersion})
14719 {
14720 foreach (keys(%{$Registered_Headers{$LibVersion}})) {
14721 $TargetHeaders{$LibVersion}{get_filename($_)}=1;
14722 }
14723 }
14724 }
14725 }
14726 $Constants{$LibVersion} = $LibraryABI->{"Constants"};
14727 $NestedNameSpaces{$LibVersion} = $LibraryABI->{"NameSpaces"};
14728 if(not $NestedNameSpaces{$LibVersion})
14729 { # support for old dumps
14730 # Cannot reconstruct NameSpaces. This may affect design
14731 # of the compatibility report.
14732 $NestedNameSpaces{$LibVersion} = {};
14733 }
14734 # target system type
14735 # needed to adopt HTML report
14736 if(not $DumpSystem)
14737 { # to use in createSymbolsList(...)
14738 $OStarget = $LibraryABI->{"Target"};
14739 }
14740 # recreate environment
14741 foreach my $Lib_Name (keys(%{$Library_Symbol{$LibVersion}}))
14742 {
14743 foreach my $Interface (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
14744 {
14745 $Symbol_Library{$LibVersion}{$Interface} = $Lib_Name;
14746 if($Library_Symbol{$LibVersion}{$Lib_Name}{$Interface}<=-1)
14747 { # data marked as -size in the dump
14748 $CompleteSignature{$LibVersion}{$Interface}{"Object"} = 1;
14749 }
14750 if($COMMON_LANGUAGE{$LibVersion} ne "C++"
14751 and $Interface=~/\A(_Z|\?)/) {
14752 setLanguage($LibVersion, "C++");
14753 }
14754 }
14755 }
14756 my @VFunc = ();
14757 foreach my $FuncInfoId (keys(%{$SymbolInfo{$LibVersion}}))
14758 {
14759 my $MnglName = $SymbolInfo{$LibVersion}{$FuncInfoId}{"MnglName"};
14760 if(not $MnglName)
14761 { # C-functions
14762 next;
14763 }
14764 if(not $Symbol_Library{$LibVersion}{$MnglName}
14765 and not $DepSymbols{$LibVersion}{$MnglName}) {
14766 push(@VFunc, $MnglName);
14767 }
14768 }
14769 translateSymbols(@VFunc, $LibVersion);
14770 translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
14771 translateSymbols(keys(%{$DepSymbols{$LibVersion}}), $LibVersion);
14772
14773 foreach my $TypeDeclId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
14774 {
14775 foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$TypeDeclId}}))
14776 {
14777 if(defined $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"BaseClass"})
14778 { # support for old ABI dumps < 2.0 (ACC 1.22)
14779 foreach my $BId (keys(%{$TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"BaseClass"}}))
14780 {
14781 if(my $Access = $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"BaseClass"}{$BId})
14782 {
14783 if($Access ne "public") {
14784 $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"Base"}{$BId}{"access"} = $Access;
14785 }
14786 }
14787 $TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"Base"}{$BId} = {};
14788 }
14789 delete($TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}{"BaseClass"});
14790 }
14791 my %TInfo = %{$TypeInfo{$LibVersion}{$TypeDeclId}{$TypeId}};
14792 if(defined $TInfo{"Base"})
14793 {
14794 foreach (keys(%{$TInfo{"Base"}})) {
14795 $Class_SubClasses{$LibVersion}{$_}{$TypeId}=1;
14796 }
14797 }
14798 if($TInfo{"Type"} eq "Typedef")
14799 {
14800 my ($BTDid, $BTid) = ($TInfo{"BaseType"}{"TDid"}, $TInfo{"BaseType"}{"Tid"});
14801 $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}} = $TypeInfo{$LibVersion}{$BTDid}{$BTid}{"Name"};
14802 }
14803 if(not $TName_Tid{$LibVersion}{$TInfo{"Name"}})
14804 { # classes: class (id1), typedef (artificial, id2 > id1)
14805 $TName_Tid{$LibVersion}{$TInfo{"Name"}} = $TypeId;
14806 }
14807 }
14808 }
14809
14810 $Descriptor{$LibVersion}{"Dump"} = 1;
14811}
14812
14813sub read_Machine_DumpInfo($$)
14814{
14815 my ($LibraryABI, $LibVersion) = @_;
14816 if($LibraryABI->{"Arch"}) {
14817 $CPU_ARCH{$LibVersion} = $LibraryABI->{"Arch"};
14818 }
14819 if($LibraryABI->{"WordSize"}) {
14820 $WORD_SIZE{$LibVersion} = $LibraryABI->{"WordSize"};
14821 }
14822 else
14823 { # support for old dumps
14824 $WORD_SIZE{$LibVersion} = $LibraryABI->{"SizeOfPointer"};
14825 }
14826 if(not $WORD_SIZE{$LibVersion})
14827 { # support for old dumps (<1.23)
14828 if(my $Tid = getTypeIdByName("char*", $LibVersion))
14829 { # size of char*
14830 $WORD_SIZE{$LibVersion} = get_TypeSize($Tid, $LibVersion);
14831 }
14832 else
14833 {
14834 my $PSize = 0;
14835 foreach my $TDid (keys(%{$TypeInfo{$LibVersion}}))
14836 {
14837 foreach my $Tid (keys(%{$TypeInfo{$LibVersion}{$TDid}}))
14838 {
14839 if(get_TypeAttr($Tid, $LibVersion, "Type") eq "Pointer")
14840 { # any "pointer"-type
14841 $PSize = get_TypeSize($Tid, $LibVersion);
14842 last;
14843 }
14844 }
14845 if($PSize) {
14846 last;
14847 }
14848 }
14849 if($PSize)
14850 { # a pointer type size
14851 $WORD_SIZE{$LibVersion} = $PSize;
14852 }
14853 else {
14854 printMsg("WARNING", "cannot identify a WORD size in the ABI dump (too old format)");
14855 }
14856 }
14857 }
14858 if($LibraryABI->{"GccVersion"}) {
14859 $GCC_VERSION{$LibVersion} = $LibraryABI->{"GccVersion"};
14860 }
14861}
14862
14863sub read_Libs_DumpInfo($$)
14864{
14865 my ($LibraryABI, $LibVersion) = @_;
14866 if(keys(%{$Library_Symbol{$LibVersion}})
14867 and not $DumpAPI) {
14868 $Descriptor{$LibVersion}{"Libs"} = "OK";
14869 }
14870}
14871
14872sub read_Headers_DumpInfo($$)
14873{
14874 my ($LibraryABI, $LibVersion) = @_;
14875 if(keys(%{$LibraryABI->{"Headers"}})
14876 and not $DumpAPI) {
14877 $Descriptor{$LibVersion}{"Headers"} = "OK";
14878 }
14879 foreach my $Identity (keys(%{$LibraryABI->{"Headers"}}))
14880 { # headers info is stored in the old dumps in the different way
14881 if($UseOldDumps
14882 and my $Name = $LibraryABI->{"Headers"}{$Identity}{"Name"})
14883 { # support for old dumps: headers info corrected in 1.22
14884 $Identity = $Name;
14885 }
14886 $Registered_Headers{$LibVersion}{$Identity}{"Identity"} = $Identity;
14887 }
14888}
14889
14890sub find_libs($$$)
14891{
14892 my ($Path, $Type, $MaxDepth) = @_;
14893 # FIXME: correct the search pattern
14894 return cmd_find($Path, $Type, ".*\\.$LIB_EXT\[0-9.]*", $MaxDepth);
14895}
14896
14897sub createDescriptor($$)
14898{
14899 my ($LibVersion, $Path) = @_;
14900 if(not $LibVersion or not $Path
14901 or not -e $Path) {
14902 return "";
14903 }
14904 if(-d $Path)
14905 { # directory with headers files and shared objects
14906 return "
14907 <version>
14908 ".$TargetVersion{$LibVersion}."
14909 </version>
14910
14911 <headers>
14912 $Path
14913 </headers>
14914
14915 <libs>
14916 $Path
14917 </libs>";
14918 }
14919 else
14920 { # files
14921 if($Path=~/\.xml\Z/i)
14922 { # standard XML-descriptor
14923 return readFile($Path);
14924 }
14925 elsif(is_header($Path, 2, $LibVersion))
14926 { # header file
14927 return "
14928 <version>
14929 ".$TargetVersion{$LibVersion}."
14930 </version>
14931
14932 <headers>
14933 $Path
14934 </headers>
14935
14936 <libs>
14937 none
14938 </libs>";
14939 }
14940 elsif(parse_libname($Path, "name", $OStarget))
14941 { # shared object
14942 return "
14943 <version>
14944 ".$TargetVersion{$LibVersion}."
14945 </version>
14946
14947 <headers>
14948 none
14949 </headers>
14950
14951 <libs>
14952 $Path
14953 </libs>";
14954 }
14955 else
14956 { # standard XML-descriptor
14957 return readFile($Path);
14958 }
14959 }
14960}
14961
14962sub detect_lib_default_paths()
14963{
14964 my %LPaths = ();
14965 if($OSgroup eq "bsd")
14966 {
14967 if(my $LdConfig = get_CmdPath("ldconfig")) {
14968 foreach my $Line (split(/\n/, `$LdConfig -r 2>$TMP_DIR/null`)) {
14969 if($Line=~/\A[ \t]*\d+:\-l(.+) \=\> (.+)\Z/) {
14970 $LPaths{"lib".$1} = $2;
14971 }
14972 }
14973 }
14974 else {
14975 printMsg("WARNING", "can't find ldconfig");
14976 }
14977 }
14978 else
14979 {
14980 if(my $LdConfig = get_CmdPath("ldconfig"))
14981 {
14982 if($SystemRoot and $OSgroup eq "linux")
14983 { # use host (x86) ldconfig with the target (arm) ld.so.conf
14984 if(-e $SystemRoot."/etc/ld.so.conf") {
14985 $LdConfig .= " -f ".$SystemRoot."/etc/ld.so.conf";
14986 }
14987 }
14988 foreach my $Line (split(/\n/, `$LdConfig -p 2>$TMP_DIR/null`)) {
14989 if($Line=~/\A[ \t]*([^ \t]+) .* \=\> (.+)\Z/)
14990 {
14991 my ($Name, $Path) = ($1, $2);
14992 $Path=~s/[\/]{2,}/\//;
14993 $LPaths{$Name} = $Path;
14994 }
14995 }
14996 }
14997 elsif($OSgroup=~/linux/i) {
14998 printMsg("WARNING", "can't find ldconfig");
14999 }
15000 }
15001 return \%LPaths;
15002}
15003
15004sub detect_bin_default_paths()
15005{
15006 my $EnvPaths = $ENV{"PATH"};
15007 if($OSgroup eq "beos") {
15008 $EnvPaths.=":".$ENV{"BETOOLS"};
15009 }
15010 my $Sep = ($OSgroup eq "windows")?";":":|;";
15011 foreach my $Path (sort {length($a)<=>length($b)} split(/$Sep/, $EnvPaths))
15012 {
15013 $Path = path_format($Path, $OSgroup);
15014 $Path=~s/[\/\\]+\Z//g;
15015 next if(not $Path);
15016 if($SystemRoot
15017 and $Path=~/\A\Q$SystemRoot\E\//)
15018 { # do NOT use binaries from target system
15019 next;
15020 }
15021 $DefaultBinPaths{$Path} = 1;
15022 }
15023}
15024
15025sub detect_inc_default_paths()
15026{
15027 return () if(not $GCC_PATH);
15028 my %DPaths = ("Cpp"=>{},"Gcc"=>{},"Inc"=>{});
15029 writeFile("$TMP_DIR/empty.h", "");
15030 foreach my $Line (split(/\n/, `$GCC_PATH -v -x c++ -E "$TMP_DIR/empty.h" 2>&1`))
15031 {# detecting gcc default include paths
15032 if($Line=~/\A[ \t]*((\/|\w+:\\).+)[ \t]*\Z/)
15033 {
15034 my $Path = simplify_path($1);
15035 $Path=~s/[\/\\]+\Z//g;
15036 $Path = path_format($Path, $OSgroup);
15037 if($Path=~/c\+\+|\/g\+\+\//)
15038 {
15039 $DPaths{"Cpp"}{$Path}=1;
15040 if(not defined $MAIN_CPP_DIR
15041 or get_depth($MAIN_CPP_DIR)>get_depth($Path)) {
15042 $MAIN_CPP_DIR = $Path;
15043 }
15044 }
15045 elsif($Path=~/gcc/) {
15046 $DPaths{"Gcc"}{$Path}=1;
15047 }
15048 else
15049 {
15050 next if($Path=~/local[\/\\]+include/);
15051 if($SystemRoot
15052 and $Path!~/\A\Q$SystemRoot\E(\/|\Z)/)
15053 { # The GCC include path for user headers is not a part of the system root
15054 # The reason: you are not specified the --cross-gcc option or selected a wrong compiler
15055 # or it is the internal cross-GCC path like arm-linux-gnueabi/include
15056 next;
15057 }
15058 $DPaths{"Inc"}{$Path}=1;
15059 }
15060 }
15061 }
15062 unlink("$TMP_DIR/empty.h");
15063 return %DPaths;
15064}
15065
15066sub detect_default_paths($)
15067{
15068 my ($HSearch, $LSearch, $BSearch, $GSearch) = (1, 1, 1, 1);
15069 my $Search = $_[0];
15070 if($Search!~/inc/) {
15071 $HSearch = 0;
15072 }
15073 if($Search!~/lib/) {
15074 $LSearch = 0;
15075 }
15076 if($Search!~/bin/) {
15077 $BSearch = 0;
15078 }
15079 if($Search!~/gcc/) {
15080 $GSearch = 0;
15081 }
15082 if(keys(%{$SystemPaths{"include"}}))
15083 { # <search_headers> section of the XML descriptor
15084 # do NOT search for systems headers
15085 $HSearch = 0;
15086 }
15087 if(keys(%{$SystemPaths{"lib"}}))
15088 { # <search_headers> section of the XML descriptor
15089 # do NOT search for systems headers
15090 $LSearch = 0;
15091 }
15092 foreach my $Type (keys(%{$OS_AddPath{$OSgroup}}))
15093 { # additional search paths
15094 next if($Type eq "include" and not $HSearch);
15095 next if($Type eq "lib" and not $LSearch);
15096 next if($Type eq "bin" and not $BSearch);
15097 foreach my $Path (keys(%{$OS_AddPath{$OSgroup}{$Type}}))
15098 {
15099 next if(not -d $Path);
15100 $SystemPaths{$Type}{$Path} = $OS_AddPath{$OSgroup}{$Type}{$Path};
15101 }
15102 }
15103 if($OSgroup ne "windows")
15104 { # unix-like
15105 foreach my $Type ("include", "lib", "bin")
15106 { # automatic detection of system "devel" directories
15107 next if($Type eq "include" and not $HSearch);
15108 next if($Type eq "lib" and not $LSearch);
15109 next if($Type eq "bin" and not $BSearch);
15110 my ($UsrDir, $RootDir) = ("/usr", "/");
15111 if($SystemRoot and $Type ne "bin")
15112 { # 1. search for target headers and libraries
15113 # 2. use host commands: ldconfig, readelf, etc.
15114 ($UsrDir, $RootDir) = ("$SystemRoot/usr", $SystemRoot);
15115 }
15116 foreach my $Path (cmd_find($RootDir,"d","*$Type*",1)) {
15117 $SystemPaths{$Type}{$Path} = 1;
15118 }
15119 if(-d $RootDir."/".$Type)
15120 { # if "/lib" is symbolic link
15121 if($RootDir eq "/") {
15122 $SystemPaths{$Type}{"/".$Type} = 1;
15123 }
15124 else {
15125 $SystemPaths{$Type}{$RootDir."/".$Type} = 1;
15126 }
15127 }
15128 if(-d $UsrDir) {
15129 foreach my $Path (cmd_find($UsrDir,"d","*$Type*",1)) {
15130 $SystemPaths{$Type}{$Path} = 1;
15131 }
15132 if(-d $UsrDir."/".$Type)
15133 { # if "/usr/lib" is symbolic link
15134 $SystemPaths{$Type}{$UsrDir."/".$Type} = 1;
15135 }
15136 }
15137 }
15138 }
15139 if($BSearch)
15140 {
15141 detect_bin_default_paths();
15142 foreach my $Path (keys(%DefaultBinPaths)) {
15143 $SystemPaths{"bin"}{$Path} = $DefaultBinPaths{$Path};
15144 }
15145 }
15146 # check environment variables
15147 if($OSgroup eq "beos")
15148 {
15149 foreach (keys(%{$SystemPaths{"bin"}}))
15150 {
15151 if($_ eq ".") {
15152 next;
15153 }
15154 foreach my $Path (cmd_find($_, "d", "bin", ""))
15155 { # search for /boot/develop/abi/x86/gcc4/tools/gcc-4.4.4-haiku-101111/bin/
15156 $SystemPaths{"bin"}{$Path} = 1;
15157 }
15158 }
15159 if($HSearch)
15160 {
15161 foreach my $Path (split(/:|;/, $ENV{"BEINCLUDES"}))
15162 {
15163 if(is_abs($Path)) {
15164 $DefaultIncPaths{$Path} = 1;
15165 }
15166 }
15167 }
15168 if($LSearch)
15169 {
15170 foreach my $Path (split(/:|;/, $ENV{"BELIBRARIES"}), split(/:|;/, $ENV{"LIBRARY_PATH"}))
15171 {
15172 if(is_abs($Path)) {
15173 $DefaultLibPaths{$Path} = 1;
15174 }
15175 }
15176 }
15177 }
15178 if($LSearch)
15179 { # using linker to get system paths
15180 if(my $LPaths = detect_lib_default_paths())
15181 { # unix-like
15182 foreach my $Name (keys(%{$LPaths}))
15183 {
15184 if($SystemRoot
15185 and $LPaths->{$Name}!~/\A\Q$SystemRoot\E\//)
15186 { # wrong ldconfig configuration
15187 # check your <sysroot>/etc/ld.so.conf
15188 next;
15189 }
15190 $DyLib_DefaultPath{$Name} = $LPaths->{$Name};
15191 $DefaultLibPaths{get_dirname($LPaths->{$Name})} = 1;
15192 }
15193 }
15194 foreach my $Path (keys(%DefaultLibPaths)) {
15195 $SystemPaths{"lib"}{$Path} = $DefaultLibPaths{$Path};
15196 }
15197 }
15198 if($BSearch)
15199 {
15200 if($CrossGcc)
15201 { # --cross-gcc=arm-linux-gcc
15202 if(-e $CrossGcc)
15203 { # absolute or relative path
15204 $GCC_PATH = get_abs_path($CrossGcc);
15205 }
15206 elsif($CrossGcc!~/\// and get_CmdPath($CrossGcc))
15207 { # command name
15208 $GCC_PATH = $CrossGcc;
15209 }
15210 else {
15211 exitStatus("Access_Error", "can't access \'$CrossGcc\'");
15212 }
15213 if($GCC_PATH=~/\s/) {
15214 $GCC_PATH = "\"".$GCC_PATH."\"";
15215 }
15216 }
15217 }
15218 if($GSearch)
15219 { # GCC path and default include dirs
15220 if(not $CrossGcc) {
15221 $GCC_PATH = get_CmdPath("gcc");
15222 }
15223 if(not $GCC_PATH) {
15224 exitStatus("Not_Found", "can't find GCC>=3.0 in PATH");
15225 }
15226 if(not $CheckObjectsOnly_Opt)
15227 {
15228 if(my $GCC_Ver = get_dumpversion($GCC_PATH))
15229 {
15230 my $GccTarget = get_dumpmachine($GCC_PATH);
15231 printMsg("INFO", "Using GCC $GCC_Ver ($GccTarget)");
15232 if($GccTarget=~/symbian/)
15233 {
15234 $OStarget = "symbian";
15235 $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget};
15236 }
15237 }
15238 else {
15239 exitStatus("Error", "something is going wrong with the GCC compiler");
15240 }
15241 }
15242 if(not $NoStdInc)
15243 { # do NOT search in GCC standard paths
15244 my %DPaths = detect_inc_default_paths();
15245 %DefaultCppPaths = %{$DPaths{"Cpp"}};
15246 %DefaultGccPaths = %{$DPaths{"Gcc"}};
15247 %DefaultIncPaths = %{$DPaths{"Inc"}};
15248 foreach my $Path (keys(%DefaultIncPaths)) {
15249 $SystemPaths{"include"}{$Path} = $DefaultIncPaths{$Path};
15250 }
15251 }
15252 }
15253 if($HSearch)
15254 { # user include paths
15255 if(-d $SystemRoot."/usr/include") {
15256 $UserIncPath{$SystemRoot."/usr/include"}=1;
15257 }
15258 }
15259}
15260
15261sub getLIB_EXT($)
15262{
15263 my $Target = $_[0];
15264 if(my $Ext = $OS_LibExt{$LIB_TYPE}{$Target}) {
15265 return $Ext;
15266 }
15267 return $OS_LibExt{$LIB_TYPE}{"default"};
15268}
15269
15270sub getAR_EXT($)
15271{
15272 my $Target = $_[0];
15273 if(my $Ext = $OS_Archive{$Target}) {
15274 return $Ext;
15275 }
15276 return $OS_Archive{"default"};
15277}
15278
15279sub get_dumpversion($)
15280{
15281 my $Cmd = $_[0];
15282 return "" if(not $Cmd);
15283 if($Cache{"get_dumpversion"}{$Cmd}) {
15284 return $Cache{"get_dumpversion"}{$Cmd};
15285 }
15286 my $V = `$Cmd -dumpversion 2>$TMP_DIR/null`;
15287 chomp($V);
15288 return ($Cache{"get_dumpversion"}{$Cmd} = $V);
15289}
15290
15291sub get_dumpmachine($)
15292{
15293 my $Cmd = $_[0];
15294 return "" if(not $Cmd);
15295 if($Cache{"get_dumpmachine"}{$Cmd}) {
15296 return $Cache{"get_dumpmachine"}{$Cmd};
15297 }
15298 my $Machine = `$Cmd -dumpmachine 2>$TMP_DIR/null`;
15299 chomp($Machine);
15300 return ($Cache{"get_dumpmachine"}{$Cmd} = $Machine);
15301}
15302
15303sub check_command($)
15304{
15305 my $Cmd = $_[0];
15306 return "" if(not $Cmd);
15307 my @Options = (
15308 "--version",
15309 "-help"
15310 );
15311 foreach my $Opt (@Options)
15312 {
15313 my $Info = `$Cmd $Opt 2>$TMP_DIR/null`;
15314 if($Info) {
15315 return 1;
15316 }
15317 }
15318 return 0;
15319}
15320
15321sub check_gcc_version($$)
15322{
15323 my ($Cmd, $Req_V) = @_;
15324 return 0 if(not $Cmd or not $Req_V);
15325 my $Gcc_V = get_dumpversion($Cmd);
15326 $Gcc_V=~s/(-|_)[a-z_]+.*\Z//; # remove suffix (like "-haiku-100818")
15327 if(cmpVersions($Gcc_V, $Req_V)>=0) {
15328 return $Cmd;
15329 }
15330 return "";
15331}
15332
15333sub get_depth($)
15334{
15335 if(defined $Cache{"get_depth"}{$_[0]}) {
15336 return $Cache{"get_depth"}{$_[0]}
15337 }
15338 return ($Cache{"get_depth"}{$_[0]} = ($_[0]=~tr![\/\\]|\:\:!!));
15339}
15340
15341sub find_gcc_cxx_headers($)
15342{
15343 my $LibVersion = $_[0];
15344 return if($Cache{"find_gcc_cxx_headers"});# this function should be called once
15345 # detecting system header paths
15346 foreach my $Path (sort {get_depth($b) <=> get_depth($a)} keys(%DefaultGccPaths))
15347 {
15348 foreach my $HeaderPath (sort {get_depth($a) <=> get_depth($b)} cmd_find($Path,"f","",""))
15349 {
15350 my $FileName = get_filename($HeaderPath);
15351 next if($DefaultGccHeader{$FileName});
15352 $DefaultGccHeader{$FileName} = $HeaderPath;
15353 }
15354 }
15355 if($COMMON_LANGUAGE{$LibVersion} eq "C++" and not $STDCXX_TESTING)
15356 {
15357 foreach my $CppDir (sort {get_depth($b)<=>get_depth($a)} keys(%DefaultCppPaths))
15358 {
15359 my @AllCppHeaders = cmd_find($CppDir,"f","","");
15360 foreach my $Path (sort {get_depth($a)<=>get_depth($b)} @AllCppHeaders)
15361 {
15362 my $FileName = get_filename($Path);
15363 next if($DefaultCppHeader{$FileName});
15364 $DefaultCppHeader{$FileName} = $Path;
15365 }
15366 }
15367 }
15368 $Cache{"find_gcc_cxx_headers"} = 1;
15369}
15370
15371sub parse_libname($$$)
15372{
15373 my ($Name, $Type, $Target) = @_;
15374 if($Target eq "symbian") {
15375 return parse_libname_symbian($Name, $Type);
15376 }
15377 elsif($Target eq "windows") {
15378 return parse_libname_windows($Name, $Type);
15379 }
15380 my $Ext = getLIB_EXT($Target);
15381 if($Name=~/((((lib|).+?)([\-\_][\d\-\.\_]+|))\.$Ext)(\.(.+)|)\Z/)
15382 { # libSDL-1.2.so.0.7.1
15383 # libwbxml2.so.0.0.18
15384 if($Type eq "name")
15385 { # libSDL-1.2
15386 # libwbxml2
15387 return $2;
15388 }
15389 elsif($Type eq "name+ext")
15390 { # libSDL-1.2.so
15391 # libwbxml2.so
15392 return $1;
15393 }
15394 elsif($Type eq "version")
15395 {
15396 if($7 ne "")
15397 { # 0.7.1
15398 return $7;
15399 }
15400 else
15401 { # libc-2.5.so (=>2.5 version)
15402 my $MV = $5;
15403 $MV=~s/\A[\-\_]+//g;
15404 return $MV;
15405 }
15406 }
15407 elsif($Type eq "short")
15408 { # libSDL
15409 # libwbxml2
15410 return $3;
15411 }
15412 elsif($Type eq "shortest")
15413 { # SDL
15414 # wbxml
15415 return shortest_name($3);
15416 }
15417 }
15418 return "";# error
15419}
15420
15421sub parse_libname_symbian($$)
15422{
15423 my ($Name, $Type) = @_;
15424 my $Ext = getLIB_EXT("symbian");
15425 if($Name=~/(((.+?)(\{.+\}|))\.$Ext)\Z/)
15426 { # libpthread{00010001}.dso
15427 if($Type eq "name")
15428 { # libpthread{00010001}
15429 return $2;
15430 }
15431 elsif($Type eq "name+ext")
15432 { # libpthread{00010001}.dso
15433 return $1;
15434 }
15435 elsif($Type eq "version")
15436 { # 00010001
15437 my $V = $4;
15438 $V=~s/\{(.+)\}/$1/;
15439 return $V;
15440 }
15441 elsif($Type eq "short")
15442 { # libpthread
15443 return $3;
15444 }
15445 elsif($Type eq "shortest")
15446 { # pthread
15447 return shortest_name($3);
15448 }
15449 }
15450 return "";# error
15451}
15452
15453sub parse_libname_windows($$)
15454{
15455 my ($Name, $Type) = @_;
15456 my $Ext = getLIB_EXT("windows");
15457 if($Name=~/((.+?)\.$Ext)\Z/)
15458 { # netapi32.dll
15459 if($Type eq "name")
15460 { # netapi32
15461 return $2;
15462 }
15463 elsif($Type eq "name+ext")
15464 { # netapi32.dll
15465 return $1;
15466 }
15467 elsif($Type eq "version")
15468 { # DLL version embedded
15469 # at binary-level
15470 return "";
15471 }
15472 elsif($Type eq "short")
15473 { # netapi32
15474 return $2;
15475 }
15476 elsif($Type eq "shortest")
15477 { # netapi
15478 return shortest_name($2);
15479 }
15480 }
15481 return "";# error
15482}
15483
15484sub shortest_name($)
15485{
15486 my $Name = $_[0];
15487 # remove prefix
15488 $Name=~s/\A(lib|open)//;
15489 # remove suffix
15490 $Name=~s/[\W\d_]+\Z//i;
15491 $Name=~s/([a-z]{2,})(lib)\Z/$1/i;
15492 return $Name;
15493}
15494
15495sub getPrefix($)
15496{
15497 my $Str = $_[0];
15498 if($Str=~/\A(Get|get|Set|set)([A-Z]|_)/)
15499 {# GetError
15500 return "";
15501 }
15502 if($Str=~/\A([_]*[A-Z][a-z]{1,5})[A-Z]/)
15503 {# XmuValidArea: Xmu
15504 return $1;
15505 }
15506 elsif($Str=~/\A([_]*[a-z]+)[A-Z]/)
15507 {# snfReadFont: snf
15508 return $1;
15509 }
15510 elsif($Str=~/\A([_]*[A-Z]{2,})[A-Z][a-z]+([A-Z][a-z]+|\Z)/)
15511 {# XRRTimes: XRR
15512 return $1;
15513 }
15514 elsif($Str=~/\A([_]*[a-z0-9]{2,}_)[a-z]+/i)
15515 {# alarm_event_add: alarm_
15516 return $1;
15517 }
15518 elsif($Str=~/\A(([a-z])\2{1,})/i)
15519 {# ffopen
15520 return $1;
15521 }
15522 else {
15523 return "";
15524 }
15525}
15526
15527sub problem_title($)
15528{
15529 if($_[0]==1) {
15530 return "1 problem";
15531 }
15532 else {
15533 return $_[0]." problems";
15534 }
15535}
15536
15537sub warning_title($)
15538{
15539 if($_[0]==1) {
15540 return "1 warning";
15541 }
15542 else {
15543 return $_[0]." warnings";
15544 }
15545}
15546
15547sub createSymbolsList($$$$$)
15548{
15549 my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_;
15550 read_ABI_Dump(1, $DPath);
15551 if(not $CheckObjectsOnly) {
15552 prepareInterfaces(1);
15553 }
15554 my %SymbolHeaderLib = ();
15555 my $Total = 0;
15556 # Get List
15557 foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
15558 {
15559 if(not link_symbol($Symbol, 1, "-Deps"))
15560 {# skip src only and all external functions
15561 next;
15562 }
15563 if(not symbolFilter($Symbol, 1, "Public"))
15564 { # skip other symbols
15565 next;
15566 }
15567 my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
15568 if(not $HeaderName)
15569 {# skip src only and all external functions
15570 next;
15571 }
15572 my $DyLib = $Symbol_Library{1}{$Symbol};
15573 if(not $DyLib)
15574 {# skip src only and all external functions
15575 next;
15576 }
15577 $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1;
15578 $Total+=1;
15579 }
15580 # Draw List
15581 my $SYMBOLS_LIST = "<h1>Public symbols in <span style='color:Blue;'>$LName</span> (<span style='color:Red;'>$LVersion</span>)";
15582 $SYMBOLS_LIST .= " on <span style='color:Blue;'>".showArch($ArchName)."</span><br/>Total: $Total</h1><br/>";
15583 foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib))
15584 {
15585 foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}}))
15586 {
15587 if($HeaderName and $DyLib) {
15588 $SYMBOLS_LIST .= "<span class='h_name'>$HeaderName</span>, <span class='lib_name'>$DyLib</span><br/>\n";
15589 }
15590 elsif($DyLib) {
15591 $SYMBOLS_LIST .= "<span class='lib_name'>$DyLib</span><br/>\n";
15592 }
15593 elsif($HeaderName) {
15594 $SYMBOLS_LIST .= "<span class='h_name'>$HeaderName</span><br/>\n";
15595 }
15596 my %NS_Symbol = ();
15597 foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) {
15598 $NS_Symbol{get_IntNameSpace($Symbol, 1)}{$Symbol} = 1;
15599 }
15600 foreach my $NameSpace (sort keys(%NS_Symbol))
15601 {
15602 $SYMBOLS_LIST .= ($NameSpace)?"<span class='ns_title'>namespace</span> <span class='ns'>$NameSpace</span>"."<br/>\n":"";
15603 my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NS_Symbol{$NameSpace}});
15604 foreach my $Symbol (@SortedInterfaces)
15605 {
15606 my $SubReport = "";
15607 my $Signature = get_Signature($Symbol, 1);
15608 if($NameSpace) {
15609 $Signature=~s/(\W|\A)\Q$NameSpace\E\:\:(\w)/$1$2/g;
15610 }
15611 if($Symbol=~/\A(_Z|\?)/)
15612 {
15613 if($Signature) {
15614 $SubReport = insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[ symbol: <b>$Symbol</b> ]</span><br/><br/>".$ContentDivEnd."\n");
15615 }# report_added
15616 else {
15617 $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
15618 }
15619 }
15620 else
15621 {
15622 if($Signature) {
15623 $SubReport = "<span class='iname'>".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
15624 }
15625 else {
15626 $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
15627 }
15628 }
15629 $SYMBOLS_LIST .= $SubReport;
15630 }
15631 }
15632 $SYMBOLS_LIST .= "<br/>\n";
15633 }
15634 }
15635 # Clear Info
15636 (%TypeInfo, %SymbolInfo, %Library_Symbol,
15637 %DepSymbols, %SymVer, %Tid_TDid, %SkipTypes,
15638 %SkipSymbols, %NestedNameSpaces, %ClassMethods,
15639 %AllocableClass, %ClassToId, %CompleteSignature,
15640 %SkipNameSpaces, %Symbol_Library) = ();
15641 ($Content_Counter, $ContentID) = (0, 0);
15642 # Print Report
15643 my $CssStyles = readStyles("SymbolsList.css");
15644 $SYMBOLS_LIST = "<a name='Top'></a>".$SYMBOLS_LIST."<a style='font-size:11px;' href='#Top'>to the top</a><br/>\n";
15645 my $Title = "$LName: public symbols";
15646 my $Keywords = "$LName, API, symbols";
15647 my $Description = "List of symbols in $LName ($LVersion) on ".showArch($ArchName);
15648 $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Description, $CssStyles."\n".$JScripts)."
15649 <body><div>\n$SYMBOLS_LIST</div>
15650 <br/><br/><hr/>\n".getReportFooter($LName)."
15651 <div style='height:999px;'></div></body></html>";
15652 writeFile($SaveTo, $SYMBOLS_LIST);
15653}
15654
15655sub readStyles($)
15656{
15657 my $Name = $_[0];
15658 my $Path = $MODULES_DIR."/Internals/Styles/".$Name;
15659 if(not -f $Path) {
15660 exitStatus("Module_Error", "can't access \'$Path\'");
15661 }
15662 my $Styles = readFile($Path);
15663 return "<style type=\"text/css\">\n".$Styles."\n</style>";
15664}
15665
15666sub is_target_lib($)
15667{
15668 my $LName = $_[0];
15669 if($TargetLibraryName
15670 and $LName!~/\Q$TargetLibraryName\E/) {
15671 return 0;
15672 }
15673 if(keys(%TargetLibs)
15674 and not $TargetLibs{$LName}
15675 and not $TargetLibs{parse_libname($LName, "name+ext", $OStarget)}) {
15676 return 0;
15677 }
15678 return 1;
15679}
15680
15681sub is_target_header($)
15682{ # --header, --headers-list
15683 if(keys(%{$TargetHeaders{1}})
15684 or keys(%{$TargetHeaders{2}}))
15685 {
15686 if(not $TargetHeaders{1}{$_[0]}
15687 and not $TargetHeaders{2}{$_[0]})
15688 {
15689 return 0;
15690 }
15691 }
15692 return 1;
15693}
15694
15695sub checkVersionNum($$)
15696{
15697 my ($LibVersion, $Path) = @_;
15698 if(my $VerNum = $TargetVersion{$LibVersion}) {
15699 return $VerNum;
15700 }
15701 my $UsedAltDescr = 0;
15702 foreach my $Part (split(/\s*,\s*/, $Path))
15703 {# try to get version string from file path
15704 next if($Part=~/\.xml\Z/i);
15705 next if(isDump($Part));
15706 if(parse_libname($Part, "version", $OStarget)
15707 or is_header($Part, 2, $LibVersion) or -d $Part)
15708 {
15709 $UsedAltDescr = 1;
15710 if(my $VerNum = readStringVersion($Part))
15711 {
15712 $TargetVersion{$LibVersion} = $VerNum;
15713 if($DumpAPI) {
15714 printMsg("WARNING", "setting version number to $VerNum (use -vnum <num> option to change it)");
15715 }
15716 else {
15717 printMsg("WARNING", "setting ".($LibVersion==1?"1st":"2nd")." version number to \"$VerNum\" (use -v$LibVersion <num> option to change it)");
15718 }
15719 return $TargetVersion{$LibVersion};
15720 }
15721 }
15722 }
15723 if($UsedAltDescr)
15724 {
15725 if($DumpAPI) {
15726 exitStatus("Error", "version number is not set (use -vnum <num> option)");
15727 }
15728 else {
15729 exitStatus("Error", ($LibVersion==1?"1st":"2nd")." version number is not set (use -v$LibVersion <num> option)");
15730 }
15731 }
15732}
15733
15734sub readStringVersion($)
15735{
15736 my $Str = $_[0];
15737 return "" if(not $Str);
15738 $Str=~s/\Q$TargetLibraryName\E//g;
15739 if($Str=~/(\/|\\|\w|\A)[\-\_]*(\d+[\d\.\-]+\d+|\d+)/)
15740 {# .../libssh-0.4.0/...
15741 return $2;
15742 }
15743 elsif(my $V = parse_libname($Str, "version", $OStarget)) {
15744 return $V;
15745 }
15746 return "";
15747}
15748
15749sub readLibs($)
15750{
15751 my $LibVersion = $_[0];
15752 if($OStarget eq "windows")
15753 { # dumpbin.exe will crash
15754 # without VS Environment
15755 check_win32_env();
15756 }
15757 getSymbols($LibVersion);
15758 translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
15759 translateSymbols(keys(%{$DepSymbols{$LibVersion}}), $LibVersion);
15760}
15761
15762sub dump_sorting($)
15763{
15764 my $hash = $_[0];
15765 return [] if(not $hash or not keys(%{$hash}));
15766 if((keys(%{$hash}))[0]=~/\A\d+\Z/) {
15767 return [sort {int($a) <=> int($b)} keys(%{$hash})];
15768 }
15769 else {
15770 return [sort {$a cmp $b} keys(%{$hash})];
15771 }
15772}
15773
15774sub printMsg($$)
15775{
15776 my ($Type, $Msg) = @_;
15777 if($Type!~/\AINFO/) {
15778 $Msg = $Type.": ".$Msg;
15779 }
15780 if($Type!~/_C\Z/) {
15781 $Msg .= "\n";
15782 }
15783 if($Quiet)
15784 { # --quiet option
15785 appendFile($COMMON_LOG_PATH, $Msg);
15786 }
15787 else
15788 {
15789 if($Type eq "ERROR") {
15790 print STDERR $Msg;
15791 }
15792 else {
15793 print $Msg;
15794 }
15795 }
15796}
15797
15798sub exitStatus($$)
15799{
15800 my ($Code, $Msg) = @_;
15801 printMsg("ERROR", $Msg);
15802 exit($ERROR_CODE{$Code});
15803}
15804
15805sub exitReport()
15806{ # the tool has run without any errors
15807 printReport();
15808 if($COMPILE_ERRORS)
15809 { # errors in headers may add false positives/negatives
15810 exit($ERROR_CODE{"Compile_Error"});
15811 }
15812 if($RESULT{"Problems"}) {
15813 exit($ERROR_CODE{"Incompatible"});
15814 }
15815 else {
15816 exit($ERROR_CODE{"Compatible"});
15817 }
15818}
15819
15820sub readRules($)
15821{
15822 my $Kind = $_[0];
15823 if(not -f $RULES_PATH{$Kind}) {
15824 exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'");
15825 }
15826 my $Content = readFile($RULES_PATH{$Kind});
15827 while(my $Rule = parseTag(\$Content, "rule"))
15828 {
15829 my $RId = parseTag(\$Rule, "id");
15830 my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind");
15831 foreach my $Prop (@Properties) {
15832 if(my $Value = parseTag(\$Rule, lc($Prop)))
15833 {
15834 $Value=~s/\n[ ]*//;
15835 $CompatRules{$Kind}{$RId}{$Prop} = $Value;
15836 }
15837 }
15838 if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) {
15839 $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols";
15840 }
15841 else {
15842 $CompatRules{$Kind}{$RId}{"Kind"} = "Types";
15843 }
15844 }
15845}
15846
15847sub printReport()
15848{
15849 printMsg("INFO", "creating compatibility report ...");
15850 my $ReportPath = $OutputReportPath;
15851 if(not $ReportPath) {
15852 $ReportPath = "compat_reports/$TargetLibraryName/".$Descriptor{1}{"Version"}."_to_".$Descriptor{2}{"Version"}."/abi_compat_report.$ReportFormat";
15853 }
15854 createHtmlReport($ReportPath);
15855 if($ListAffected)
15856 { # --list-affected
15857 my $List = "";
15858 foreach (keys(%{$TotalAffected{"Binary"}}))
15859 {
15860 if($StrictCompat and $TotalAffected{"Binary"}{$_} eq "Low")
15861 { # skip "Low"-severity problems
15862 next;
15863 }
15864 $List .= "$_\n";
15865 }
15866 writeFile(get_dirname($ReportPath)."/abi_affected.txt", $List);
15867 }
15868 if($RESULT{"Problems"}) {
15869 printMsg("INFO", "result: INCOMPATIBLE ".$RESULT{"Affected"}."\% (total problems: ".$RESULT{"Problems"}.", warnings: ".$RESULT{"Warnings"}.")");
15870 }
15871 else {
15872 printMsg("INFO", "result: COMPATIBLE (total problems: ".$RESULT{"Problems"}.", warnings: ".$RESULT{"Warnings"}.")");
15873 }
15874 if($StdOut) {
15875 printMsg("INFO", "compatibility report has been generated to stdout");
15876 }
15877 else {
15878 printMsg("INFO", "see detailed report:\n $ReportPath");
15879 }
15880}
15881
15882sub check_win32_env()
15883{
15884 if(not $ENV{"DevEnvDir"}
15885 or not $ENV{"LIB"}) {
15886 exitStatus("Error", "can't start without VS environment (vsvars32.bat)");
15887 }
15888}
15889
15890sub create_ABI_Dump()
15891{
15892 if(not -e $DumpAPI) {
15893 exitStatus("Access_Error", "can't access \'$DumpAPI\'");
15894 }
15895 # check the archive utilities
15896 if($OSgroup eq "windows")
15897 { # using zip
15898 my $ZipCmd = get_CmdPath("zip");
15899 if(not $ZipCmd) {
15900 exitStatus("Not_Found", "can't find \"zip\"");
15901 }
15902 }
15903 else
15904 { # using tar and gzip
15905 my $TarCmd = get_CmdPath("tar");
15906 if(not $TarCmd) {
15907 exitStatus("Not_Found", "can't find \"tar\"");
15908 }
15909 my $GzipCmd = get_CmdPath("gzip");
15910 if(not $GzipCmd) {
15911 exitStatus("Not_Found", "can't find \"gzip\"");
15912 }
15913 }
15914 my @DParts = split(/\s*,\s*/, $DumpAPI);
15915 foreach my $Part (@DParts)
15916 {
15917 if(not -e $Part) {
15918 exitStatus("Access_Error", "can't access \'$Part\'");
15919 }
15920 }
15921 checkVersionNum(1, $DumpAPI);
15922 foreach my $Part (@DParts)
15923 {
15924 if(isDump($Part)) {
15925 read_ABI_Dump(1, $Part);
15926 }
15927 else {
15928 readDescriptor(1, createDescriptor(1, $Part));
15929 }
15930 }
15931 initLogging(1);
15932 detect_default_paths("inc|lib|bin|gcc"); # complete analysis
15933 if(not $CheckHeadersOnly) {
15934 readLibs(1);
15935 }
15936 if($CheckHeadersOnly) {
15937 setLanguage(1, "C++");
15938 }
15939 if(not $CheckObjectsOnly) {
15940 searchForHeaders(1);
15941 }
15942 $WORD_SIZE{1} = detectWordSize();
15943 if($Descriptor{1}{"Headers"}
15944 and not $Descriptor{1}{"Dump"}) {
15945 readHeaders(1);
15946 }
15947 if($ExtendedCheck)
15948 { # --ext option
15949 addExtension(1);
15950 }
15951 formatDump(1);
15952 if(not keys(%{$SymbolInfo{1}}))
15953 { # check if created dump is valid
15954 if(not $ExtendedCheck and not $CheckObjectsOnly)
15955 {
15956 if($CheckHeadersOnly) {
15957 exitStatus("Empty_Set", "the set of public symbols is empty");
15958 }
15959 else {
15960 exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection");
15961 }
15962 }
15963 }
15964 my %HeadersInfo = ();
15965 foreach my $HPath (keys(%{$Registered_Headers{1}}))
15966 { # headers info stored without paths in the dump
15967 $HeadersInfo{$Registered_Headers{1}{$HPath}{"Identity"}} = $Registered_Headers{1}{$HPath}{"Pos"};
15968 }
15969 printMsg("INFO", "creating library ABI dump ...");
15970 my %LibraryABI = (
15971 "TypeInfo" => $TypeInfo{1},
15972 "SymbolInfo" => $SymbolInfo{1},
15973 "Symbols" => $Library_Symbol{1},
15974 "DepSymbols" => $DepSymbols{1},
15975 "SymbolVersion" => $SymVer{1},
15976 "LibraryVersion" => $Descriptor{1}{"Version"},
15977 "LibraryName" => $TargetLibraryName,
15978 "Language" => $COMMON_LANGUAGE{1},
15979 "Tid_TDid" => $Tid_TDid{1},
15980 "SkipTypes" => $SkipTypes{1},
15981 "SkipSymbols" => $SkipSymbols{1},
15982 "SkipNameSpaces" => $SkipNameSpaces{1},
15983 "SkipHeaders" => $SkipHeadersList{1},
15984 "TargetHeaders" => $TargetHeaders{1},
15985 "Headers" => \%HeadersInfo,
15986 "Constants" => $Constants{1},
15987 "NameSpaces" => $NestedNameSpaces{1},
15988 "Target" => $OStarget,
15989 "Arch" => getArch(1),
15990 "WordSize" => $WORD_SIZE{1},
15991 "GccVersion" => get_dumpversion($GCC_PATH),
15992 "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION,
15993 "ABI_COMPLIANCE_CHECKER_VERSION" => $TOOL_VERSION
15994 );
15995 if($ExtendedCheck)
15996 { # --ext option
15997 $LibraryABI{"Mode"} = "Extended";
15998 }
15999 if($StdOut)
16000 { # --stdout option
16001 print STDOUT Dumper(\%LibraryABI);
16002 printMsg("INFO", "ABI dump has been generated to stdout");
16003 return;
16004 }
16005 else
16006 { # write to gzipped file
16007 my $DumpPath = "abi_dumps/$TargetLibraryName/".$TargetLibraryName."_".$Descriptor{1}{"Version"}.".abi.".$AR_EXT;
16008 if($OutputDumpPath)
16009 { # user defined path
16010 $DumpPath = $OutputDumpPath;
16011 }
16012 if(not $DumpPath=~s/\Q.$AR_EXT\E\Z//g) {
16013 exitStatus("Error", "the dump path (-dump-path option) should be the path to a *.$AR_EXT file");
16014 }
16015 my ($DDir, $DName) = separate_path($DumpPath);
16016 my $DPath = $TMP_DIR."/".$DName;
16017 mkpath($DDir);
16018 writeFile($DPath, Dumper(\%LibraryABI));
16019 if(not -s $DPath) {
16020 exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module");
16021 }
16022 my $Pkg = createArchive($DPath, $DDir);
16023 printMsg("INFO", "library ABI has been dumped to:\n $Pkg");
16024 printMsg("INFO", "you can transfer this dump everywhere and use instead of the ".$Descriptor{1}{"Version"}." version descriptor");
16025 }
16026}
16027
16028sub quickEmptyReports()
16029{ # Quick "empty" reports
16030 # 4 times faster than merging equal dumps
16031 # NOTE: the dump contains the "LibraryVersion" attribute
16032 # if you change the version, then your dump will be different
16033 # OVERCOME: use -v1 and v2 options for comparing dumps
16034 # and don't change version in the XML descriptor (and dumps)
16035 # OVERCOME 2: separate meta info from the dumps in ACC 2.0
16036 if(-s $Descriptor{1}{"Path"} == -s $Descriptor{2}{"Path"})
16037 {
16038 my $FilePath1 = unpackDump($Descriptor{1}{"Path"});
16039 my $FilePath2 = unpackDump($Descriptor{2}{"Path"});
16040 if($FilePath1 and $FilePath2)
16041 {
16042 my $Content = readFile($FilePath1);
16043 if($Content eq readFile($FilePath2))
16044 {
16045 # read a number of headers, libs, symbols and types
16046 my $ABIdump = eval($Content);
16047 if(not $ABIdump) {
16048 exitStatus("Error", "internal error");
16049 }
16050 if(not $ABIdump->{"TypeInfo"})
16051 {# support for old dumps
16052 $ABIdump->{"TypeInfo"} = $ABIdump->{"TypeDescr"};
16053 }
16054 if(not $ABIdump->{"SymbolInfo"})
16055 {# support for old dumps
16056 $ABIdump->{"SymbolInfo"} = $ABIdump->{"FuncDescr"};
16057 }
16058 read_Headers_DumpInfo($ABIdump, 1);
16059 read_Libs_DumpInfo($ABIdump, 1);
16060 read_Machine_DumpInfo($ABIdump, 1);
16061 read_Machine_DumpInfo($ABIdump, 2);
16062 %CheckedTypes = %{$ABIdump->{"TypeInfo"}};
16063 %CheckedSymbols = %{$ABIdump->{"SymbolInfo"}};
16064 $Descriptor{1}{"Version"} = $TargetVersion{1}?$TargetVersion{1}:$ABIdump->{"LibraryVersion"};
16065 $Descriptor{2}{"Version"} = $TargetVersion{2}?$TargetVersion{2}:$ABIdump->{"LibraryVersion"};
16066 exitReport();
16067 }
16068 }
16069 }
16070}
16071
16072sub initLogging($)
16073{
16074 my $LibVersion = $_[0];
16075 # create log directory
16076 my ($LOG_DIR, $LOG_FILE) = ("logs/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"}, "log.txt");
16077 if($OutputLogPath{$LibVersion})
16078 { # user-defined by -log-path option
16079 ($LOG_DIR, $LOG_FILE) = separate_path($OutputLogPath{$LibVersion});
16080 }
16081 if($LogMode ne "n") {
16082 mkpath($LOG_DIR);
16083 }
16084 $LOG_PATH{$LibVersion} = get_abs_path($LOG_DIR)."/".$LOG_FILE;
16085 resetLogging($LibVersion);
16086 if($Debug)
16087 { # debug directory
16088 $DEBUG_PATH{$LibVersion} = "debug/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"};
16089 rmtree($DEBUG_PATH{$LibVersion});
16090 }
16091}
16092
16093sub writeLog($$)
16094{
16095 my ($LibVersion, $Msg) = @_;
16096 if($LogMode ne "n") {
16097 appendFile($LOG_PATH{$LibVersion}, $Msg);
16098 }
16099}
16100
16101sub resetLogging($)
16102{
16103 my $LibVersion = $_[0];
16104 if($LogMode!~/a|n/)
16105 { # remove old log
16106 unlink($LOG_PATH{$LibVersion});
16107 }
16108}
16109
16110sub printErrorLog($)
16111{
16112 my $LibVersion = $_[0];
16113 if($LogMode ne "n") {
16114 printMsg("ERROR", "see log for details:\n ".$LOG_PATH{$LibVersion}."\n");
16115 }
16116}
16117
16118sub isDump($)
16119{
16120 if(get_filename($_[0])=~/\A(.+)\.abi(\Q.tar.gz\E|\Q.zip\E|)\Z/)
16121 { # returns a name of package
16122 return $1;
16123 }
16124 return 0;
16125}
16126
16127sub compareAPIs()
16128{
16129 # read input XML descriptors or ABI dumps
16130 if(not $Descriptor{1}{"Path"}) {
16131 exitStatus("Error", "-d1 option is not specified");
16132 }
16133 my @DParts1 = split(/\s*,\s*/, $Descriptor{1}{"Path"});
16134 foreach my $Part (@DParts1)
16135 {
16136 if(not -e $Part) {
16137 exitStatus("Access_Error", "can't access \'$Part\'");
16138 }
16139 }
16140 if(not $Descriptor{2}{"Path"}) {
16141 exitStatus("Error", "-d2 option is not specified");
16142 }
16143 my @DParts2 = split(/\s*,\s*/, $Descriptor{2}{"Path"});
16144 foreach my $Part (@DParts2)
16145 {
16146 if(not -e $Part) {
16147 exitStatus("Access_Error", "can't access \'$Part\'");
16148 }
16149 }
16150 detect_default_paths("bin"); # to extract dumps
16151 if($#DParts1==0 and $#DParts2==0
16152 and isDump($Descriptor{1}{"Path"})
16153 and isDump($Descriptor{2}{"Path"}))
16154 { # optimization: equal ABI dumps
16155 quickEmptyReports();
16156 }
16157 checkVersionNum(1, $Descriptor{1}{"Path"});
16158 checkVersionNum(2, $Descriptor{2}{"Path"});
16159 printMsg("INFO", "preparation, please wait ...");
16160 foreach my $Part (@DParts1)
16161 {
16162 if(isDump($Part)) {
16163 read_ABI_Dump(1, $Part);
16164 }
16165 else {
16166 readDescriptor(1, createDescriptor(1, $Part));
16167 }
16168 }
16169 foreach my $Part (@DParts2)
16170 {
16171 if(isDump($Part)) {
16172 read_ABI_Dump(2, $Part);
16173 }
16174 else {
16175 readDescriptor(2, createDescriptor(2, $Part));
16176 }
16177 }
16178 initLogging(1);
16179 initLogging(2);
16180 # check consistency
16181 if(not $Descriptor{1}{"Headers"}
16182 and not $Descriptor{1}{"Libs"}) {
16183 exitStatus("Error", "descriptor d1 does not contain both header files and libraries info");
16184 }
16185 if(not $Descriptor{2}{"Headers"}
16186 and not $Descriptor{2}{"Libs"}) {
16187 exitStatus("Error", "descriptor d2 does not contain both header files and libraries info");
16188 }
16189 if($Descriptor{1}{"Headers"} and not $Descriptor{1}{"Libs"}
16190 and not $Descriptor{2}{"Headers"} and $Descriptor{2}{"Libs"}) {
16191 exitStatus("Error", "can't compare headers with $SLIB_TYPE libraries");
16192 }
16193 elsif(not $Descriptor{1}{"Headers"} and $Descriptor{1}{"Libs"}
16194 and $Descriptor{2}{"Headers"} and not $Descriptor{2}{"Libs"}) {
16195 exitStatus("Error", "can't compare $SLIB_TYPE libraries with headers");
16196 }
16197 if(not $Descriptor{1}{"Headers"}) {
16198 if($CheckHeadersOnly_Opt) {
16199 exitStatus("Error", "can't find header files info in descriptor d1");
16200 }
16201 }
16202 if(not $Descriptor{2}{"Headers"}) {
16203 if($CheckHeadersOnly_Opt) {
16204 exitStatus("Error", "can't find header files info in descriptor d2");
16205 }
16206 }
16207 if(not $Descriptor{1}{"Headers"}
16208 or not $Descriptor{2}{"Headers"}) {
16209 if(not $CheckObjectsOnly_Opt) {
16210 printMsg("WARNING", "comparing $SLIB_TYPE libraries only");
16211 $CheckObjectsOnly = 1;
16212 }
16213 }
16214 if(not $Descriptor{1}{"Libs"}) {
16215 if($CheckObjectsOnly_Opt) {
16216 exitStatus("Error", "can't find $SLIB_TYPE libraries info in descriptor d1");
16217 }
16218 }
16219 if(not $Descriptor{2}{"Libs"}) {
16220 if($CheckObjectsOnly_Opt) {
16221 exitStatus("Error", "can't find $SLIB_TYPE libraries info in descriptor d2");
16222 }
16223 }
16224 if(not $Descriptor{1}{"Libs"}
16225 or not $Descriptor{2}{"Libs"})
16226 { # comparing standalone header files
16227 # comparing ABI dumps created with --headers-only
16228 if(not $CheckHeadersOnly_Opt)
16229 {
16230 printMsg("WARNING", "checking headers only");
16231 $CheckHeadersOnly = 1;
16232 }
16233 }
16234 if($UseDumps)
16235 { # --use-dumps
16236 # parallel processing
16237 my $pid = fork();
16238 if($pid)
16239 { # dump on two CPU cores
16240 my @PARAMS = ("-dump", $Descriptor{1}{"Path"}, "-l", $TargetLibraryName);
16241 if($RelativeDirectory{1}) {
16242 @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{1});
16243 }
16244 if($OutputLogPath{1}) {
16245 @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{1});
16246 }
16247 if($CrossGcc) {
16248 @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
16249 }
16250 if($Debug) {
16251 @PARAMS = (@PARAMS, "-debug");
16252 }
16253 if($Quiet) {
16254 @PARAMS = (@PARAMS, "-quiet");
16255 }
16256 if($ExtendedCheck) {
16257 @PARAMS = (@PARAMS, "-extended");
16258 }
16259 if($UserLang) {
16260 @PARAMS = (@PARAMS, "-lang", $UserLang);
16261 }
16262 if($TargetVersion{1}) {
16263 @PARAMS = (@PARAMS, "-vnum", $TargetVersion{1});
16264 }
16265 if($LogMode eq "n") {
16266 @PARAMS = (@PARAMS, "-logging-mode", "n");
16267 }
16268 elsif($Quiet) {
16269 @PARAMS = (@PARAMS, "-logging-mode", "a");
16270 }
16271 system("perl", $0, @PARAMS);
16272 if($?) {
16273 exit(1);
16274 }
16275 }
16276 else
16277 { # child
16278 my @PARAMS = ("-dump", $Descriptor{2}{"Path"}, "-l", $TargetLibraryName);
16279 if($RelativeDirectory{2}) {
16280 @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{2});
16281 }
16282 if($OutputLogPath{2}) {
16283 @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{2});
16284 }
16285 if($CrossGcc) {
16286 @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
16287 }
16288 if($Debug) {
16289 @PARAMS = (@PARAMS, "-debug");
16290 }
16291 if($Quiet) {
16292 @PARAMS = (@PARAMS, "-quiet");
16293 }
16294 if($ExtendedCheck) {
16295 @PARAMS = (@PARAMS, "-extended");
16296 }
16297 if($UserLang) {
16298 @PARAMS = (@PARAMS, "-lang", $UserLang);
16299 }
16300 if($TargetVersion{2}) {
16301 @PARAMS = (@PARAMS, "-vnum", $TargetVersion{2});
16302 }
16303 if($LogMode eq "n") {
16304 @PARAMS = (@PARAMS, "-logging-mode", "n");
16305 }
16306 elsif($Quiet) {
16307 @PARAMS = (@PARAMS, "-logging-mode", "a");
16308 }
16309 system("perl", $0, @PARAMS);
16310 if($?) {
16311 exit(1);
16312 }
16313 else {
16314 exit(0);
16315 }
16316 }
16317 waitpid($pid, 0);
16318 my @CMP_PARAMS = ("-l", $TargetLibraryName);
16319 @CMP_PARAMS = (@CMP_PARAMS, "-d1", "abi_dumps/$TargetLibraryName/".$TargetLibraryName."_".$Descriptor{1}{"Version"}.".abi.$AR_EXT");
16320 @CMP_PARAMS = (@CMP_PARAMS, "-d2", "abi_dumps/$TargetLibraryName/".$TargetLibraryName."_".$Descriptor{2}{"Version"}.".abi.$AR_EXT");
16321 if($TargetLibraryFName ne $TargetLibraryName) {
16322 @CMP_PARAMS = (@CMP_PARAMS, "-l-full", $TargetLibraryFName);
16323 }
16324 if($ShowRetVal) {
16325 @CMP_PARAMS = (@CMP_PARAMS, "-show-retval");
16326 }
16327 if($CrossGcc) {
16328 @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $CrossGcc);
16329 }
16330 if($Quiet) {
16331 @CMP_PARAMS = (@CMP_PARAMS, "-quiet");
16332 }
16333 if($LogMode eq "n") {
16334 @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "n");
16335 }
16336 elsif($Quiet) {
16337 @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a");
16338 }
16339 if($ReportFormat) {
16340 @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $ReportFormat);
16341 }
16342 system("perl", $0, @CMP_PARAMS);
16343 exit($?>>8);
16344 }
16345 if(not $Descriptor{1}{"Dump"}
16346 or not $Descriptor{2}{"Dump"})
16347 { # need GCC toolchain to analyze
16348 # header files and libraries
16349 detect_default_paths("inc|lib|gcc");
16350 }
16351 if(not $Descriptor{1}{"Dump"})
16352 {
16353 if(not $CheckHeadersOnly) {
16354 readLibs(1);
16355 }
16356 if($CheckHeadersOnly) {
16357 setLanguage(1, "C++");
16358 }
16359 if(not $CheckObjectsOnly) {
16360 searchForHeaders(1);
16361 }
16362 $WORD_SIZE{1} = detectWordSize();
16363 }
16364 if(not $Descriptor{2}{"Dump"})
16365 {
16366 if(not $CheckHeadersOnly) {
16367 readLibs(2);
16368 }
16369 if($CheckHeadersOnly) {
16370 setLanguage(2, "C++");
16371 }
16372 if(not $CheckObjectsOnly) {
16373 searchForHeaders(2);
16374 }
16375 $WORD_SIZE{2} = detectWordSize();
16376 }
16377 if($WORD_SIZE{1} ne $WORD_SIZE{2})
16378 { # support for old ABI dumps
16379 # try to synch different WORD sizes
16380 if($UsedDump{1}{"V"}
16381 and cmpVersions($UsedDump{1}{"V"}, "2.1")<0)
16382 {
16383 $WORD_SIZE{1} = $WORD_SIZE{2};
16384 printMsg("WARNING", "set WORD size to ".$WORD_SIZE{2}." bytes");
16385 }
16386 elsif($UsedDump{2}{"V"}
16387 and cmpVersions($UsedDump{2}{"V"}, "2.1")<0)
16388 {
16389 $WORD_SIZE{2} = $WORD_SIZE{1};
16390 printMsg("WARNING", "set WORD size to ".$WORD_SIZE{1}." bytes");
16391 }
16392 }
16393 elsif(not $WORD_SIZE{1}
16394 and not $WORD_SIZE{2})
16395 { # support for old ABI dumps
16396 $WORD_SIZE{1} = 4;
16397 $WORD_SIZE{2} = 4;
16398 }
16399 if($Descriptor{1}{"Dump"})
16400 { # support for old ABI dumps
16401 prepareTypes(1);
16402 }
16403 if($Descriptor{2}{"Dump"})
16404 { # support for old ABI dumps
16405 prepareTypes(2);
16406 }
16407 if($AppPath and not keys(%{$Symbol_Library{1}})) {
16408 printMsg("WARNING", "the application ".get_filename($AppPath)." has no symbols imported from the $SLIB_TYPE libraries");
16409 }
16410 # started to process input data
16411 readRules("Binary");
16412 readRules("Source");
16413 if(not $CheckHeadersOnly)
16414 {# added/removed in libs
16415 detectAdded();
16416 detectRemoved();
16417 }
16418 if(not $CheckObjectsOnly)
16419 {
16420 if($Descriptor{1}{"Headers"}
16421 and not $Descriptor{1}{"Dump"}) {
16422 readHeaders(1);
16423 }
16424 if($Descriptor{2}{"Headers"}
16425 and not $Descriptor{2}{"Dump"}) {
16426 readHeaders(2);
16427 }
16428 printMsg("INFO", "comparing headers ...");
16429 prepareInterfaces(1);
16430 prepareInterfaces(2);
16431 %SymbolInfo=();
16432 if($CheckHeadersOnly)
16433 { # added/removed in headers
16434 detectAdded_H();
16435 detectRemoved_H();
16436 }
16437 mergeSignatures();
16438 if(keys(%CheckedSymbols)) {
16439 mergeConstants();
16440 }
16441 }
16442 if($CheckHeadersOnly)
16443 { # added/removed in headers
16444 mergeHeaders();
16445 }
16446 else
16447 {
16448 printMsg("INFO", "comparing libraries ...");
16449 mergeLibs();
16450 if($CheckImpl) {
16451 mergeImpl();
16452 }
16453 }
16454}
16455
16456sub optimize_set(@)
16457{
16458 my %Included = ();
16459 foreach my $Path (@_)
16460 {
16461 detect_header_includes($Path, 1);
16462 foreach my $Include (keys(%{$Header_Includes{1}{$Path}})) {
16463 $Included{get_filename($Include)}{$Include}=1;
16464 }
16465 }
16466 my @Res = ();
16467 foreach my $Path (@_)
16468 {
16469 my $Add = 1;
16470 foreach my $Inc (keys(%{$Included{get_filename($Path)}}))
16471 {
16472 if($Path=~/\/\Q$Inc\E\Z/)
16473 {
16474 $Add = 0;
16475 last;
16476 }
16477 }
16478 if($Add) {
16479 push(@Res, $Path);
16480 }
16481 }
16482 return @Res;
16483}
16484
16485sub writeOpts()
16486{
16487 my %Opts = (
16488 "OStarget"=>$OStarget,
16489 "Debug"=>$Debug,
16490 "Quiet"=>$Quiet,
16491 "LogMode"=>$LogMode,
16492 "CheckHeadersOnly"=>$CheckHeadersOnly,
16493
16494 "SystemRoot"=>$SystemRoot,
16495 "MODULES_DIR"=>$MODULES_DIR,
16496 "GCC_PATH"=>$GCC_PATH,
16497 "TargetSysInfo"=>$TargetSysInfo,
16498 "CrossPrefix"=>$CrossPrefix,
16499 "TargetLibraryName"=>$TargetLibraryName,
16500 "CrossGcc"=>$CrossGcc,
16501 "UseStaticLibs"=>$UseStaticLibs,
16502 "NoStdInc"=>$NoStdInc
16503 );
16504 return \%Opts;
16505}
16506
16507sub get_CoreError($)
16508{
16509 my %CODE_ERROR = reverse(%ERROR_CODE);
16510 return $CODE_ERROR{$_[0]};
16511}
16512
16513sub scenario()
16514{
16515 if($StdOut)
16516 { # enable quiet mode
16517 $Quiet = 1;
16518 }
16519 if($UserLang)
16520 { # --lang=C++
16521 $UserLang = uc($UserLang);
16522 $COMMON_LANGUAGE{1}=$UserLang;
16523 $COMMON_LANGUAGE{2}=$UserLang;
16524 }
16525 if($LoggingPath)
16526 {
16527 $OutputLogPath{1} = $LoggingPath;
16528 $OutputLogPath{2} = $LoggingPath;
16529 if($Quiet) {
16530 $COMMON_LOG_PATH = $LoggingPath;
16531 }
16532 }
16533 if($ReportFormat)
16534 { # validate
16535 if($ReportFormat!~/\A(xml|html)\Z/) {
16536 exitStatus("Error", "unknown format \'$ReportFormat\'");
16537 }
16538 }
16539 else
16540 { # default: HTML
16541 $ReportFormat = "html";
16542 }
16543 if($Quiet and $LogMode!~/a|n/)
16544 { # --quiet log
16545 if(-f $COMMON_LOG_PATH) {
16546 unlink($COMMON_LOG_PATH);
16547 }
16548 }
16549 if($TestTool and $UseDumps)
16550 { # --test && --use-dumps == --test-dump
16551 $TestDump = 1;
16552 }
16553 if($Help) {
16554 HELP_MESSAGE();
16555 exit(0);
16556 }
16557 if($InfoMsg) {
16558 INFO_MESSAGE();
16559 exit(0);
16560 }
16561 if($ShowVersion) {
16562 printMsg("INFO", "ABI Compliance Checker (ACC) $TOOL_VERSION\nCopyright (C) 2012 ROSA Laboratory\nLicense: LGPL or GPL <http://www.gnu.org/licenses/>\nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko.");
16563 exit(0);
16564 }
16565 if($DumpVersion) {
16566 printMsg("INFO", $TOOL_VERSION);
16567 exit(0);
16568 }
16569 if($ExtendedCheck) {
16570 $CheckHeadersOnly = 1;
16571 }
16572 if($SystemRoot_Opt)
16573 { # user defined root
16574 if(not -e $SystemRoot_Opt) {
16575 exitStatus("Access_Error", "can't access \'$SystemRoot\'");
16576 }
16577 $SystemRoot = $SystemRoot_Opt;
16578 $SystemRoot=~s/[\/]+\Z//g;
16579 if($SystemRoot) {
16580 $SystemRoot = get_abs_path($SystemRoot);
16581 }
16582 }
16583 $Data::Dumper::Sortkeys = 1;
16584 # FIXME: can't pass \&dump_sorting - cause a segfault sometimes
16585 # $Data::Dumper::Sortkeys = \&dump_sorting;
16586 if($TargetLibsPath)
16587 {
16588 if(not -f $TargetLibsPath) {
16589 exitStatus("Access_Error", "can't access file \'$TargetLibsPath\'");
16590 }
16591 foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath))) {
16592 $TargetLibs{$Lib} = 1;
16593 }
16594 }
16595 if($TargetHeadersPath)
16596 { # --headers-list
16597 if(not -f $TargetHeadersPath) {
16598 exitStatus("Access_Error", "can't access file \'$TargetHeadersPath\'");
16599 }
16600 foreach my $Header (split(/\s*\n\s*/, readFile($TargetHeadersPath)))
16601 {
16602 $TargetHeaders{1}{$Header} = 1;
16603 $TargetHeaders{2}{$Header} = 1;
16604 }
16605 }
16606 if($TargetHeader)
16607 { # --header
16608 $TargetHeaders{1}{$TargetHeader} = 1;
16609 $TargetHeaders{2}{$TargetHeader} = 1;
16610 }
16611 if($TestTool
16612 or $TestDump)
16613 { # --test, --test-dump
16614 detect_default_paths("bin|gcc"); # to compile libs
16615 loadModule("RegTests");
16616 testTool($TestDump, $Debug, $Quiet, $ExtendedCheck, $LogMode, $ReportFormat, $LIB_EXT, $GCC_PATH);
16617 exit(0);
16618 }
16619 if($DumpSystem)
16620 { # --dump-system
16621 loadModule("SysCheck");
16622 if($DumpSystem=~/\.xml\Z/)
16623 { # system XML descriptor
16624 if(not -f $DumpSystem) {
16625 exitStatus("Access_Error", "can't access file \'$DumpSystem\'");
16626 }
16627 my $Ret = readSystemDescriptor(readFile($DumpSystem));
16628 foreach (@{$Ret->{"Tools"}}) {
16629 $SystemPaths{"bin"}{$_} = 1;
16630 $TargetTools{$_}=1;
16631 }
16632 if($Ret->{"CrossPrefix"}) {
16633 $CrossPrefix = $Ret->{"CrossPrefix"};
16634 }
16635 }
16636 elsif($SystemRoot_Opt)
16637 { # -sysroot "/" option
16638 # default target: /usr/lib, /usr/include
16639 # search libs: /usr/lib and /lib
16640 if(not -e $SystemRoot."/usr/lib") {
16641 exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/lib'");
16642 }
16643 if(not -e $SystemRoot."/lib") {
16644 exitStatus("Access_Error", "can't access '".$SystemRoot."/lib'");
16645 }
16646 if(not -e $SystemRoot."/usr/include") {
16647 exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/include'");
16648 }
16649 readSystemDescriptor("
16650 <name>
16651 $DumpSystem
16652 </name>
16653 <headers>
16654 $SystemRoot/usr/include
16655 </headers>
16656 <libs>
16657 $SystemRoot/usr/lib
16658 </libs>
16659 <search_libs>
16660 $SystemRoot/lib
16661 </search_libs>");
16662 }
16663 else {
16664 exitStatus("Error", "-sysroot <dirpath> option should be specified, usually it's \"/\"");
16665 }
16666 detect_default_paths("bin|gcc"); # to check symbols
16667 if($OStarget eq "windows")
16668 { # to run dumpbin.exe
16669 # and undname.exe
16670 check_win32_env();
16671 }
16672 dumpSystem(writeOpts());
16673 exit(0);
16674 }
16675 if($CmpSystems)
16676 { # --cmp-systems
16677 detect_default_paths("bin"); # to extract dumps
16678 loadModule("SysCheck");
16679 cmpSystems($Descriptor{1}{"Path"}, $Descriptor{2}{"Path"}, writeOpts());
16680 exit(0);
16681 }
16682 if($GenerateTemplate) {
16683 generateTemplate();
16684 exit(0);
16685 }
16686 if(not $TargetLibraryName) {
16687 exitStatus("Error", "library name is not selected (option -l <name>)");
16688 }
16689 else
16690 { # validate library name
16691 if($TargetLibraryName=~/[\*\/\\]/) {
16692 exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name");
16693 }
16694 }
16695 if(not $TargetLibraryFName) {
16696 $TargetLibraryFName = $TargetLibraryName;
16697 }
16698 if($CheckHeadersOnly_Opt and $CheckObjectsOnly_Opt) {
16699 exitStatus("Error", "you can't specify both -headers-only and -objects-only options at the same time");
16700 }
16701 if($SymbolsListPath)
16702 {
16703 if(not -f $SymbolsListPath) {
16704 exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'");
16705 }
16706 foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath))) {
16707 $SymbolsList{$Interface} = 1;
16708 }
16709 }
16710 if($SkipHeadersPath)
16711 {
16712 if(not -f $SkipHeadersPath) {
16713 exitStatus("Access_Error", "can't access file \'$SkipHeadersPath\'");
16714 }
16715 foreach my $Path (split(/\s*\n\s*/, readFile($SkipHeadersPath)))
16716 {
16717 my ($CPath, $Type) = classifyPath($Path);
16718 $SkipHeaders{1}{$Type}{$CPath} = 1;
16719 $SkipHeadersList{1}{$Path} = 1;
16720 # register for both versions
16721 $SkipHeaders{2}{$Type}{$CPath} = 1;
16722 $SkipHeadersList{2}{$Path} = 1;
16723 }
16724 }
16725 if($ParamNamesPath)
16726 {
16727 if(not -f $ParamNamesPath) {
16728 exitStatus("Access_Error", "can't access file \'$ParamNamesPath\'");
16729 }
16730 foreach my $Line (split(/\n/, readFile($ParamNamesPath)))
16731 {
16732 if($Line=~s/\A(\w+)\;//)
16733 {
16734 my $Interface = $1;
16735 if($Line=~/;(\d+);/) {
16736 while($Line=~s/(\d+);(\w+)//) {
16737 $AddIntParams{$Interface}{$1}=$2;
16738 }
16739 }
16740 else {
16741 my $Num = 0;
16742 foreach my $Name (split(/;/, $Line)) {
16743 $AddIntParams{$Interface}{$Num++}=$Name;
16744 }
16745 }
16746 }
16747 }
16748 }
16749 if($AppPath)
16750 {
16751 if(not -f $AppPath) {
16752 exitStatus("Access_Error", "can't access file \'$AppPath\'");
16753 }
16754 foreach my $Interface (getSymbols_App($AppPath)) {
16755 $SymbolsList_App{$Interface} = 1;
16756 }
16757 }
16758 if($DumpAPI)
16759 { # --dump-abi
16760 # make an API dump
16761 create_ABI_Dump();
16762 exit($COMPILE_ERRORS);
16763 }
16764 # default: compare APIs
16765 # -d1 <path>
16766 # -d2 <path>
16767 compareAPIs();
16768 exitReport();
16769}
16770
16771scenario();