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