+ return 0;
+ }
+
+ return 1;
+}
+
+sub showArch($)
+{
+ my $Arch = $_[0];
+ if($Arch eq "arm"
+ or $Arch eq "mips") {
+ return uc($Arch);
+ }
+ return $Arch;
+}
+
+sub getReportTitle($)
+{
+ my $Level = $_[0];
+
+ my $ArchInfo = " on ".showArch($In::ABI{1}{"Arch"})."";
+ if($In::ABI{1}{"Arch"} ne $In::ABI{2}{"Arch"}
+ or $Level eq "Source")
+ { # don't show architecture in the header
+ $ArchInfo = "";
+ }
+ my $Title = "";
+ if($Level eq "Source") {
+ $Title .= "Source compatibility";
+ }
+ elsif($Level eq "Binary") {
+ $Title .= "Binary compatibility";
+ }
+ else {
+ $Title .= "API compatibility";
+ }
+
+ my $V1 = $In::Desc{1}{"Version"};
+ my $V2 = $In::Desc{2}{"Version"};
+
+ if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"})
+ {
+ my $M1 = $In::ABI{1}{"LibraryName"};
+ my $M2 = $In::ABI{2}{"LibraryName"};
+
+ my $M1S = $M1;
+ my $M2S = $M2;
+
+ $M1S=~s/(\.so|\.ko)\..+/$1/ig;
+ $M2S=~s/(\.so|\.ko)\..+/$1/ig;
+
+ if($M1S eq $M2S
+ and $V1 ne "X" and $V2 ne "Y")
+ {
+ $Title .= " report for the $M1S ".$In::Opt{"TargetComponent"};
+ $Title .= " between ".$V1." and ".$V2." versions";
+ }
+ else
+ {
+ $Title .= " report between $M1 (".$V1.")";
+ $Title .= " and $M2 (".$V2.") objects";
+ }
+ }
+ else
+ {
+ $Title .= " report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"};
+ $Title .= " between ".$V1." and ".$V2." versions";
+ }
+
+ $Title .= $ArchInfo;
+
+ if($In::Opt{"AppPath"}) {
+ $Title .= " (relating to the portability of application ".getFilename($In::Opt{"AppPath"}).")";
+ }
+ $Title = "".$Title."
\n";
+ return $Title;
+}
+
+sub getCheckedHeaders($)
+{
+ my $LVer = $_[0];
+
+ my @Headers = ();
+
+ foreach my $Path (keys(%{$In::ABI{$LVer}{"Headers"}}))
+ {
+ my $Name = getFilename($Path);
+
+ if(not isTargetHeader($Name, $LVer)) {
+ next;
+ }
+
+ if(skipHeader($Name, $LVer)) {
+ next;
+ }
+
+ push(@Headers, $Path);
+ }
+
+ return @Headers;
+}
+
+sub getSourceInfo()
+{
+ my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", "");
+
+ if(my @Headers = getCheckedHeaders(1))
+ {
+ $CheckedHeaders = "";
+ if($In::Opt{"OldStyle"}) {
+ $CheckedHeaders .= "Header Files (".($#Headers+1).")
";
+ }
+ else {
+ $CheckedHeaders .= "Header Files ".($#Headers+1)."
";
+ }
+ $CheckedHeaders .= "
\n";
+ $CheckedHeaders .= "\n";
+ foreach my $Path (sort {lc($a) cmp lc($b)} @Headers) {
+ $CheckedHeaders .= getFilename($Path)."
\n";
+ }
+ $CheckedHeaders .= "
\n";
+ $CheckedHeaders .= "
$TOP_REF
\n";
+ }
+
+ if(my @Sources = keys(%{$In::ABI{1}{"Sources"}}))
+ {
+ $CheckedSources = "";
+ if($In::Opt{"OldStyle"}) {
+ $CheckedSources .= "Source Files (".($#Sources+1).")
";
+ }
+ else {
+ $CheckedSources .= "Source Files ".($#Sources+1)."
";
+ }
+ $CheckedSources .= "
\n";
+ $CheckedSources .= "\n";
+ foreach my $Path (sort {lc($a) cmp lc($b)} @Sources) {
+ $CheckedSources .= getFilename($Path)."
\n";
+ }
+ $CheckedSources .= "
\n";
+ $CheckedSources .= "
$TOP_REF
\n";
+ }
+
+ if(not $In::Opt{"CheckHeadersOnly"})
+ {
+ $CheckedLibs = "";
+ if($In::Opt{"OldStyle"}) {
+ $CheckedLibs .= "".getObjTitle()." (".keys(%{$In::ABI{1}{"Symbols"}}).")
";
+ }
+ else {
+ $CheckedLibs .= "".getObjTitle()." ".keys(%{$In::ABI{1}{"Symbols"}})."
";
+ }
+ $CheckedLibs .= "
\n";
+ $CheckedLibs .= "\n";
+ foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{1}{"Symbols"}})) {
+ $CheckedLibs .= $Library."
\n";
+ }
+ $CheckedLibs .= "
\n";
+ $CheckedLibs .= "
$TOP_REF
\n";
+ }
+
+ return $CheckedHeaders.$CheckedSources.$CheckedLibs;
+}
+
+sub getObjTitle()
+{
+ if(defined $UsedDump{1}{"DWARF"}) {
+ return "Objects";
+ }
+
+ return "Libraries";
+}
+
+sub getTypeProblemsCount($$)
+{
+ my ($TargetSeverity, $Level) = @_;
+ my $Count = 0;
+
+ foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}}))
+ {
+ my %Kinds_Target = ();
+ foreach my $Kind (keys(%{$TypeChanges{$Level}{$Type_Name}}))
+ {
+ if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) {
+ next;
+ }
+
+ foreach my $Loc (keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}}))
+ {
+ my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Loc}{"Target"};
+
+ if($Kinds_Target{$Kind}{$Target}) {
+ next;
+ }
+
+ $Kinds_Target{$Kind}{$Target} = 1;
+ $Count += 1;
+ }
+ }
+ }
+ return $Count;
+}
+
+sub getSummary($)
+{
+ my $Level = $_[0];
+ my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High,
+ $C_Problems_Low, $T_Problems_Medium, $T_Problems_Low, $I_Other, $T_Other, $C_Other) = (0,0,0,0,0,0,0,0,0,0,0,0);
+ %{$RESULT{$Level}} = (
+ "Problems"=>0,
+ "Warnings"=>0,
+ "Affected"=>0);
+ # check rules
+ foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
+ {
+ foreach my $Kind (keys(%{$CompatProblems{$Level}{$Symbol}}))
+ {
+ if(not defined $CompatRules{$Level}{$Kind})
+ { # unknown rule
+ if(not $UnknownRules{$Level}{$Kind})
+ { # only one warning
+ printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
+ $UnknownRules{$Level}{$Kind}=1;
+ }
+ delete($CompatProblems{$Level}{$Symbol}{$Kind});
+ }
+ }
+ }
+ foreach my $Constant (sort keys(%{$CompatProblems_Constants{$Level}}))
+ {
+ foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
+ {
+ if(not defined $CompatRules{$Level}{$Kind})
+ { # unknown rule
+ if(not $UnknownRules{$Level}{$Kind})
+ { # only one warning
+ printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
+ $UnknownRules{$Level}{$Kind}=1;
+ }
+ delete($CompatProblems_Constants{$Level}{$Constant}{$Kind});
+ }
+ }
+ }
+ foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
+ {
+ foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
+ {
+ if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols")
+ {
+ my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
+ foreach my $Loc (sort keys(%{$CompatProblems{$Level}{$Symbol}{$Kind}}))
+ {
+ if($Kind eq "Added_Symbol") {
+ $Added += 1;
+ }
+ elsif($Kind eq "Removed_Symbol")
+ {
+ $Removed += 1;
+ $TotalAffected{$Level}{$Symbol} = $Severity;
+ }
+ else
+ {
+ if($Severity eq "Safe") {
+ $I_Other += 1;
+ }
+ elsif($Severity eq "High") {
+ $I_Problems_High += 1;
+ }
+ elsif($Severity eq "Medium") {
+ $I_Problems_Medium += 1;
+ }
+ elsif($Severity eq "Low") {
+ $I_Problems_Low += 1;
+ }
+ if(($Severity ne "Low" or $In::Opt{"StrictCompat"})
+ and $Severity ne "Safe") {
+ $TotalAffected{$Level}{$Symbol} = $Severity;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ my %MethodTypeIndex = ();
+
+ foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
+ {
+ foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
+ {
+ if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
+ {
+ my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
+ if(($Severity ne "Low" or $In::Opt{"StrictCompat"})
+ and $Severity ne "Safe")
+ {
+ if(my $Sev = $TotalAffected{$Level}{$Symbol})
+ {
+ if($Severity_Val{$Severity}>$Severity_Val{$Sev}) {
+ $TotalAffected{$Level}{$Symbol} = $Severity;
+ }
+ }
+ else {
+ $TotalAffected{$Level}{$Symbol} = $Severity;
+ }
+ }
+
+ my $LSK = $CompatProblems{$Level}{$Symbol}{$Kind};
+ my (@Locs1, @Locs2) = ();
+ foreach my $Loc (sort keys(%{$LSK}))
+ {
+ if(index($Loc, "retval")==0 or index($Loc, "this")==0) {
+ push(@Locs2, $Loc);
+ }
+ else {
+ push(@Locs1, $Loc);
+ }
+ }
+
+ foreach my $Loc (@Locs1, @Locs2)
+ {
+ my $Type = $LSK->{$Loc}{"Type_Name"};
+ my $Target = $LSK->{$Loc}{"Target"};
+
+ if(defined $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target})
+ { # one location for one type and target
+ next;
+ }
+ $MethodTypeIndex{$Symbol}{$Type}{$Kind}{$Target} = 1;
+
+ $TypeChanges{$Level}{$Type}{$Kind}{$Loc} = $LSK->{$Loc};
+ $TypeProblemsIndex{$Level}{$Type}{$Kind}{$Loc}{$Symbol} = 1;
+ }
+ }
+ }
+ }
+
+ # clean memory
+ %MethodTypeIndex = ();
+
+ $T_Problems_High = getTypeProblemsCount("High", $Level);
+ $T_Problems_Medium = getTypeProblemsCount("Medium", $Level);
+ $T_Problems_Low = getTypeProblemsCount("Low", $Level);
+ $T_Other = getTypeProblemsCount("Safe", $Level);
+
+ # changed and removed public symbols
+ my $SCount = keys(%{$CheckedSymbols{$Level}});
+ if($In::Opt{"ExtendedCheck"})
+ { # don't count external_func_0 for constants
+ $SCount-=1;
+ }
+ if($SCount)
+ {
+ my %Weight = (
+ "High" => 100,
+ "Medium" => 50,
+ "Low" => 25
+ );
+ foreach (keys(%{$TotalAffected{$Level}})) {
+ $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
+ }
+ $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
+ }
+ else {
+ $RESULT{$Level}{"Affected"} = 0;
+ }
+
+ $RESULT{$Level}{"Affected"} = showNum($RESULT{$Level}{"Affected"});
+ if($RESULT{$Level}{"Affected"}>=100) {
+ $RESULT{$Level}{"Affected"} = 100;
+ }
+
+ $RESULT{$Level}{"Problems"} += $Removed;
+ $RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High;
+ $RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium;
+ if($In::Opt{"StrictCompat"}) {
+ $RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low;
+ }
+ else {
+ $RESULT{$Level}{"Warnings"} += $T_Problems_Low + $I_Problems_Low;
+ }
+
+ foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
+ {
+ foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
+ {
+ my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
+ if($Severity eq "Safe")
+ {
+ $C_Other+=1;
+ }
+ elsif($Severity eq "Low")
+ {
+ $C_Problems_Low+=1;
+ }
+ }
+ }
+
+ if($C_Problems_Low)
+ {
+ if($In::Opt{"StrictCompat"}) {
+ $RESULT{$Level}{"Problems"} += $C_Problems_Low;
+ }
+ else {
+ $RESULT{$Level}{"Warnings"} += $C_Problems_Low;
+ }
+ }
+ if($RESULT{$Level}{"Problems"}
+ and $RESULT{$Level}{"Affected"}) {
+ $RESULT{$Level}{"Verdict"} = "incompatible";
+ }
+ else {
+ $RESULT{$Level}{"Verdict"} = "compatible";
+ }
+
+ my $TotalTypes = keys(%{$CheckedTypes{$Level}});
+ if(not $TotalTypes)
+ { # list all the types
+ $TotalTypes = keys(%{$TName_Tid{1}});
+ }
+
+ my $TotalSymbols = keys(%{$CheckedSymbols{$Level}});
+
+ if($In::Opt{"ExtendedCheck"}) {
+ $TotalSymbols -= keys(%ExtendedSymbols);
+ }
+
+ my $AnyChanged = ($Added or $Removed or $I_Problems_High or $I_Problems_Medium or $I_Problems_Low or $T_Problems_High or
+ $C_Problems_Low or $T_Problems_Medium or $T_Problems_Low or $I_Other or $T_Other or $C_Other);
+
+ my ($Arch1, $Arch2) = ($In::ABI{1}{"Arch"}, $In::ABI{2}{"Arch"});
+ my ($GccV1, $GccV2) = ($In::ABI{1}{"GccVersion"}, $In::ABI{2}{"GccVersion"});
+ my ($ClangV1, $ClangV2) = ($In::ABI{1}{"ClangVersion"}, $In::ABI{2}{"ClangVersion"});
+
+ my ($TestInfo, $TestResults, $Problem_Summary) = ();
+
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ # test info
+ $TestInfo .= " ".$In::Opt{"TargetLib"}."\n";
+ $TestInfo .= " \n";
+ $TestInfo .= " ".$In::Desc{1}{"Version"}."\n";
+ $TestInfo .= " $Arch1\n";
+ if($GccV1) {
+ $TestInfo .= " $GccV1\n";
+ }
+ elsif($ClangV1) {
+ $TestInfo .= " $ClangV1\n";
+ }
+ $TestInfo .= " \n";
+
+ $TestInfo .= " \n";
+ $TestInfo .= " ".$In::Desc{2}{"Version"}."\n";
+ $TestInfo .= " $Arch2\n";
+ if($GccV2) {
+ $TestInfo .= " $GccV2\n";
+ }
+ elsif($ClangV2) {
+ $TestInfo .= " $ClangV2\n";
+ }
+ $TestInfo .= " \n";
+ $TestInfo = "\n".$TestInfo."\n\n";
+
+ # test results
+ if(my @Headers = keys(%{$In::ABI{1}{"Headers"}}))
+ {
+ $TestResults .= " \n";
+ foreach my $Name (sort {lc($a) cmp lc($b)} @Headers) {
+ $TestResults .= " ".getFilename($Name)."\n";
+ }
+ $TestResults .= " \n";
+ }
+
+ if(my @Sources = keys(%{$In::ABI{1}{"Sources"}}))
+ {
+ $TestResults .= " \n";
+ foreach my $Name (sort {lc($a) cmp lc($b)} @Sources) {
+ $TestResults .= " ".getFilename($Name)."\n";
+ }
+ $TestResults .= " \n";
+ }
+
+ $TestResults .= " \n";
+ foreach my $Library (sort {lc($a) cmp lc($b)} keys(%{$In::ABI{1}{"Symbols"}}))
+ {
+ $TestResults .= " $Library\n";
+ }
+ $TestResults .= " \n";
+
+ $TestResults .= " ".$TotalSymbols."\n";
+ $TestResults .= " ".$TotalTypes."\n";
+
+ $TestResults .= " ".$RESULT{$Level}{"Verdict"}."\n";
+ $TestResults .= " ".$RESULT{$Level}{"Affected"}."\n";
+ $TestResults = "\n".$TestResults."\n\n";
+
+ # problem summary
+ $Problem_Summary .= " ".$Added."\n";
+ $Problem_Summary .= " ".$Removed."\n";
+
+ $Problem_Summary .= " \n";
+ $Problem_Summary .= " $T_Problems_High\n";
+ $Problem_Summary .= " $T_Problems_Medium\n";
+ $Problem_Summary .= " $T_Problems_Low\n";
+ $Problem_Summary .= " $T_Other\n";
+ $Problem_Summary .= " \n";
+
+ $Problem_Summary .= " \n";
+ $Problem_Summary .= " $I_Problems_High\n";
+ $Problem_Summary .= " $I_Problems_Medium\n";
+ $Problem_Summary .= " $I_Problems_Low\n";
+ $Problem_Summary .= " $I_Other\n";
+ $Problem_Summary .= " \n";
+
+ $Problem_Summary .= " \n";
+ $Problem_Summary .= " $C_Problems_Low\n";
+ $Problem_Summary .= " \n";
+
+ $Problem_Summary = "\n".$Problem_Summary."\n\n";
+
+ return ($TestInfo.$TestResults.$Problem_Summary, "", $AnyChanged);
+ }
+ else
+ { # HTML
+ # test info
+ $TestInfo = "Test Info
\n";
+ $TestInfo .= "\n";
+
+ if($In::Opt{"TargetComponent"} eq "library") {
+ $TestInfo .= "| Library Name | ".$In::Opt{"TargetTitle"}." |
\n";
+ }
+ else {
+ $TestInfo .= "| Module Name | ".$In::Opt{"TargetTitle"}." |
\n";
+ }
+
+ my (@VInf1, @VInf2, $AddTestInfo) = ();
+
+ # CPU arch
+ if($Arch1 eq $Arch2)
+ { # go to the separate section
+ $AddTestInfo .= "| Arch | ".showArch($Arch1)." |
\n";
+ }
+ else
+ { # go to the version number
+ push(@VInf1, showArch($Arch1));
+ push(@VInf2, showArch($Arch2));
+ }
+
+ if($Level eq "Binary"
+ and $In::Opt{"Target"} ne "windows")
+ {
+ if($GccV1 and $GccV2)
+ { # GCC version
+ if($GccV1 eq $GccV2)
+ { # go to the separate section
+ $AddTestInfo .= "| GCC Version | $GccV1 |
\n";
+ }
+ else
+ { # go to the version number
+ push(@VInf1, "gcc ".$GccV1);
+ push(@VInf2, "gcc ".$GccV2);
+ }
+ }
+ elsif($ClangV1 and $ClangV2)
+ { # Clang version
+ if($ClangV1 eq $ClangV2)
+ { # go to the separate section
+ $AddTestInfo .= "| Clang Version | $ClangV1 |
\n";
+ }
+ else
+ { # go to the version number
+ push(@VInf1, "clang ".$ClangV1);
+ push(@VInf2, "clang ".$ClangV2);
+ }
+ }
+ elsif($GccV1 and $ClangV2)
+ {
+ push(@VInf1, "gcc ".$GccV1);
+ push(@VInf2, "clang ".$ClangV2);
+ }
+ elsif($ClangV1 and $GccV2)
+ {
+ push(@VInf1, "clang ".$ClangV1);
+ push(@VInf2, "gcc ".$GccV2);
+ }
+ }
+ # show long version names with GCC version and CPU architecture name (if different)
+ $TestInfo .= "| Version #1 | ".$In::Desc{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")." |
\n";
+ $TestInfo .= "| Version #2 | ".$In::Desc{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")." |
\n";
+ $TestInfo .= $AddTestInfo;
+
+ if($In::Opt{"ExtendedCheck"}) {
+ $TestInfo .= "| Mode | Extended |
\n";
+ }
+ if($In::Opt{"JoinReport"})
+ {
+ if($Level eq "Binary") {
+ $TestInfo .= "| Subject | Binary Compatibility |
\n"; # Run-time
+ }
+ elsif($Level eq "Source") {
+ $TestInfo .= "| Subject | Source Compatibility |
\n"; # Build-time
+ }
+ }
+ $TestInfo .= "
\n";
+
+ # test results
+ $TestResults = "Test Results
\n";
+ $TestResults .= "";
+
+ if(my @Headers = getCheckedHeaders(1))
+ {
+ my $Headers_Link = "".($#Headers + 1)."";
+ $TestResults .= "| Total Header Files | ".$Headers_Link." |
\n";
+ }
+
+ if(my @Sources = keys(%{$In::ABI{1}{"Sources"}}))
+ {
+ my $Src_Link = "".($#Sources + 1)."";
+ $TestResults .= "| Total Source Files | ".$Src_Link." |
\n";
+ }
+
+ if(not $In::Opt{"ExtendedCheck"})
+ {
+ my $Libs_Link = "0";
+ $Libs_Link = "".keys(%{$In::ABI{1}{"Symbols"}})."" if(keys(%{$In::ABI{1}{"Symbols"}})>0);
+ $TestResults .= "| Total ".getObjTitle()." | ".($In::Opt{"CheckHeadersOnly"}?"0 (not analyzed)":$Libs_Link)." |
\n";
+ }
+
+ $TestResults .= "| Total Symbols / Types | ".$TotalSymbols." / ".$TotalTypes." |
\n";
+
+ my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";";
+ if($In::Opt{"JoinReport"}) {
+ $META_DATA = "kind:".lc($Level).";".$META_DATA;
+ }
+
+ my $BC_Rate = showNum(100 - $RESULT{$Level}{"Affected"});
+
+ $TestResults .= "| Compatibility | \n";
+ if($RESULT{$Level}{"Verdict"} eq "incompatible")
+ {
+ my $Cl = "incompatible";
+ if($BC_Rate>=90) {
+ $Cl = "warning";
+ }
+ elsif($BC_Rate>=80) {
+ $Cl = "almost_compatible";
+ }
+
+ $TestResults .= "".$BC_Rate."% | \n";
+ }
+ else {
+ $TestResults .= "100% | \n";
+ }
+ $TestResults .= "
\n";
+ $TestResults .= "
\n";
+
+ $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
+ # problem summary
+ $Problem_Summary = "Problem Summary
\n";
+ $Problem_Summary .= "";
+ $Problem_Summary .= " | Severity | Count |
";
+
+ my $Added_Link = "0";
+ if($Added>0)
+ {
+ if($In::Opt{"JoinReport"}) {
+ $Added_Link = "$Added";
+ }
+ else {
+ $Added_Link = "$Added";
+ }
+ }
+ $META_DATA .= "added:$Added;";
+ $Problem_Summary .= "| Added Symbols | - | $Added_Link |
\n";
+
+ my $Removed_Link = "0";
+ if($Removed>0)
+ {
+ if($In::Opt{"JoinReport"}) {
+ $Removed_Link = "$Removed"
+ }
+ else {
+ $Removed_Link = "$Removed"
+ }
+ }
+ $META_DATA .= "removed:$Removed;";
+ $Problem_Summary .= "| Removed Symbols | ";
+ $Problem_Summary .= "High | $Removed_Link |
\n";
+
+ my $TH_Link = "0";
+ $TH_Link = "$T_Problems_High" if($T_Problems_High>0);
+ $META_DATA .= "type_problems_high:$T_Problems_High;";
+ $Problem_Summary .= "Problems with Data Types | ";
+ $Problem_Summary .= "High | $TH_Link |
\n";
+
+ my $TM_Link = "0";
+ $TM_Link = "$T_Problems_Medium" if($T_Problems_Medium>0);
+ $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
+ $Problem_Summary .= "| Medium | $TM_Link |
\n";
+
+ my $TL_Link = "0";
+ $TL_Link = "$T_Problems_Low" if($T_Problems_Low>0);
+ $META_DATA .= "type_problems_low:$T_Problems_Low;";
+ $Problem_Summary .= "| Low | $TL_Link |
\n";
+
+ my $IH_Link = "0";
+ $IH_Link = "$I_Problems_High" if($I_Problems_High>0);
+ $META_DATA .= "interface_problems_high:$I_Problems_High;";
+ $Problem_Summary .= "Problems with Symbols | ";
+ $Problem_Summary .= "High | $IH_Link |
\n";
+
+ my $IM_Link = "0";
+ $IM_Link = "$I_Problems_Medium" if($I_Problems_Medium>0);
+ $META_DATA .= "interface_problems_medium:$I_Problems_Medium;";
+ $Problem_Summary .= "| Medium | $IM_Link |
\n";
+
+ my $IL_Link = "0";
+ $IL_Link = "$I_Problems_Low" if($I_Problems_Low>0);
+ $META_DATA .= "interface_problems_low:$I_Problems_Low;";
+ $Problem_Summary .= "| Low | $IL_Link |
\n";
+
+ my $ChangedConstants_Link = "0";
+ if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) {
+ $ChangedConstants_Link = "$C_Problems_Low";
+ }
+ $META_DATA .= "changed_constants:$C_Problems_Low;";
+ $Problem_Summary .= "Problems with Constants | Low | $ChangedConstants_Link |
\n";
+
+ # Safe Changes
+ if($T_Other)
+ {
+ my $TS_Link = "$T_Other";
+ $Problem_Summary .= "Other Changes in Data Types | - | $TS_Link |
\n";
+ $META_DATA .= "type_changes_other:$T_Other;";
+ }
+
+ if($I_Other)
+ {
+ my $IS_Link = "$I_Other";
+ $Problem_Summary .= "Other Changes in Symbols | - | $IS_Link |
\n";
+ $META_DATA .= "interface_changes_other:$I_Other;";
+ }
+
+ if($C_Other)
+ {
+ my $CS_Link = "$C_Other";
+ $Problem_Summary .= "Other Changes in Constants | - | $CS_Link |
\n";
+ $META_DATA .= "constant_changes_other:$C_Other;";
+ }
+
+ $META_DATA .= "tool_version:$TOOL_VERSION";
+ $Problem_Summary .= "
\n";
+
+ return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA, $AnyChanged);
+ }
+}
+
+sub getStyle($$$)
+{
+ my ($Subj, $Act, $Num) = @_;
+ my %Style = (
+ "Added"=>"new",
+ "Removed"=>"failed",
+ "Safe"=>"passed",
+ "Low"=>"warning",
+ "Medium"=>"failed",
+ "High"=>"failed"
+ );
+
+ if($Num>0) {
+ return " class='".$Style{$Act}."'";
+ }
+
+ return "";
+}
+
+sub getReportChangedConstants($$)
+{
+ my ($TargetSeverity, $Level) = @_;
+ my $CHANGED_CONSTANTS = "";
+
+ my %ReportMap = ();
+ foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
+ {
+ my $Header = $Constants{1}{$Constant}{"Header"};
+ if(not $Header) {
+ $Header = $Constants{1}{$Constant}{"Source"};
+ }
+ if(not $Header)
+ { # added
+ $Header = $Constants{2}{$Constant}{"Header"};
+ if(not $Header) {
+ $Header = $Constants{2}{$Constant}{"Source"}
+ }
+ }
+
+ foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
+ {
+ if(not defined $CompatRules{$Level}{$Kind}) {
+ next;
+ }
+ if($TargetSeverity ne $CompatRules{$Level}{$Kind}{"Severity"}) {
+ next;
+ }
+ $ReportMap{$Header}{$Constant}{$Kind} = 1;
+ }
+ }
+
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ $CHANGED_CONSTANTS .= " \n";
+ foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ $CHANGED_CONSTANTS .= " \n";
+ foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
+ {
+ my $Change = $CompatRules{$Level}{$Kind}{"Change"};
+ my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
+ my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
+
+ $CHANGED_CONSTANTS .= " \n";
+ $CHANGED_CONSTANTS .= " $Change\n";
+ $CHANGED_CONSTANTS .= " $Effect\n";
+ if($Overcome) {
+ $CHANGED_CONSTANTS .= " $Overcome\n";
+ }
+ $CHANGED_CONSTANTS .= " \n";
+ }
+ $CHANGED_CONSTANTS .= " \n";
+ }
+ $CHANGED_CONSTANTS .= " \n";
+ }
+ $CHANGED_CONSTANTS = "\n".$CHANGED_CONSTANTS."\n\n";
+ }
+ else
+ { # HTML
+ my $ProblemsNum = 0;
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ $CHANGED_CONSTANTS .= "$HeaderName
\n";
+ foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ my $Report = "";
+
+ foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
+ {
+ my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind});
+ my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
+ $Report .= "\n| 1 | \n".$Change." | \n$Effect | \n
\n";
+ $ProblemsNum += 1;
+ }
+ if($Report)
+ {
+ $Report = $ContentDivStart."\n\n | \nChange | \nEffect | \n
\n".$Report."
\n
\n$ContentDivEnd\n";
+ $Report = $ContentSpanStart."[+] ".$Constant.$ContentSpanEnd."
\n".$Report;
+ $Report = insertIDs($Report);
+ }
+ $CHANGED_CONSTANTS .= $Report;
+ }
+ $CHANGED_CONSTANTS .= "
\n";
+ }
+ if($CHANGED_CONSTANTS)
+ {
+ my $Title = "Problems with Constants, $TargetSeverity Severity";
+ if($TargetSeverity eq "Safe")
+ { # Safe Changes
+ $Title = "Other Changes in Constants";
+ }
+ if($In::Opt{"OldStyle"}) {
+ $CHANGED_CONSTANTS = "$Title ($ProblemsNum)
\n".$CHANGED_CONSTANTS;
+ }
+ else {
+ $CHANGED_CONSTANTS = "$Title $ProblemsNum
\n".$CHANGED_CONSTANTS;
+ }
+ $CHANGED_CONSTANTS = "\n".$CHANGED_CONSTANTS.$TOP_REF."
\n";
+ }
+ }
+ return $CHANGED_CONSTANTS;
+}
+
+sub getTitle($$$)
+{
+ my ($Header, $Library, $NameSpace) = @_;
+ my $Title = "";
+
+ if($Header and $Library)
+ {
+ $Title .= "$Header";
+ $Title .= ", $Library
\n";
+ }
+ elsif($Library) {
+ $Title .= "$Library
\n";
+ }
+ elsif($Header) {
+ $Title .= "$Header
\n";
+ }
+
+ if($NameSpace) {
+ $Title .= "namespace $NameSpace
\n";
+ }
+
+ return $Title;
+}
+
+sub getReportAdded($)
+{
+ my $Level = $_[0];
+ my $ADDED_INTERFACES = "";
+ my %ReportMap = ();
+ foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
+ {
+ foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
+ {
+ if($Kind eq "Added_Symbol")
+ {
+ my $HeaderName = $CompSign{2}{$Symbol}{"Header"};
+ if(not $HeaderName) {
+ $HeaderName = $CompSign{2}{$Symbol}{"Source"};
+ }
+
+ my $DyLib = $In::ABI{2}{"SymLib"}{$Symbol};
+ if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html")
+ { # do not show library name in the HTML report
+ $DyLib = "";
+ }
+ $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
+ }
+ }
+ }
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ $ADDED_INTERFACES .= " \n";
+ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ $ADDED_INTERFACES .= " \n";
+ foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
+ $ADDED_INTERFACES .= " $Symbol\n";
+ }
+ $ADDED_INTERFACES .= " \n";
+ }
+ $ADDED_INTERFACES .= " \n";
+ }
+ $ADDED_INTERFACES = "\n".$ADDED_INTERFACES."\n\n";
+ }
+ else
+ { # HTML
+ my $Added_Number = 0;
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ my %NameSpaceSymbols = ();
+ foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
+ $NameSpaceSymbols{selectSymbolNs($Symbol, 2)}{$Symbol} = 1;
+ }
+ foreach my $NameSpace (sort keys(%NameSpaceSymbols))
+ {
+ $ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
+ my @SortedInterfaces = sort {lc($CompSign{2}{$a}{"Unmangled"}) cmp lc($CompSign{2}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}});
+ foreach my $Symbol (@SortedInterfaces)
+ {
+ $Added_Number += 1;
+ my $Signature = highLight_ItalicColor($Symbol, 2);
+ if($NameSpace) {
+ $Signature = cutNs($Signature, $NameSpace);
+ }
+
+ if($Symbol=~/\A(_Z|\?)/) {
+ $ADDED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol\n
\n
\n".$ContentDivEnd."\n");
+ }
+ else {
+ $ADDED_INTERFACES .= "".$Signature."
\n";
+ }
+ }
+ $ADDED_INTERFACES .= "
\n";
+ }
+ }
+ }
+ if($ADDED_INTERFACES)
+ {
+ my $Anchor = "";
+ if($In::Opt{"JoinReport"}) {
+ $Anchor = "";
+ }
+ if($In::Opt{"OldStyle"}) {
+ $ADDED_INTERFACES = "Added Symbols ($Added_Number)
\n".$ADDED_INTERFACES;
+ }
+ else {
+ $ADDED_INTERFACES = "Added Symbols $Added_Number
\n".$ADDED_INTERFACES;
+ }
+ $ADDED_INTERFACES = $Anchor.$ADDED_INTERFACES.$TOP_REF."
\n";
+ }
+ }
+ return $ADDED_INTERFACES;
+}
+
+sub getReportRemoved($)
+{
+ my $Level = $_[0];
+ my $REMOVED_INTERFACES = "";
+ my %ReportMap = ();
+ foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
+ {
+ foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
+ {
+ if($Kind eq "Removed_Symbol")
+ {
+ my $HeaderName = $CompSign{1}{$Symbol}{"Header"};
+ if(not $HeaderName) {
+ $HeaderName = $CompSign{1}{$Symbol}{"Source"};
+ }
+
+ my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol};
+ if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html")
+ { # do not show library name in HTML report
+ $DyLib = "";
+ }
+ $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
+ }
+ }
+ }
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ $REMOVED_INTERFACES .= " \n";
+ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ $REMOVED_INTERFACES .= " \n";
+ foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
+ $REMOVED_INTERFACES .= " $Symbol\n";
+ }
+ $REMOVED_INTERFACES .= " \n";
+ }
+ $REMOVED_INTERFACES .= " \n";
+ }
+ $REMOVED_INTERFACES = "\n".$REMOVED_INTERFACES."\n\n";
+ }
+ else
+ { # HTML
+ my $Removed_Number = 0;
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ my %NameSpaceSymbols = ();
+ foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
+ $NameSpaceSymbols{selectSymbolNs($Interface, 1)}{$Interface} = 1;
+ }
+ foreach my $NameSpace (sort keys(%NameSpaceSymbols))
+ {
+ $REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
+ my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}});
+ foreach my $Symbol (@SortedInterfaces)
+ {
+ $Removed_Number += 1;
+ my $Signature = highLight_ItalicColor($Symbol, 1);
+ if($NameSpace) {
+ $Signature = cutNs($Signature, $NameSpace);
+ }
+ if($Symbol=~/\A(_Z|\?)/) {
+ $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.$Signature.$ContentSpanEnd."
\n".$ContentDivStart."$Symbol\n
\n
\n".$ContentDivEnd."\n");
+ }
+ else {
+ $REMOVED_INTERFACES .= "".$Signature."
\n";
+ }
+ }
+ }
+ $REMOVED_INTERFACES .= "
\n";
+ }
+ }
+ if($REMOVED_INTERFACES)
+ {
+ my $Anchor = "";
+ if($In::Opt{"JoinReport"}) {
+ $Anchor = "";
+ }
+ if($In::Opt{"OldStyle"}) {
+ $REMOVED_INTERFACES = "Removed Symbols ($Removed_Number)
\n".$REMOVED_INTERFACES;
+ }
+ else {
+ $REMOVED_INTERFACES = "Removed Symbols $Removed_Number
\n".$REMOVED_INTERFACES;
+ }
+
+ $REMOVED_INTERFACES = $Anchor.$REMOVED_INTERFACES.$TOP_REF."
\n";
+ }
+ }
+ return $REMOVED_INTERFACES;
+}
+
+sub getXmlParams($$)
+{
+ my ($Content, $Problem) = @_;
+
+ my %XMLparams = ();
+ foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
+ {
+ my $Macro = "\@".lc($Attr);
+ if($Content=~/\Q$Macro\E/)
+ {
+ my $Value = $Problem->{$Attr};
+
+ if($Attr eq "Param_Pos") {
+ $Value = showPos($Value);
+ }
+
+ $XMLparams{lc($Attr)} = $Value;
+ }
+ }
+ my @PString = ();
+ foreach my $P (sort {$b cmp $a} keys(%XMLparams)) {
+ push(@PString, $P."=\"".xmlSpecChars($XMLparams{$P})."\"");
+ }
+ if(@PString) {
+ return " ".join(" ", @PString);
+ }
+ return "";
+}
+
+sub addMarkup($)
+{
+ my $Content = $_[0];
+
+ # auto-markup
+ $Content=~s/\n[ ]*//; # spaces
+ $Content=~s!(\@\w+\s*\(\@\w+\))!$1!g; # @old_type (@old_size)
+ $Content=~s!(... \(\w+\))!$1!g; # ... (va_list)
+ $Content=~s!(.+?)!$1!g;
+ $Content=~s!([2-9]\))!
$1!g; # 1), 2), ...
+
+ if($Content=~/\ANOTE:/)
+ { # notes
+ $Content=~s!(NOTE):!$1:!g;
+ }
+ else {
+ $Content=~s!(NOTE):!
$1:!g;
+ }
+ $Content=~s! (out)-! $1-!g; # out-parameters
+
+ my @Keywords = (
+ "void",
+ "const",
+ "static",
+ "restrict",
+ "volatile",
+ "register",
+ "virtual"
+ );
+ my $MKeys = join("|", @Keywords);
+ foreach (@Keywords) {
+ $MKeys .= "|non-".$_;
+ }
+ $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1$2$3!ig; # intrinsic types, modifiers
+
+ # Markdown
+ $Content=~s!\*\*([\w\-]+?)\*\*!$1!ig;
+ $Content=~s!\*([\w\-]+?)\*!$1!ig;
+
+ return $Content;
+}
+
+sub applyMacroses($$$$)
+{
+ my ($Level, $Kind, $Content, $Problem) = @_;
+
+ $Problem->{"Word_Size"} = $In::ABI{2}{"WordSize"};
+ $Content = addMarkup($Content);
+
+ # macros
+ foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
+ {
+ my $Macro = "\@".lc($Attr);
+ my $Value = $Problem->{$Attr};
+ if(not defined $Value
+ or $Value eq "") {
+ next;
+ }
+
+ if(index($Content, $Macro)==-1) {
+ next;
+ }
+
+ if($Attr eq "Param_Pos") {
+ $Value = showPos($Value);
+ }
+
+ if($Value=~/\s/) {
+ $Value = "".specChars($Value)."";
+ }
+ elsif($Value=~/\A\d+\Z/
+ and ($Attr eq "Old_Size" or $Attr eq "New_Size"))
+ { # bits to bytes
+ if($Value % $BYTE)
+ { # bits
+ if($Value==1) {
+ $Value = "".$Value." bit";
+ }
+ else {
+ $Value = "".$Value." bits";
+ }
+ }
+ else
+ { # bytes
+ $Value /= $BYTE;
+ if($Value==1) {
+ $Value = "".$Value." byte";
+ }
+ else {
+ $Value = "".$Value." bytes";
+ }
+ }
+ }
+ else
+ {
+ my $Fmt = "Class|Name|Qual|HTML|Desc";
+ if($Kind!~/Overridden/) {
+ $Fmt = "Name|Qual|HTML|Desc";
+ }
+
+ my $V1 = (defined $CompSign{1}{$Value} and defined $CompSign{1}{$Value}{"ShortName"});
+ my $V2 = (defined $CompSign{2}{$Value} and defined $CompSign{2}{$Value}{"ShortName"});
+
+ if($Kind!~/Symbol_Became|Symbol_Changed|Method_Became/
+ and ($V1 or $V2))
+ { # symbols
+ if($V1) {
+ $Value = blackName(getSignature($Value, 1, $Fmt));
+ }
+ else {
+ $Value = blackName(getSignature($Value, 2, $Fmt));
+ }
+ }
+ else
+ {
+ $Value = "".specChars($Value)."";
+ }
+ }
+ $Content=~s/\Q$Macro\E/$Value/g;
+ }
+
+ if($Content=~/(\A|[^\@\w])\@\w/)
+ {
+ if(not $IncompleteRules{$Level}{$Kind})
+ { # only one warning
+ printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")");
+ $IncompleteRules{$Level}{$Kind} = 1;
+ }
+ }
+ return $Content;
+}
+
+sub getReportSymbolProblems($$)
+{
+ my ($TargetSeverity, $Level) = @_;
+ my $INTERFACE_PROBLEMS = "";
+ my (%ReportMap, %SymbolChanges) = ();
+
+ foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
+ {
+ my ($SN, $SS, $SV) = symbolParts($Symbol);
+ if($SV and defined $CompatProblems{$Level}{$SN}) {
+ next;
+ }
+
+ if(not defined $CompSign{1}{$Symbol})
+ { # added symbols
+ next;
+ }
+
+ my $HeaderName = $CompSign{1}{$Symbol}{"Header"};
+ if(not $HeaderName) {
+ $HeaderName = $CompSign{1}{$Symbol}{"Source"};
+ }
+
+ my $DyLib = $In::ABI{1}{"SymLib"}{$Symbol};
+ if(not $DyLib and my $VSym = $In::ABI{1}{"SymbolVersion"}{$Symbol})
+ { # Symbol with Version
+ $DyLib = $In::ABI{1}{"SymLib"}{$VSym};
+ }
+ if(not $DyLib)
+ { # const global data
+ $DyLib = "";
+ }
+ if($Level eq "Source" and $In::Opt{"ReportFormat"} eq "html")
+ { # do not show library name in HTML report
+ $DyLib = "";
+ }
+
+ foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
+ {
+ if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols"
+ and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol")
+ {
+ my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
+ if($Severity eq $TargetSeverity)
+ {
+ $SymbolChanges{$Symbol}{$Kind} = $CompatProblems{$Level}{$Symbol}{$Kind};
+ $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
+ }
+ }
+ }
+ }
+
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ $INTERFACE_PROBLEMS .= " \n";
+ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ $INTERFACE_PROBLEMS .= " \n";
+
+ my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$DyLib}});
+ foreach my $Symbol (@SortedInterfaces)
+ {
+ $INTERFACE_PROBLEMS .= " \n";
+ foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
+ {
+ foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
+ {
+ my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc};
+
+ $INTERFACE_PROBLEMS .= " \n";
+ my $Change = $CompatRules{$Level}{$Kind}{"Change"};
+ $INTERFACE_PROBLEMS .= " $Change\n";
+ my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
+ $INTERFACE_PROBLEMS .= " $Effect\n";
+ if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
+ $INTERFACE_PROBLEMS .= " $Overcome\n";
+ }
+ $INTERFACE_PROBLEMS .= " \n";
+ }
+ }
+ $INTERFACE_PROBLEMS .= " \n";
+ }
+ $INTERFACE_PROBLEMS .= " \n";
+ }
+ $INTERFACE_PROBLEMS .= " \n";
+ }
+ $INTERFACE_PROBLEMS = "\n".$INTERFACE_PROBLEMS."\n\n";
+ }
+ else
+ { # HTML
+ my $ProblemsNum = 0;
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
+ {
+ my (%NameSpaceSymbols, %NewSignature) = ();
+ foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
+ $NameSpaceSymbols{selectSymbolNs($Symbol, 1)}{$Symbol} = 1;
+ }
+ foreach my $NameSpace (sort keys(%NameSpaceSymbols))
+ {
+ $INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace);
+
+ my @SortedInterfaces = sort {lc($CompSign{1}{$a}{"Unmangled"}) cmp lc($CompSign{1}{$b}{"Unmangled"})} sort {lc($a) cmp lc($b)} keys(%{$NameSpaceSymbols{$NameSpace}});
+ foreach my $Symbol (@SortedInterfaces)
+ {
+ my $SYMBOL_REPORT = "";
+ my $ProblemNum = 1;
+ foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
+ {
+ foreach my $Loc (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
+ {
+ my $ProblemAttr = $SymbolChanges{$Symbol}{$Kind}{$Loc};
+
+ if(my $NSign = $ProblemAttr->{"New_Signature"}) {
+ $NewSignature{$Symbol} = $NSign;
+ }
+
+ if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr))
+ {
+ my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr);
+ $SYMBOL_REPORT .= "\n| $ProblemNum | \n".$Change." | \n".$Effect." | \n
\n";
+ $ProblemNum += 1;
+ $ProblemsNum += 1;
+ }
+ }
+ }
+ $ProblemNum -= 1;
+ if($SYMBOL_REPORT)
+ {
+ my $ShowSymbol = highLight_ItalicColor($Symbol, 1);
+
+ if($NameSpace)
+ {
+ $SYMBOL_REPORT = cutNs($SYMBOL_REPORT, $NameSpace);
+ $ShowSymbol = cutNs($ShowSymbol, $NameSpace);
+ }
+
+ $INTERFACE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowSymbol;
+ if($In::Opt{"OldStyle"}) {
+ $INTERFACE_PROBLEMS .= " ($ProblemNum)";
+ }
+ else {
+ $INTERFACE_PROBLEMS .= " $ProblemNum ";
+ }
+ $INTERFACE_PROBLEMS .= $ContentSpanEnd."
\n";
+ $INTERFACE_PROBLEMS .= $ContentDivStart."\n";
+
+ if(my $NSign = $NewSignature{$Symbol})
+ { # argument list changed to
+ $NSign = highLight_ItalicColor($NSign, 2);
+ if($NameSpace) {
+ $NSign = cutNs($NSign, $NameSpace);
+ }
+ $INTERFACE_PROBLEMS .= "\n⇣\n
\n".$NSign."
\n";
+ }
+
+ if($Symbol=~/\A(_Z|\?)/) {
+ $INTERFACE_PROBLEMS .= "$Symbol
\n";
+ }
+
+ $INTERFACE_PROBLEMS .= "\n\n | \nChange | \nEffect | \n
\n$SYMBOL_REPORT
\n
\n";
+ $INTERFACE_PROBLEMS .= $ContentDivEnd;
+ }
+ }
+ $INTERFACE_PROBLEMS .= "
\n";
+ }
+ }
+ }
+
+ if($INTERFACE_PROBLEMS)
+ {
+ $INTERFACE_PROBLEMS = insertIDs($INTERFACE_PROBLEMS);
+ my $Title = "Problems with Symbols, $TargetSeverity Severity";
+ if($TargetSeverity eq "Safe")
+ { # Safe Changes
+ $Title = "Other Changes in Symbols";
+ }
+ if($In::Opt{"OldStyle"}) {
+ $INTERFACE_PROBLEMS = "$Title ($ProblemsNum)
\n".$INTERFACE_PROBLEMS;
+ }
+ else {
+ $INTERFACE_PROBLEMS = "$Title $ProblemsNum
\n".$INTERFACE_PROBLEMS;
+ }
+ $INTERFACE_PROBLEMS = "\n".$INTERFACE_PROBLEMS.$TOP_REF."
\n";
+ }
+ }
+ return $INTERFACE_PROBLEMS;
+}
+
+sub cutNs($$)
+{
+ my ($N, $Ns) = @_;
+ $N=~s/\b\Q$Ns\E:://g;
+ return $N;
+}
+
+sub getReportTypeProblems($$)
+{
+ my ($TargetSeverity, $Level) = @_;
+ my $TYPE_PROBLEMS = "";
+
+ my %ReportMap = ();
+ my %TypeChanges_Sev = ();
+
+ foreach my $TypeName (keys(%{$TypeChanges{$Level}}))
+ {
+ my $Tid = $TName_Tid{1}{$TypeName};
+ my $HeaderName = $TypeInfo{1}{$Tid}{"Header"};
+ if(not $HeaderName) {
+ $HeaderName = $TypeInfo{1}{$Tid}{"Source"};
+ }
+
+ foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}}))
+ {
+ if($CompatRules{$Level}{$Kind}{"Severity"} ne $TargetSeverity) {
+ next;
+ }
+
+ foreach my $Loc (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}}))
+ {
+ $ReportMap{$HeaderName}{$TypeName} = 1;
+ $TypeChanges_Sev{$TypeName}{$Kind}{$Loc} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Loc};
+ }
+ }
+ }
+
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ $TYPE_PROBLEMS .= " \n";
+ foreach my $TypeName (keys(%{$ReportMap{$HeaderName}}))
+ {
+ my (%Kinds_Locations, %Kinds_Target) = ();
+ $TYPE_PROBLEMS .= " \n";
+ foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}}))
+ {
+ foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
+ {
+ $Kinds_Locations{$Kind}{$Loc} = 1;
+
+ my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"};
+ if($Kinds_Target{$Kind}{$Target}) {
+ next;
+ }
+ $Kinds_Target{$Kind}{$Target} = 1;
+
+ my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc};
+ $TYPE_PROBLEMS .= " \n";
+ my $Change = $CompatRules{$Level}{$Kind}{"Change"};
+ $TYPE_PROBLEMS .= " $Change\n";
+ my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
+ $TYPE_PROBLEMS .= " $Effect\n";
+ if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
+ $TYPE_PROBLEMS .= " $Overcome\n";
+ }
+ $TYPE_PROBLEMS .= " \n";
+ }
+ }
+ $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
+ if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
+ $TYPE_PROBLEMS .= showVTables($TypeName);
+ }
+ $TYPE_PROBLEMS .= " \n";
+ }
+ $TYPE_PROBLEMS .= " \n";
+ }
+ $TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS."\n\n";
+ }
+ else
+ { # HTML
+ my $ProblemsNum = 0;
+ foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
+ {
+ my (%NameSpace_Type) = ();
+ foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) {
+ $NameSpace_Type{selectTypeNs($TypeName, 1)}{$TypeName} = 1;
+ }
+ foreach my $NameSpace (sort keys(%NameSpace_Type))
+ {
+ $TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace);
+ my @SortedTypes = sort {lc(showType($a, 0, 1)) cmp lc(showType($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}});
+ foreach my $TypeName (@SortedTypes)
+ {
+ my $ProblemNum = 1;
+ my $TYPE_REPORT = "";
+ my (%Kinds_Locations, %Kinds_Target) = ();
+
+ foreach my $Kind (sort {(index($b, "Size")!=-1) cmp (index($a, "Size")!=-1)} sort keys(%{$TypeChanges_Sev{$TypeName}}))
+ {
+ foreach my $Loc (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
+ {
+ $Kinds_Locations{$Kind}{$Loc} = 1;
+
+ my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc}{"Target"};
+ if($Kinds_Target{$Kind}{$Target}) {
+ next;
+ }
+ $Kinds_Target{$Kind}{$Target} = 1;
+
+ my $ProblemAttr = $TypeChanges_Sev{$TypeName}{$Kind}{$Loc};
+ if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $ProblemAttr))
+ {
+ my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, $ProblemAttr);
+ $TYPE_REPORT .= "\n| $ProblemNum | \n".$Change." | \n$Effect | \n
\n";
+ $ProblemNum += 1;
+ $ProblemsNum += 1;
+ }
+ }
+ }
+ $ProblemNum -= 1;
+ if($TYPE_REPORT)
+ {
+ my $Affected = getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
+ my $ShowVTables = "";
+ if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
+ $ShowVTables = showVTables($TypeName);
+ }
+
+ my $ShowType = showType($TypeName, 1, 1);
+
+ if($NameSpace)
+ {
+ $TYPE_REPORT = cutNs($TYPE_REPORT, $NameSpace);
+ $ShowType = cutNs($ShowType, $NameSpace);
+ $Affected = cutNs($Affected, $NameSpace);
+ $ShowVTables = cutNs($ShowVTables, $NameSpace);
+ }
+
+ $TYPE_PROBLEMS .= $ContentSpanStart."[+] ".$ShowType;
+ if($In::Opt{"OldStyle"}) {
+ $TYPE_PROBLEMS .= " ($ProblemNum)";
+ }
+ else {
+ $TYPE_PROBLEMS .= " $ProblemNum ";
+ }
+ $TYPE_PROBLEMS .= $ContentSpanEnd;
+ $TYPE_PROBLEMS .= "
\n".$ContentDivStart."\n";
+ $TYPE_PROBLEMS .= " | \nChange | \n";
+ $TYPE_PROBLEMS .= "Effect |
".$TYPE_REPORT."
\n";
+ $TYPE_PROBLEMS .= $ShowVTables.$Affected."
".$ContentDivEnd."\n";
+ }
+ }
+ $TYPE_PROBLEMS .= "
\n";
+ }
+ }
+
+ if($TYPE_PROBLEMS)
+ {
+ $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
+ my $Title = "Problems with Data Types, $TargetSeverity Severity";
+ if($TargetSeverity eq "Safe")
+ { # Safe Changes
+ $Title = "Other Changes in Data Types";
+ }
+ if($In::Opt{"OldStyle"}) {
+ $TYPE_PROBLEMS = "$Title ($ProblemsNum)
\n".$TYPE_PROBLEMS;
+ }
+ else {
+ $TYPE_PROBLEMS = "$Title $ProblemsNum
\n".$TYPE_PROBLEMS;
+ }
+ $TYPE_PROBLEMS = "\n".$TYPE_PROBLEMS.$TOP_REF."
\n";
+ }
+ }
+ return $TYPE_PROBLEMS;
+}
+
+sub showType($$$)
+{
+ my ($Name, $Html, $LVer) = @_;
+ my $TType = $TypeInfo{$LVer}{$TName_Tid{$LVer}{$Name}}{"Type"};
+ $TType = lc($TType);
+ if($TType=~/struct|union|enum/) {
+ $Name=~s/\A\Q$TType\E //g;
+ }
+ if($Html) {
+ $Name = "".$TType." ".specChars($Name);
+ }
+ else {
+ $Name = $TType." ".$Name;
+ }
+ return $Name;
+}
+
+sub getAnchor($$$)
+{
+ my ($Kind, $Level, $Severity) = @_;
+ if($In::Opt{"JoinReport"})
+ {
+ if($Severity eq "Safe") {
+ return "Other_".$Level."_Changes_In_".$Kind."s";
+ }
+ else {
+ return $Kind."_".$Level."_Problems_".$Severity;
+ }
+ }
+ else
+ {
+ if($Severity eq "Safe") {
+ return "Other_Changes_In_".$Kind."s";
+ }
+ else {
+ return $Kind."_Problems_".$Severity;
+ }
+ }
+}
+
+sub showVTables($)
+{
+ my $TypeName = $_[0];
+ my $TypeId1 = $TName_Tid{1}{$TypeName};
+ my %Type1 = getType($TypeId1, 1);
+ if(defined $Type1{"VTable"}
+ and keys(%{$Type1{"VTable"}}))
+ {
+ my $TypeId2 = $TName_Tid{2}{$TypeName};
+ my %Type2 = getType($TypeId2, 2);
+ if(defined $Type2{"VTable"}
+ and keys(%{$Type2{"VTable"}}))
+ {
+ my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
+ my %Entries = ();
+ foreach my $Index (sort {$a<=>$b} (keys(%Indexes)))
+ {
+ $Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index});
+ $Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index});
+ }
+ my $VTABLES = "";
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ $VTABLES .= " \n";
+ foreach my $Index (sort {$a<=>$b} (keys(%Entries)))
+ {
+ $VTABLES .= " \n";
+ $VTABLES .= " ".xmlSpecChars($Entries{$Index}{"E1"})."\n";
+ $VTABLES .= " ".xmlSpecChars($Entries{$Index}{"E2"})."\n";
+ $VTABLES .= " \n";
+ }
+ $VTABLES .= " \n\n";
+ }
+ else
+ { # HTML
+ $VTABLES .= "";
+ $VTABLES .= "| Offset | ";
+ $VTABLES .= "Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries | ";
+ $VTABLES .= "Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries |
";
+ foreach my $Index (sort {$a<=>$b} (keys(%Entries)))
+ {
+ my ($Color1, $Color2) = ("", "");
+
+ my $E1 = $Entries{$Index}{"E1"};
+ my $E2 = $Entries{$Index}{"E2"};
+
+ if($E1 ne $E2
+ and $E1!~/ 0x/
+ and $E2!~/ 0x/)
+ {
+ if($Entries{$Index}{"E1"})
+ {
+ $Color1 = " class='failed'";
+ $Color2 = " class='failed'";
+ }
+ else {
+ $Color2 = " class='warning'";
+ }
+ }
+ $VTABLES .= "| ".$Index." | \n";
+ $VTABLES .= "".specChars($Entries{$Index}{"E1"})." | \n";
+ $VTABLES .= "".specChars($Entries{$Index}{"E2"})." |
\n";
+ }
+ $VTABLES .= "
\n";
+ $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd;
+ $VTABLES = $ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."
\n".$VTABLES;
+ }
+ return $VTABLES;
+ }
+ }
+ return "";
+}
+
+sub simpleVEntry($)
+{
+ my $VEntry = $_[0];
+ if(not defined $VEntry
+ or $VEntry eq "") {
+ return "";
+ }
+
+ $VEntry=~s/ \[.+?\]\Z//; # support for ABI Dumper
+ $VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks
+ $VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo
+ if($VEntry=~/\A_ZThn.+\Z/) {
+ $VEntry = "non-virtual thunk";
+ }
+ $VEntry=~s/\A\(int \(\*\)\(...\)\)\s*([a-z_])/$1/i;
+ # support for old GCC versions
+ $VEntry=~s/\A0u\Z/(int (*)(...))0/;
+ $VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/;
+ $VEntry=~s/\A&_Z\Z/& _Z/;
+ $VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors
+ return $VEntry;
+}
+
+sub adjustParamPos($$$)
+{
+ my ($Pos, $Symbol, $LVer) = @_;
+ if(defined $CompSign{$LVer}{$Symbol})
+ {
+ if(not $CompSign{$LVer}{$Symbol}{"Static"}
+ and $CompSign{$LVer}{$Symbol}{"Class"})
+ {
+ return $Pos-1;
+ }
+
+ return $Pos;
+ }
+
+ return undef;
+}
+
+sub getParamPos($$$)
+{
+ my ($Name, $Symbol, $LVer) = @_;
+
+ if(defined $CompSign{$LVer}{$Symbol}
+ and defined $CompSign{$LVer}{$Symbol}{"Param"})
+ {
+ my $Info = $CompSign{$LVer}{$Symbol};
+ foreach (keys(%{$Info->{"Param"}}))
+ {
+ if($Info->{"Param"}{$_}{"name"} eq $Name)
+ {
+ return $_;
+ }
+ }
+ }
+
+ return undef;
+}
+
+sub getParamName($)
+{
+ my $Loc = $_[0];
+ $Loc=~s/\->.*//g;
+ return $Loc;
+}
+
+sub getAffectedSymbols($$$)
+{
+ my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
+
+ my $LIMIT = 10;
+ if(defined $In::Opt{"AffectLimit"}) {
+ $LIMIT = $In::Opt{"AffectLimit"};
+ }
+
+ my %SymSel = ();
+
+ foreach my $Kind (sort keys(%{$Kinds_Locations}))
+ {
+ my @Locs = sort {(index($a, "retval")!=-1) cmp (index($b, "retval")!=-1)} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}});
+
+ foreach my $Loc (@Locs)
+ {
+ foreach my $Symbol (keys(%{$TypeProblemsIndex{$Level}{$Target_TypeName}{$Kind}{$Loc}}))
+ {
+ if(index($Symbol, "_Z")==0
+ and $Symbol=~/(C4|C2|D4|D2|D0)[EI]/)
+ { # duplicated problems for C2/C4 constructors, D2/D4 and D0 destructors
+ next;
+ }
+
+ if(index($Symbol, "\@")!=-1
+ or index($Symbol, "\$")!=-1)
+ {
+ my ($SN, $SS, $SV) = symbolParts($Symbol);
+
+ if($Level eq "Source")
+ { # remove symbol version
+ $Symbol = $SN;
+ }
+
+ if($SV and defined $CompatProblems{$Level}{$SN})
+ { # duplicated problems for versioned symbols
+ next;
+ }
+ }
+
+ if(not defined $SymSel{$Symbol})
+ {
+ $SymSel{$Symbol}{"Kind"} = $Kind;
+ $SymSel{$Symbol}{"Loc"} = $Loc;
+ }
+ }
+ }
+ }
+
+ my $Affected = "";
+ my $SNum = 0;
+
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ $Affected .= " \n";
+
+ foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
+ {
+ my $Kind = $SymSel{$Symbol}{"Kind"};
+ my $Loc = $SymSel{$Symbol}{"Loc"};
+
+ my $PName = getParamName($Loc);
+ my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc);
+
+ my $Target = "";
+ if($PName)
+ {
+ $Target .= " param=\"$PName\"";
+ $Des=~s/parameter $PName /parameter \@param /;
+ }
+ elsif($Loc=~/\Aretval(\-|\Z)/i) {
+ $Target .= " affected=\"retval\"";
+ }
+ elsif($Loc=~/\Athis(\-|\Z)/i) {
+ $Target .= " affected=\"this\"";
+ }
+
+ if($Des=~s/\AField ([^\s]+) /Field \@field /) {
+ $Target .= " field=\"$1\"";
+ }
+
+ $Affected .= " \n";
+ $Affected .= " ".xmlSpecChars($Des)."\n";
+ $Affected .= " \n";
+
+ if(++$SNum>=$LIMIT) {
+ last;
+ }
+ }
+ $Affected .= " \n";
+ }
+ else
+ { # HTML
+ foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
+ {
+ my $Kind = $SymSel{$Symbol}{"Kind"};
+ my $Loc = $SymSel{$Symbol}{"Loc"};
+
+ my $Des = getAffectDesc($Level, $Symbol, $Kind, $Loc);
+ my $PName = getParamName($Loc);
+ my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1);
+
+ $Affected .= "".getSignature($Symbol, 1, "Class|Name|Param|HTML|Italic|Target=".$Pos)."
\n";
+ $Affected .= "".specChars($Des)."
\n";
+
+ if(++$SNum>=$LIMIT) {
+ last;
+ }
+ }
+
+ my $Total = keys(%SymSel);
+
+ if($Total>$LIMIT) {
+ $Affected .= " ...\n
\n"; # and others ...
+ }
+
+ $Affected = "".$Affected."
\n";
+ if($Affected)
+ {
+
+ my $Per = showNum($Total*100/keys(%{$CheckedSymbols{$Level}}));
+ $Affected = $ContentDivStart.$Affected.$ContentDivEnd;
+ $Affected = $ContentSpanStart_Affected."[+] affected symbols: $Total ($Per\%)".$ContentSpanEnd.$Affected;
+ }
+ }
+
+ return $Affected;
+}
+
+sub cmpLocations($$)
+{
+ my ($L1, $L2) = @_;
+ if((index($L2, "retval")==0 or index($L2, "this")==0)
+ and (index($L1, "retval")!=0 and index($L1, "this")!=0))
+ {
+ if(index($L1, "->")==-1) {
+ return 1;
+ }
+ elsif(index($L2, "->")!=-1) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub getAffectDesc($$$$)
+{
+ my ($Level, $Symbol, $Kind, $Loc) = @_;
+
+ if($ExtendedSymbols{$Symbol}) {
+ return "This is a symbol from an external library that may use subject library and change the ABI after recompiling.";
+ }
+
+ my $PAttr = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc};
+
+ $Loc=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field
+
+ my @Sentence = ();
+
+ if($Kind eq "Overridden_Virtual_Method"
+ or $Kind eq "Overridden_Virtual_Method_B") {
+ push(@Sentence, "The method '".$PAttr->{"New_Value"}."' will be called instead of this method.");
+ }
+ elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
+ {
+ my %SymInfo = %{$CompSign{1}{$Symbol}};
+
+ if($Loc eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/)
+ {
+ my $MType = "method";
+ if($SymInfo{"Constructor"}) {
+ $MType = "constructor";
+ }
+ elsif($SymInfo{"Destructor"}) {
+ $MType = "destructor";
+ }
+
+ my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"};
+
+ if($ClassName eq $PAttr->{"Type_Name"}) {
+ push(@Sentence, "This $MType is from \'".$PAttr->{"Type_Name"}."\' class.");
+ }
+ else {
+ push(@Sentence, "This $MType is from derived class \'".$ClassName."\'.");
+ }
+ }
+ else
+ {
+ my $TypeID = undef;
+
+ if($Loc=~/retval/)
+ { # return value
+ if(index($Loc, "->")!=-1) {
+ push(@Sentence, "Field \'".$Loc."\' in the return value");
+ }
+ else {
+ push(@Sentence, "Return value");
+ }
+
+ $TypeID = $SymInfo{"Return"};
+ }
+ elsif($Loc=~/this/)
+ { # "this" pointer
+ if(index($Loc, "->")!=-1) {
+ push(@Sentence, "Field \'".$Loc."\' in the object of this method");
+ }
+ else {
+ push(@Sentence, "\'this\' pointer");
+ }
+
+ $TypeID = $SymInfo{"Class"};
+ }
+ else
+ { # parameters
+ my $PName = getParamName($Loc);
+ my $PPos = getParamPos($PName, $Symbol, 1);
+
+ if(index($Loc, "->")!=-1) {
+ push(@Sentence, "Field \'".$Loc."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
+ }
+ else {
+ push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
+ }
+ if($PName) {
+ push(@Sentence, "\'".$PName."\'");
+ }
+
+ $TypeID = $SymInfo{"Param"}{$PPos}{"type"};
+ }
+
+ if($Loc!~/this/)
+ {
+ if(my %PureType = getPureType($TypeID, 1))
+ {
+ if($PureType{"Type"} eq "Pointer") {
+ push(@Sentence, "(pointer)");
+ }
+ elsif($PureType{"Type"} eq "Ref") {
+ push(@Sentence, "(reference)");
+ }
+ }
+ }
+
+ if($Loc eq "this") {
+ push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'.");
+ }
+ else
+ {
+ my $Loc_T = $Loc;
+ $Loc_T=~s/\A\w+(\->|\Z)//; # location in type
+
+ my $TypeID_Problem = $TypeID;
+ if($Loc_T) {
+ $TypeID_Problem = getFieldType($Loc_T, $TypeID, 1);
+ }
+
+ if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $PAttr->{"Type_Name"}) {
+ push(@Sentence, "is of type \'".$PAttr->{"Type_Name"}."\'.");
+ }
+ else {
+ push(@Sentence, "has base type \'".$PAttr->{"Type_Name"}."\'.");
+ }
+ }
+ }
+ }
+
+ my $Sent = join(" ", @Sentence);
+
+ $Sent=~s/->/./g;
+
+ if($In::Opt{"ReportFormat"} eq "xml") {
+ $Sent=~s/'//g;
+ }
+
+ return $Sent;
+}
+
+sub getFieldType($$$)
+{
+ my ($Loc, $TypeId, $LVer) = @_;
+
+ my @Fields = split(/\->/, $Loc);
+
+ foreach my $Name (@Fields)
+ {
+ my %Info = getBaseType($TypeId, $LVer);
+
+ foreach my $Pos (keys(%{$Info{"Memb"}}))
+ {
+ if($Info{"Memb"}{$Pos}{"name"} eq $Name)
+ {
+ $TypeId = $Info{"Memb"}{$Pos}{"type"};
+ last;
+ }
+ }
+ }
+
+ return $TypeId;
+}
+
+sub writeReport($$)
+{
+ my ($Level, $Report) = @_;
+ if($In::Opt{"ReportFormat"} eq "xml") {
+ $Report = "\n".$Report;
+ }
+ if($In::Opt{"StdOut"})
+ { # --stdout option
+ print STDOUT $Report;
+ }
+ else
+ {
+ my $RPath = getReportPath($Level);
+ mkpath(getDirname($RPath));
+
+ open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n");
+ print REPORT $Report;
+ close(REPORT);
+ }
+}
+
+sub getReport($)
+{
+ my $Level = $_[0];
+ if($In::Opt{"ReportFormat"} eq "xml")
+ { # XML
+ if($Level eq "Join")
+ {
+ my $Report = "\n";
+ $Report .= getReport("Binary");
+ $Report .= getReport("Source");
+ $Report .= "\n";
+ return $Report;
+ }
+ else
+ {
+ my $Report = "\n\n";
+ my ($Summary, $MetaData, $AnyChanged) = getSummary($Level);
+ $Report .= $Summary."\n";
+ $Report .= getReportProblems_All($Level);
+ $Report .= "\n";
+ return $Report;
+ }
+ }
+ else
+ { # HTML
+ my $CssStyles = readModule("Styles", "Report.css");
+ my $JScripts = readModule("Scripts", "Sections.js");
+ if($Level eq "Join")
+ {
+ $CssStyles .= "\n".readModule("Styles", "Tabs.css");
+ $JScripts .= "\n".readModule("Scripts", "Tabs.js");
+ my $Title = $In::Opt{"TargetTitle"}.": ".$In::Desc{1}{"Version"}." to ".$In::Desc{2}{"Version"}." compatibility report";
+ my $Keywords = $In::Opt{"TargetTitle"}.", compatibility, API, ABI, report";
+ my $Des = "API/ABI compatibility report for the ".$In::Opt{"TargetTitle"}." ".$In::Opt{"TargetComponent"}." between ".$In::Desc{1}{"Version"}." and ".$In::Desc{2}{"Version"}." versions";
+ my ($BSummary, $BMetaData, $BAnyChanged) = getSummary("Binary");
+ my ($SSummary, $SMetaData, $SAnyChanged) = getSummary("Source");
+ my $Report = "\n\n".composeHTML_Head($Title, $Keywords, $Des, $CssStyles, $JScripts, ($BAnyChanged or $SAnyChanged))."";
+ $Report .= getReportTitle("Join")."
+
+ ";
+ $Report .= "\n$BSummary\n".getReportProblems_All("Binary").getSourceInfo()."
";
+ $Report .= "\n$SSummary\n".getReportProblems_All("Source").getSourceInfo()."
";
+ $Report .= getReportFooter();
+ $Report .= "\n