xref: /petsc/lib/petsc/bin/maint/abi-compliance-checker/modules/Internals/TUDump.pm (revision e8b6250908b962c387f7ab2e7b38caaa661b5fa1)
1###########################################################################
2# A module to create AST dump
3#
4# Copyright (C) 2015-2018 Andrey Ponomarenko's ABI Laboratory
5#
6# Written by Andrey Ponomarenko
7#
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Lesser General Public
10# License as published by the Free Software Foundation; either
11# version 2.1 of the License, or (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16# Lesser General Public License for more details.
17#
18# You should have received a copy of the GNU Lesser General Public
19# License along with this library; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21# MA  02110-1301 USA
22###########################################################################
23use strict;
24
25my %C_Structure = map {$_=>1} (
26# FIXME: Can't separate union and struct data types before dumping,
27# so it sometimes cause compilation errors for unknown reason
28# when trying to declare TYPE* tmp_add_class_N
29# This is a list of such structures + list of other C structures
30    "sigval",
31    "sigevent",
32    "sigaction",
33    "sigvec",
34    "sigstack",
35    "timeval",
36    "timezone",
37    "rusage",
38    "rlimit",
39    "wait",
40    "flock",
41    "stat",
42    "_stat",
43    "stat32",
44    "_stat32",
45    "stat64",
46    "_stat64",
47    "_stati64",
48    "if_nameindex",
49    "usb_device",
50    "sigaltstack",
51    "sysinfo",
52    "timeLocale",
53    "tcp_debug",
54    "rpc_createerr",
55    "dirent",
56    "dirent64",
57    "pthread_attr_t",
58    "_fpreg",
59    "_fpstate",
60    "_fpx_sw_bytes",
61    "_fpxreg",
62    "_libc_fpstate",
63    "_libc_fpxreg",
64    "_libc_xmmreg",
65    "_xmmreg",
66    "_xsave_hdr",
67    "_xstate",
68    "_ymmh_state",
69    "_prop_t",
70 # Other
71    "timespec",
72    "random_data",
73    "drand48_data",
74    "_IO_marker",
75    "_IO_FILE",
76    "lconv",
77    "sched_param",
78    "tm",
79    "itimerspec",
80    "_pthread_cleanup_buffer",
81    "fd_set",
82    "siginfo",
83    "mallinfo",
84    "timex",
85    "sigcontext",
86    "ucontext",
87 # Mac
88    "_timex",
89    "_class_t",
90    "_category_t",
91    "_class_ro_t",
92    "_protocol_t",
93    "_message_ref_t",
94    "_super_message_ref_t",
95    "_ivar_t",
96    "_ivar_list_t"
97);
98
99my %CppKeywords_C = map {$_=>1} (
100    # C++ 2003 keywords
101    "public",
102    "protected",
103    "private",
104    "default",
105    "template",
106    "new",
107    #"asm",
108    "dynamic_cast",
109    "auto",
110    "try",
111    "namespace",
112    "typename",
113    "using",
114    "reinterpret_cast",
115    "friend",
116    "class",
117    "virtual",
118    "const_cast",
119    "mutable",
120    "static_cast",
121    "export",
122    # C++0x keywords
123    "noexcept",
124    "nullptr",
125    "constexpr",
126    "static_assert",
127    "explicit",
128    # cannot be used as a macro name
129    # as it is an operator in C++
130    "and",
131    #"and_eq",
132    "not",
133    #"not_eq",
134    "or"
135    #"or_eq",
136    #"bitand",
137    #"bitor",
138    #"xor",
139    #"xor_eq",
140    #"compl"
141);
142
143my %CppKeywords_F = map {$_=>1} (
144    "delete",
145    "catch",
146    "alignof",
147    "thread_local",
148    "decltype",
149    "typeid"
150);
151
152my %CppKeywords_O = map {$_=>1} (
153    "bool",
154    "register",
155    "inline",
156    "operator"
157);
158
159my %CppKeywords_A = map {$_=>1} (
160    "this",
161    "throw",
162    "template"
163);
164
165foreach (keys(%CppKeywords_C),
166keys(%CppKeywords_F),
167keys(%CppKeywords_O)) {
168    $CppKeywords_A{$_}=1;
169}
170
171my %IntrinsicKeywords = map {$_=>1} (
172    "true",
173    "false",
174    "_Bool",
175    "_Complex",
176    "const",
177    "int",
178    "long",
179    "void",
180    "short",
181    "float",
182    "volatile",
183    "restrict",
184    "unsigned",
185    "signed",
186    "char",
187    "double",
188    "class",
189    "struct",
190    "union",
191    "enum"
192);
193
194my %PreprocessedHeaders;
195my %TUnit_NameSpaces;
196my %TUnit_Classes;
197my %TUnit_Funcs;
198my %TUnit_Vars;
199
200my %AutoPreambleMode = (
201  "1"=>0,
202  "2"=>0
203);
204
205my %MinGWMode = (
206  "1"=>0,
207  "2"=>0
208);
209
210my %Cpp0xMode = (
211  "1"=>0,
212  "2"=>0
213);
214
215sub createTUDump($)
216{
217    my $LVer = $_[0];
218
219    if(not $In::Opt{"GccPath"}) {
220        exitStatus("Error", "internal error - GCC path is not set");
221    }
222
223    searchForHeaders($LVer);
224
225    my @Headers = keys(%{$In::Desc{$LVer}{"RegHeader"}});
226    @Headers = sort {$In::Desc{$LVer}{"RegHeader"}{$a}{"Pos"}<=>$In::Desc{$LVer}{"RegHeader"}{$b}{"Pos"}} @Headers;
227
228    my @IncHeaders = (@{$In::Desc{$LVer}{"Include_Preamble"}}, @Headers);
229    my $IncludeString = getIncString(getIncPaths(\@IncHeaders, $LVer), "GCC");
230
231    my $TmpDir = $In::Opt{"Tmp"};
232    my $TmpHeaderPath = $TmpDir."/dump".$LVer.".h";
233    my $HeaderPath = $TmpHeaderPath;
234
235    # write tmp-header
236    open(TMP_HEADER, ">", $TmpHeaderPath) || die ("can't open file \'$TmpHeaderPath\': $!\n");
237    if(my $AddDefines = $In::Desc{$LVer}{"Defines"})
238    {
239        $AddDefines=~s/\n\s+/\n  /g;
240        print TMP_HEADER "\n  // add defines\n  ".$AddDefines."\n";
241    }
242    print TMP_HEADER "\n  // add includes\n";
243    foreach my $HPath (@{$In::Desc{$LVer}{"Include_Preamble"}}) {
244        print TMP_HEADER "  #include \"".pathFmt($HPath, "unix")."\"\n";
245    }
246    foreach my $HPath (@Headers)
247    {
248        if(not grep {$HPath eq $_} (@{$In::Desc{$LVer}{"Include_Preamble"}})) {
249            print TMP_HEADER "  #include \"".pathFmt($HPath, "unix")."\"\n";
250        }
251    }
252    close(TMP_HEADER);
253
254    if(my $EInfo = $In::Opt{"ExtraInfo"})
255    {
256        if($IncludeString) {
257            writeFile($EInfo."/include-string", $IncludeString);
258        }
259        dumpFilesInfo($LVer);
260    }
261
262    if(not defined $In::Desc{$LVer}{"TargetHeader"}) {
263        addTargetHeaders($LVer);
264    }
265
266    # preprocessing stage
267    my $Pre = callPreprocessor($TmpHeaderPath, $IncludeString, $LVer);
268    checkPreprocessedUnit($Pre, $LVer);
269
270    if(my $EInfo = $In::Opt{"ExtraInfo"})
271    { # extra information for other tools
272        writeFile($EInfo."/header-paths", join("\n", sort keys(%{$PreprocessedHeaders{$LVer}})));
273    }
274
275    # clean memory
276    delete($PreprocessedHeaders{$LVer});
277
278    if($In::ABI{$LVer}{"Language"} eq "C++") {
279        checkCTags($Pre, $LVer);
280    }
281
282    if(my $PrePath = preChange($TmpHeaderPath, $IncludeString, $LVer))
283    { # try to correct the preprocessor output
284        $HeaderPath = $PrePath;
285    }
286
287    my $GCC_8 = checkGcc("8"); # support for GCC 8 and new options
288
289    if($In::ABI{$LVer}{"Language"} eq "C++")
290    { # add classes and namespaces to the dump
291        my $CHdump = "-fdump-class-hierarchy";
292        if($GCC_8)
293        { # -fdump-lang-class instead of -fdump-class-hierarchy
294            $CHdump = "-fdump-lang-class";
295        }
296        $CHdump .= " -c";
297
298        if($In::Desc{$LVer}{"CppMode"}==1
299        or $MinGWMode{$LVer}==1) {
300            $CHdump .= " -fpreprocessed";
301        }
302        my $ClassHierarchyCmd = getCompileCmd($HeaderPath, $CHdump, $IncludeString, $LVer);
303        chdir($TmpDir);
304        system($ClassHierarchyCmd." >null 2>&1");
305        chdir($In::Opt{"OrigDir"});
306        if(my $ClassDump = (cmdFind($TmpDir,"f","*.class",1))[0])
307        {
308            my $Content = readFile($ClassDump);
309            foreach my $ClassInfo (split(/\n\n/, $Content))
310            {
311                if($ClassInfo=~/\AClass\s+(.+)\s*/i)
312                {
313                    my $CName = $1;
314                    if($CName=~/\A(__|_objc_|_opaque_)/) {
315                        next;
316                    }
317                    $TUnit_NameSpaces{$LVer}{$CName} = -1;
318                    if($CName=~/\A[\w:]+\Z/)
319                    { # classes
320                        $TUnit_Classes{$LVer}{$CName} = 1;
321                    }
322                    if($CName=~/(\w[\w:]*)::/)
323                    { # namespaces
324                        my $NS = $1;
325                        if(not defined $TUnit_NameSpaces{$LVer}{$NS}) {
326                            $TUnit_NameSpaces{$LVer}{$NS} = 1;
327                        }
328                    }
329                }
330                elsif($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i)
331                { # read v-tables (advanced approach)
332                    my ($CName, $VTable) = ($1, $2);
333                    $In::ABI{$LVer}{"ClassVTable_Content"}{$CName} = $VTable;
334                }
335            }
336            foreach my $NS (keys(%{$In::Desc{$LVer}{"AddNameSpaces"}}))
337            { # add user-defined namespaces
338                $TUnit_NameSpaces{$LVer}{$NS} = 1;
339            }
340            if($In::Opt{"Debug"})
341            { # debug mode
342                copy($ClassDump, getDebugDir($LVer)."/class-hierarchy-dump.txt");
343            }
344            unlink($ClassDump);
345        }
346
347        # add namespaces and classes
348        if(my $NSAdd = getNSAdditions($LVer, $TUnit_NameSpaces{$LVer}))
349        { # GCC on all supported platforms does not include namespaces to the dump by default
350            appendFile($HeaderPath, "\n  // add namespaces\n".$NSAdd);
351
352            if($HeaderPath ne $TmpHeaderPath) {
353                appendFile($TmpHeaderPath, "\n  // add namespaces\n".$NSAdd);
354            }
355        }
356        # some GCC versions don't include class methods to the TU dump by default
357        my ($AddClass, $ClassNum) = ("", 0);
358        my $GCC_44 = checkGcc("4.4"); # support for old GCC versions
359        foreach my $CName (sort keys(%{$TUnit_Classes{$LVer}}))
360        {
361            next if($C_Structure{$CName});
362            next if(not $In::Opt{"StdcxxTesting"} and $CName=~/\Astd::/);
363            next if($In::Desc{$LVer}{"SkipTypes"}{$CName});
364            if(not $In::Opt{"Force"} and $GCC_44
365            and $In::Opt{"OS"} eq "linux")
366            { # optimization for linux with GCC >= 4.4
367              # disable this code by -force option
368                if(index($CName, "::")!=-1)
369                { # should be added by name space
370                    next;
371                }
372            }
373            else
374            {
375                if($CName=~/\A(.+)::[^:]+\Z/
376                and $TUnit_Classes{$LVer}{$1})
377                { # classes inside other classes
378                    next;
379                }
380            }
381            if(defined $TUnit_Funcs{$LVer}{$CName})
382            { # the same name for a function and type
383                next;
384            }
385            if(defined $TUnit_Vars{$LVer}{$CName})
386            { # the same name for a variable and type
387                next;
388            }
389            $AddClass .= "  $CName* tmp_add_class_".($ClassNum++).";\n";
390        }
391        if($AddClass)
392        {
393            appendFile($HeaderPath, "\n  // add classes\n".$AddClass);
394
395            if($HeaderPath ne $TmpHeaderPath) {
396                appendFile($TmpHeaderPath, "\n  // add classes\n".$AddClass);
397            }
398        }
399    }
400    writeLog($LVer, "Temporary header file \'$TmpHeaderPath\' with the following content will be compiled to create GCC translation unit dump:\n".readFile($TmpHeaderPath)."\n");
401
402    # create TU dump
403    my $TUdump = "-fdump-translation-unit";
404    if ($GCC_8)
405    { # -fdump-lang-raw instead of -fdump-translation-unit
406        $TUdump = "-fdump-lang-raw";
407    }
408    $TUdump .= " -fkeep-inline-functions -c";
409    if($In::Opt{"UserLang"} eq "C") {
410        $TUdump .= " -U__cplusplus -D_Bool=\"bool\"";
411    }
412    if($In::Desc{$LVer}{"CppMode"}==1
413    or $MinGWMode{$LVer}==1) {
414        $TUdump .= " -fpreprocessed";
415    }
416    my $SyntaxTreeCmd = getCompileCmd($HeaderPath, $TUdump, $IncludeString, $LVer);
417    writeLog($LVer, "The GCC parameters:\n  $SyntaxTreeCmd\n\n");
418    chdir($TmpDir);
419    system($SyntaxTreeCmd." >\"$TmpDir/tu_errors\" 2>&1");
420    chdir($In::Opt{"OrigDir"});
421
422    my $Errors = "";
423    if($?)
424    { # failed to compile, but the TU dump still can be created
425        if($Errors = readFile($TmpDir."/tu_errors"))
426        { # try to recompile
427          # FIXME: handle errors and try to recompile
428            if($AutoPreambleMode{$LVer}!=-1
429            and my $AddHeaders = detectPreamble($Errors, $LVer))
430            { # add auto preamble headers and try again
431                $AutoPreambleMode{$LVer}=-1;
432                my @Headers = sort {$b cmp $a} keys(%{$AddHeaders}); # sys/types.h should be the first
433                foreach my $Num (0 .. $#Headers)
434                {
435                    my $Path = $Headers[$Num];
436                    if(not grep {$Path eq $_} (@{$In::Desc{$LVer}{"Include_Preamble"}}))
437                    {
438                        push_U($In::Desc{$LVer}{"Include_Preamble"}, $Path);
439                        printMsg("INFO", "Adding \'".$AddHeaders->{$Path}{"Header"}."\' preamble header for \'".$AddHeaders->{$Path}{"Type"}."\'");
440                    }
441                }
442                resetLogging($LVer);
443                $TmpDir = tempdir(CLEANUP=>1);
444                return createTUDump($LVer);
445            }
446            elsif($Cpp0xMode{$LVer}!=-1
447            and ($Errors=~/\Q-std=c++0x\E/
448            or $Errors=~/is not a class or namespace/))
449            { # c++0x: enum class
450                if(checkGcc("4.6"))
451                {
452                    $Cpp0xMode{$LVer}=-1;
453                    printMsg("INFO", "Enabling c++0x mode");
454                    resetLogging($LVer);
455                    $TmpDir = tempdir(CLEANUP=>1);
456                    $In::Desc{$LVer}{"CompilerOptions"} .= " -std=c++0x";
457                    return createTUDump($LVer);
458                }
459                else {
460                    printMsg("WARNING", "Probably c++0x element detected");
461                }
462
463            }
464            writeLog($LVer, $Errors);
465        }
466        else {
467            writeLog($LVer, "$!: $?\n");
468        }
469        printMsg("ERROR", "some errors occurred when compiling headers");
470        printErrorLog($LVer);
471        $In::Opt{"CompileError"} = 1;
472        writeLog($LVer, "\n"); # new line
473    }
474
475    unlink($TmpHeaderPath);
476    unlink($HeaderPath);
477
478    my $dumpExt;
479    if ($GCC_8) {
480        $dumpExt = "*.raw";
481    }
482    else {
483        $dumpExt = "*.tu";
484    }
485    if(my @TUs = cmdFind($TmpDir,"f",$dumpExt,1)) {
486        return $TUs[0];
487    }
488    else
489    {
490        my $Msg = "can't compile header(s)";
491        if($Errors=~/error trying to exec \W+cc1plus\W+/) {
492            $Msg .= "\nDid you install G++?";
493        }
494        exitStatus("Cannot_Compile", $Msg);
495    }
496}
497
498sub detectPreamble($$)
499{
500    my ($Content, $LVer) = @_;
501    my %HeaderElems = (
502        # Types
503        "stdio.h" => ["FILE", "va_list"],
504        "stddef.h" => ["NULL", "ptrdiff_t"],
505        "stdint.h" => ["uint8_t", "uint16_t", "uint32_t", "uint64_t",
506                       "int8_t", "int16_t", "int32_t", "int64_t"],
507        "time.h" => ["time_t"],
508        "sys/types.h" => ["ssize_t", "u_int32_t", "u_short", "u_char",
509                          "u_int", "off_t", "u_quad_t", "u_long", "mode_t"],
510        "unistd.h" => ["gid_t", "uid_t", "socklen_t"],
511        "stdbool.h" => ["_Bool"],
512        "rpc/xdr.h" => ["bool_t"],
513        "in_systm.h" => ["n_long", "n_short"],
514        # Fields
515        "arpa/inet.h" => ["fw_src", "ip_src"],
516        # Functions
517        "stdlib.h" => ["free", "malloc", "size_t"],
518        "string.h" => ["memmove", "strcmp"]
519    );
520    my %AutoPreamble = ();
521    foreach (keys(%HeaderElems))
522    {
523        foreach my $Elem (@{$HeaderElems{$_}}) {
524            $AutoPreamble{$Elem} = $_;
525        }
526    }
527    my %Types = ();
528    while($Content=~s/error\:\s*(field\s*|)\W+(.+?)\W+//)
529    { # error: 'FILE' has not been declared
530        $Types{$2} = 1;
531    }
532    if(keys(%Types))
533    {
534        my %AddHeaders = ();
535        foreach my $Type (keys(%Types))
536        {
537            if(my $Header = $AutoPreamble{$Type})
538            {
539                if(my $Path = identifyHeader($Header, $LVer))
540                {
541                    if(skipHeader($Path, $LVer)) {
542                        next;
543                    }
544                    $Path = pathFmt($Path);
545                    $AddHeaders{$Path}{"Type"} = $Type;
546                    $AddHeaders{$Path}{"Header"} = $Header;
547                }
548            }
549        }
550        if(keys(%AddHeaders)) {
551            return \%AddHeaders;
552        }
553    }
554    return undef;
555}
556
557sub checkCTags($$)
558{
559    my ($Path, $LVer) = @_;
560
561    my $CTags = undef;
562
563    if($In::Opt{"OS"} eq "bsd")
564    { # use ectags on BSD
565        $CTags = getCmdPath("ectags");
566        if(not $CTags) {
567            printMsg("WARNING", "can't find \'ectags\' program");
568        }
569    }
570    if(not $CTags) {
571        $CTags = getCmdPath("ctags");
572    }
573    if(not $CTags)
574    {
575        printMsg("WARNING", "can't find \'ctags\' program");
576        return;
577    }
578
579    my $TmpDir = $In::Opt{"Tmp"};
580
581    if($In::Opt{"OS"} ne "linux")
582    { # macos, freebsd, etc.
583        my $Info = `$CTags --version 2>\"$TmpDir/null\"`;
584        if($Info!~/universal|exuberant/i)
585        {
586            printMsg("WARNING", "incompatible version of \'ctags\' program");
587            return;
588        }
589    }
590
591    my $Out = $TmpDir."/ctags.txt";
592    system("$CTags --c-kinds=pxn -f \"$Out\" \"$Path\" 2>\"$TmpDir/null\"");
593    if($In::Opt{"Debug"}) {
594        copy($Out, getDebugDir($LVer)."/ctags.txt");
595    }
596    open(CTAGS, "<", $Out);
597    while(my $Line = <CTAGS>)
598    {
599        chomp($Line);
600        my ($Name, $Header, $Def, $Type, $Scpe) = split(/\t/, $Line);
601        if(defined $IntrinsicKeywords{$Name})
602        { # noise
603            next;
604        }
605        if($Type eq "n")
606        {
607            if(index($Scpe, "class:")==0) {
608                next;
609            }
610            if(index($Scpe, "struct:")==0) {
611                next;
612            }
613            if(index($Scpe, "namespace:")==0)
614            {
615                if($Scpe=~s/\Anamespace://) {
616                    $Name = $Scpe."::".$Name;
617                }
618            }
619            $TUnit_NameSpaces{$LVer}{$Name} = 1;
620        }
621        elsif($Type eq "p")
622        {
623            if(not $Scpe or index($Scpe, "namespace:")==0) {
624                $TUnit_Funcs{$LVer}{$Name} = 1;
625            }
626        }
627        elsif($Type eq "x")
628        {
629            if(not $Scpe or index($Scpe, "namespace:")==0) {
630                $TUnit_Vars{$LVer}{$Name} = 1;
631            }
632        }
633    }
634    close(CTAGS);
635}
636
637sub preChange($$$)
638{
639    my ($HeaderPath, $IncStr, $LVer) = @_;
640
641    my $TmpDir = $In::Opt{"Tmp"};
642    my $PreprocessCmd = getCompileCmd($HeaderPath, "-E", $IncStr, $LVer);
643    my $Content = undef;
644
645    if(not defined $In::Opt{"MinGWCompat"}
646    and $In::Opt{"Target"} eq "windows"
647    and $In::Opt{"GccTarget"}=~/mingw/i
648    and $MinGWMode{$LVer}!=-1)
649    { # modify headers to compile by MinGW
650        if(not $Content)
651        { # preprocessing
652            $Content = `$PreprocessCmd 2>\"$TmpDir/null\"`;
653        }
654        if($Content=~s/__asm\s*(\{[^{}]*?\}|[^{};]*)//g)
655        { # __asm { ... }
656            $MinGWMode{$LVer}=1;
657        }
658        if($Content=~s/\s+(\/ \/.*?)\n/\n/g)
659        { # comments after preprocessing
660            $MinGWMode{$LVer}=1;
661        }
662        if($Content=~s/(\W)(0x[a-f]+|\d+)(i|ui)(8|16|32|64)(\W)/$1$2$5/g)
663        { # 0xffui8
664            $MinGWMode{$LVer}=1;
665        }
666
667        if($MinGWMode{$LVer}) {
668            printMsg("INFO", "Using MinGW compatibility mode");
669        }
670    }
671
672    if(defined $In::Opt{"CxxIncompat"}
673    and $In::ABI{$LVer}{"Language"} eq "C"
674    and $In::Desc{$LVer}{"CppMode"}!=-1 and not $In::Opt{"CppHeaders"})
675    { # rename C++ keywords in C code
676        printMsg("INFO", "Checking the code for C++ keywords");
677        if(not $Content)
678        { # preprocessing
679            $Content = `$PreprocessCmd 2>\"$TmpDir/null\"`;
680        }
681
682        my $RegExp_C = join("|", keys(%CppKeywords_C));
683        my $RegExp_F = join("|", keys(%CppKeywords_F));
684        my $RegExp_O = join("|", keys(%CppKeywords_O));
685
686        my $Detected = undef;
687        my $Regex = undef;
688
689        $Regex = qr/(\A|\n[^\#\/\n][^\n]*?|\n)(\*\s*|\s+|\@|\,|\()($RegExp_C|$RegExp_F)(\s*([\,\)\;\.\[]|\-\>|\:\s*\d))/;
690        while($Content=~/$Regex/)
691        { # MATCH:
692          # int foo(int new, int class, int (*new)(int));
693          # int foo(char template[], char*);
694          # unsigned private: 8;
695          # DO NOT MATCH:
696          # #pragma GCC visibility push(default)
697            my $Sentence_O = "$1$2$3$4";
698
699            if($Sentence_O=~/\s+decltype\(/)
700            { # C++
701              # decltype(nullptr)
702                last;
703            }
704            else
705            {
706                $Content=~s/$Regex/$1$2c99_$3$4/g;
707                $In::Desc{$LVer}{"CppMode"} = 1;
708                if(not defined $Detected) {
709                    $Detected = $Sentence_O;
710                }
711            }
712        }
713        if($Content=~s/([^\w\s]|\w\s+)(?<!operator )(delete)(\s*\()/$1c99_$2$3/g)
714        { # MATCH:
715          # int delete(...);
716          # int explicit(...);
717          # DO NOT MATCH:
718          # void operator delete(...)
719            $In::Desc{$LVer}{"CppMode"} = 1;
720            $Detected = "$1$2$3" if(not defined $Detected);
721        }
722        if($Content=~s/(\s+)($RegExp_O)(\s*(\;|\:))/$1c99_$2$3/g)
723        { # MATCH:
724          # int bool;
725          # DO NOT MATCH:
726          # bool X;
727          # return *this;
728          # throw;
729            $In::Desc{$LVer}{"CppMode"} = 1;
730            $Detected = "$1$2$3" if(not defined $Detected);
731        }
732        if($Content=~s/(\s+)(operator)(\s*(\(\s*\)\s*[^\(\s]|\(\s*[^\)\s]))/$1c99_$2$3/g)
733        { # MATCH:
734          # int operator(...);
735          # DO NOT MATCH:
736          # int operator()(...);
737            $In::Desc{$LVer}{"CppMode"} = 1;
738            $Detected = "$1$2$3" if(not defined $Detected);
739        }
740        if($Content=~s/([^\w\(\,\s]\s*|\s+)(operator)(\s*(\,\s*[^\(\s]|\)))/$1c99_$2$3/g)
741        { # MATCH:
742          # int foo(int operator);
743          # int foo(int operator, int other);
744          # DO NOT MATCH:
745          # int operator,(...);
746            $In::Desc{$LVer}{"CppMode"} = 1;
747            $Detected = "$1$2$3" if(not defined $Detected);
748        }
749        if($Content=~s/(\*\s*|\w\s+)(bool)(\s*(\,|\)))/$1c99_$2$3/g)
750        { # MATCH:
751          # int foo(gboolean *bool);
752          # DO NOT MATCH:
753          # void setTabEnabled(int index, bool);
754            $In::Desc{$LVer}{"CppMode"} = 1;
755            $Detected = "$1$2$3" if(not defined $Detected);
756        }
757        if($Content=~s/(\w)(\s*[^\w\(\,\s]\s*|\s+)(this|throw)(\s*[\,\)])/$1$2c99_$3$4/g)
758        { # MATCH:
759          # int foo(int* this);
760          # int bar(int this);
761          # int baz(int throw);
762          # DO NOT MATCH:
763          # foo(X, this);
764            $In::Desc{$LVer}{"CppMode"} = 1;
765            $Detected = "$1$2$3$4" if(not defined $Detected);
766        }
767        if($Content=~s/(struct |extern )(template) /$1c99_$2 /g)
768        { # MATCH:
769          # struct template {...};
770          # extern template foo(...);
771            $In::Desc{$LVer}{"CppMode"} = 1;
772            $Detected = "$1$2" if(not defined $Detected);
773        }
774
775        if($In::Desc{$LVer}{"CppMode"} == 1)
776        {
777            if($In::Opt{"Debug"})
778            {
779                $Detected=~s/\A\s+//g;
780                printMsg("INFO", "Detected code: \"$Detected\"");
781            }
782        }
783
784        # remove typedef enum NAME NAME;
785        my @FwdTypedefs = $Content=~/typedef\s+enum\s+(\w+)\s+(\w+);/g;
786        my $N = 0;
787        while($N<=$#FwdTypedefs-1)
788        {
789            my $S = $FwdTypedefs[$N];
790            if($S eq $FwdTypedefs[$N+1])
791            {
792                $Content=~s/typedef\s+enum\s+\Q$S\E\s+\Q$S\E;//g;
793                $In::Desc{$LVer}{"CppMode"} = 1;
794
795                if($In::Opt{"Debug"}) {
796                    printMsg("INFO", "Detected code: \"typedef enum $S $S;\"");
797                }
798            }
799            $N+=2;
800        }
801
802        if($In::Desc{$LVer}{"CppMode"}==1) {
803            printMsg("INFO", "Using C++ compatibility mode");
804        }
805        else {
806            printMsg("INFO", "C++ keywords in the C code are not found");
807        }
808    }
809
810    if($In::Desc{$LVer}{"CppMode"}==1
811    or $MinGWMode{$LVer}==1)
812    {
813        my $IPath = $TmpDir."/dump$LVer.i";
814        writeFile($IPath, $Content);
815        return $IPath;
816    }
817
818    return undef;
819}
820
821sub getNSAdditions($$)
822{
823    my ($LVer, $NameSpaces) = @_;
824
825    my ($Additions, $AddNameSpaceId) = ("", 1);
826    foreach my $NS (sort {$a=~/_/ <=> $b=~/_/} sort {lc($a) cmp lc($b)} keys(%{$NameSpaces}))
827    {
828        next if($In::Desc{$LVer}{"SkipNameSpaces"}{$NS});
829        next if(not $NS or $NameSpaces->{$NS}==-1);
830        next if($NS=~/(\A|::)iterator(::|\Z)/i);
831        next if($NS=~/\A__/i);
832        next if(($NS=~/\Astd::/ or $NS=~/\A(std|tr1|rel_ops|fcntl)\Z/) and not $In::Opt{"StdcxxTesting"});
833
834        $In::ABI{$LVer}{"NameSpaces"}{$NS} = 1; # for future use in reports
835
836        my ($TypeDecl_Prefix, $TypeDecl_Suffix) = ();
837        my @NS_Parts = split(/::/, $NS);
838        next if($#NS_Parts==-1);
839        next if($NS_Parts[0]=~/\A(random|or)\Z/);
840        foreach my $NS_Part (@NS_Parts)
841        {
842            $TypeDecl_Prefix .= "namespace $NS_Part\{";
843            $TypeDecl_Suffix .= "}";
844        }
845        my $TypeDecl = $TypeDecl_Prefix."typedef int tmp_add_type_".$AddNameSpaceId.";".$TypeDecl_Suffix;
846        my $FuncDecl = "$NS\:\:tmp_add_type_$AddNameSpaceId tmp_add_func_$AddNameSpaceId(){return 0;};";
847        $Additions .= "  $TypeDecl\n  $FuncDecl\n";
848        $AddNameSpaceId += 1;
849    }
850    return $Additions;
851}
852
853sub includeOpt($$)
854{
855    my ($Path, $Style) = @_;
856    if($Style eq "GCC")
857    { # GCC options
858        if($In::Opt{"OS"} eq "windows")
859        { # to MinGW GCC
860            return "-I\"".pathFmt($Path, "unix")."\"";
861        }
862        elsif($In::Opt{"OS"} eq "macos"
863        and $Path=~/\.framework\Z/)
864        { # to Apple's GCC
865            return "-F".escapeArg(getDirname($Path));
866        }
867        else {
868            return "-I".escapeArg($Path);
869        }
870    }
871    elsif($Style eq "CL") {
872        return "/I \"".$Path."\"";
873    }
874    return "";
875}
876
877sub checkPreprocessedUnit($$)
878{
879    my ($Path, $LVer) = @_;
880
881    my $TmpDir = $In::Opt{"Tmp"};
882    my ($CurHeader, $CurHeaderName) = ("", "");
883    my $CurClass = ""; # extra info
884
885    my $CRef = $In::ABI{$LVer}{"Constants"};
886
887    if(not $CRef) {
888        $CRef = {};
889    }
890
891    open(PREPROC, $Path) || die ("can't open file \'$Path\': $!\n");
892    while(my $Line = <PREPROC>)
893    { # detecting public and private constants
894        if(substr($Line, 0, 1) eq "#")
895        {
896            chomp($Line);
897            if($Line=~/\A\#\s+\d+\s+\"(.+)\"/)
898            {
899                $CurHeader = pathFmt($1);
900                $CurHeaderName = getFilename($CurHeader);
901                $CurClass = "";
902
903                if(index($CurHeader, $TmpDir)==0) {
904                    next;
905                }
906
907                if(substr($CurHeaderName, 0, 1) eq "<")
908                { # <built-in>, <command-line>, etc.
909                    $CurHeaderName = "";
910                    $CurHeader = "";
911                }
912
913                if($In::Opt{"ExtraInfo"})
914                {
915                    if($CurHeaderName) {
916                        $PreprocessedHeaders{$LVer}{$CurHeader} = 1;
917                    }
918                }
919            }
920            if(not $In::Opt{"ExtraDump"})
921            {
922                if($CurHeaderName)
923                {
924                    if(not $In::Desc{$LVer}{"IncludeNeighbors"}{$CurHeaderName}
925                    and not $In::Desc{$LVer}{"RegHeader"}{$CurHeader})
926                    { # not a target
927                        next;
928                    }
929                    if(not isTargetHeader($CurHeaderName, 1)
930                    and not isTargetHeader($CurHeaderName, 2))
931                    { # user-defined header
932                        next;
933                    }
934                }
935            }
936
937            if($Line=~/\A\#\s*define\s+(\w+)\s+(.+)\s*\Z/)
938            {
939                my ($Name, $Value) = ($1, $2);
940                if(not $CRef->{$Name}{"Access"})
941                {
942                    $CRef->{$Name}{"Access"} = "public";
943                    $CRef->{$Name}{"Value"} = $Value;
944                    if($CurHeaderName) {
945                        $CRef->{$Name}{"Header"} = $CurHeaderName;
946                    }
947                }
948            }
949            elsif($Line=~/\A\#[ \t]*undef[ \t]+([_A-Z]+)[ \t]*/) {
950                $CRef->{$1}{"Access"} = "private";
951            }
952        }
953        else
954        {
955            if($In::Opt{"ExtraDump"})
956            {
957                if($Line=~/(\w+)\s*\(/)
958                { # functions
959                    $In::ABI{$LVer}{"SymbolHeader"}{$CurClass}{$1} = $CurHeader;
960                }
961                elsif($Line=~/(\A|\s)class\s+(\w+)/) {
962                    $CurClass = $2;
963                }
964            }
965        }
966    }
967    close(PREPROC);
968
969    foreach my $Constant (keys(%{$CRef}))
970    {
971        if($CRef->{$Constant}{"Access"} eq "private")
972        {
973            delete($CRef->{$Constant});
974            next;
975        }
976
977        if(not $In::Opt{"ExtraDump"} and ($Constant=~/_h\Z/i
978        or isBuiltIn($CRef->{$Constant}{"Header"})))
979        { # skip
980            delete($CRef->{$Constant});
981        }
982        else {
983            delete($CRef->{$Constant}{"Access"});
984        }
985    }
986
987    if($In::Opt{"Debug"}) {
988        copy($Path, getDebugDir($LVer)."/preprocessor.txt");
989    }
990}
991
992return 1;
993