<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[284419] trunk/Tools</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/284419">284419</a></dd>
<dt>Author</dt> <dd>jbedard@apple.com</dd>
<dt>Date</dt> <dd>2021-10-18 17:48:12 -0700 (Mon, 18 Oct 2021)</dd>
</dl>
<h3>Log Message</h3>
<pre>Unreviewed, reverting <a href="http://trac.webkit.org/projects/webkit/changeset/284399">r284399</a>.
Breaks commit message generation
Reverted changeset:
"[webkitperl] Stop using LoadAsModule.pm"
https://bugs.webkit.org/show_bug.cgi?id=231868
https://commits.webkit.org/<a href="http://trac.webkit.org/projects/webkit/changeset/284399">r284399</a></pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsScriptsfilterbuildwebkit">trunk/Tools/Scripts/filter-build-webkit</a></li>
<li><a href="#trunkToolsScriptsprepareChangeLog">trunk/Tools/Scripts/prepare-ChangeLog</a></li>
<li><a href="#trunkToolsScriptsrunleaks">trunk/Tools/Scripts/run-leaks</a></li>
<li><a href="#trunkToolsScriptswebkitdirspm">trunk/Tools/Scripts/webkitdirs.pm</a></li>
<li><a href="#trunkToolsScriptswebkitperlfilterbuildwebkit_unittestshouldIgnoreLine_unittestspl">trunk/Tools/Scripts/webkitperl/filter-build-webkit_unittest/shouldIgnoreLine_unittests.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlprepareChangeLog_unittestextractLineRangeBeforeAndAfterChangepl">trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlprepareChangeLog_unittestfetchRadarURLFromBugXMLDatapl">trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/fetchRadarURLFromBugXMLData.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlprepareChangeLog_unittestfilenameWithParenthesespl">trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/filenameWithParentheses.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlprepareChangeLog_unittestgenerateFunctionListspl">trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/generateFunctionLists.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlprepareChangeLog_unittestparser_unittestspl">trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/parser_unittests.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlrunleaks_unittestrunleaksreportv10pl">trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v1.0.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlrunleaks_unittestrunleaksreportv20newpl">trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-new.pl</a></li>
<li><a href="#trunkToolsScriptswebkitperlrunleaks_unittestrunleaksreportv20oldpl">trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-old.pl</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkToolsScriptswebkitperlLoadAsModulepm">trunk/Tools/Scripts/webkitperl/LoadAsModule.pm</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li>trunk/Tools/Scripts/webkitperl/build/</li>
<li><a href="#trunkToolsScriptswebkitperlchangelogpm">trunk/Tools/Scripts/webkitperl/changelog.pm</a></li>
<li><a href="#trunkToolsScriptswebkitperldirspm">trunk/Tools/Scripts/webkitperl/dirs.pm</a></li>
<li><a href="#trunkToolsScriptswebkitperlleakspm">trunk/Tools/Scripts/webkitperl/leaks.pm</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/ChangeLog 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2021-10-18 Jonathan Bedard <jbedard@apple.com>
+
+ Unreviewed, reverting r284399.
+
+ Breaks commit message generation
+
+ Reverted changeset:
+
+ "[webkitperl] Stop using LoadAsModule.pm"
+ https://bugs.webkit.org/show_bug.cgi?id=231868
+ https://commits.webkit.org/r284399
+
</ins><span class="cx"> 2021-10-18 Alex Christensen <achristensen@webkit.org>
</span><span class="cx">
</span><span class="cx"> Remove TCPServer
</span></span></pre></div>
<a id="trunkToolsScriptsfilterbuildwebkit"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/filter-build-webkit (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/filter-build-webkit 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/filter-build-webkit 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -33,7 +33,6 @@
</span><span class="cx"> use lib $FindBin::Bin;
</span><span class="cx"> use Getopt::Long;
</span><span class="cx"> use VCSUtils;
</span><del>-use webkitperl::build::output qw(shouldIgnoreLine $platform);
</del><span class="cx">
</span><span class="cx"> use constant {
</span><span class="cx"> STYLE_PLAIN => 0,
</span><span class="lines">@@ -67,6 +66,7 @@
</span><span class="cx"> sub printLine($$);
</span><span class="cx"> sub setLogfileOption($$);
</span><span class="cx"> sub setOutputFormatOption($$);
</span><ins>+sub shouldIgnoreLine($$);
</ins><span class="cx"> sub usageAndExit();
</span><span class="cx">
</span><span class="cx"> # Defined in VCSUtils.
</span><span class="lines">@@ -74,6 +74,7 @@
</span><span class="cx">
</span><span class="cx"> # Global variables used only in global scope.
</span><span class="cx"> my $outputPath = "&STDOUT";
</span><ins>+my $platform = "mac";
</ins><span class="cx"> my $showHelp;
</span><span class="cx">
</span><span class="cx"> # Global variables used in global and subroutine scope.
</span><span class="lines">@@ -81,6 +82,8 @@
</span><span class="cx"> our $outputFormat = "text";
</span><span class="cx"> our $unfilteredOutputPath = "build.log";
</span><span class="cx"> our $useColor = -t STDOUT;
</span><ins>+our $inEntitlements = 0;
+our $inDevicePreparationWarnings = 0;
</ins><span class="cx">
</span><span class="cx"> sub usageAndExit()
</span><span class="cx"> {
</span><span class="lines">@@ -273,3 +276,127 @@
</span><span class="cx"> }
</span><span class="cx"> $outputFormat = $value;
</span><span class="cx"> }
</span><ins>+
+sub shouldShowSubsequentLine($)
+{
+ my ($line) = @_;
+
+ return 1 if $line =~ /referenced from:$/;
+ return 1 if $line =~ /(note:|error:)/;
+
+ return 0;
+}
+
+sub shouldIgnoreLine($$)
+{
+ my ($previousLine, $line) = @_;
+
+ if ($line =~ /^Entitlements:$/) {
+ $inEntitlements = 1;
+ return 1
+ }
+
+ if ($inEntitlements) {
+ $inEntitlements = 0 if $line =~ /^}$/;
+ return 1
+ }
+
+ # iPhone preparation errors always start and end with lines containing 'iPhoneConnect:'.
+ if ($inDevicePreparationWarnings) {
+ $inDevicePreparationWarnings = 0 if $line =~ /== END: Underlying device preparation warnings ==/;
+ return 1
+ }
+
+ if ($line =~ /iPhoneConnect:/) {
+ $inDevicePreparationWarnings = 1;
+ return 1
+ }
+
+ return 1 if $line =~ /^\s*$/;
+ return 1 if $line =~ /^Command line invocation:/;
+ return 1 if $line =~ /^Build settings from command line:/;
+ return 1 if $line =~ /^User defaults from command line:/;
+ return 1 if $line =~ /^Prepare build/;
+ return 1 if $line =~ /^Build system information/;
+ return 1 if $line =~ /^note: Planning build/;
+ return 1 if $line =~ /^note: Constructing build description/;
+ return 1 if $line =~ /^note: Build description (constructed|loaded) in .*/;
+ return 1 if $line =~ /^note: Using build description .*/;
+ return 1 if $line =~ /^note: Using eager compilation/;
+ return 1 if $line =~ /^note: Execution policy exception registration failed and was skipped: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"/;
+ return 1 if $line =~ /^note: detected encoding of input file as Unicode \(.*\)/;
+ return 1 if $line =~ /make(\[\d+\])?: Nothing to be done for/;
+ return 1 if $line =~ /^JavaScriptCore\/create_hash_table/;
+ return 1 if $line =~ /JavaScriptCore.framework\/PrivateHeaders\/create_hash_table/;
+ return 1 if $line =~ /^JavaScriptCore\/pcre\/dftables/;
+ return 1 if $line =~ /^Creating hashtable for /;
+ return 1 if $line =~ /^Wrote output to /;
+ return 1 if $line =~ /^UNDOCUMENTED: /;
+ return 1 if $line =~ /libtool.*has no symbols/;
+ return 1 if $line =~ /^# Lower case all the values, as CSS values are case-insensitive$/;
+ return 1 if $line =~ /^if sort /;
+ return 1 if $line =~ /set-webkit-configuration/;
+ return 1 if $line =~ /^building file list/;
+ return 1 if $line =~ /^\.\/$/;
+ return 1 if $line =~ /^\S+\.h$/;
+ return 1 if $line =~ /^\S+\/$/;
+ return 1 if $line =~ /^sent \d+ bytes/;
+ return 1 if $line =~ /^total size is/;
+ return 1 if $line =~ /One of the two will be used\. Which one is undefined\./;
+ return 1 if $line =~ /The Legacy Build System will be removed in a future release/;
+ return 1 if $line =~ /^\( (xcodebuild|if) /;
+ return 1 if $line =~ /^warning: can't find additional SDK/;
+ return 1 if $line =~ /^warning: no umbrella header found for target '.*', module map will not be generated$/;
+ return 1 if $line =~ /^warning\: detected internal install, passing entitlements to simulator anyway\./;
+ return 1 if $line =~ /may not function in the Simulator because Ad Hoc/;
+ return 1 if $line =~ /\/usr\/bin\/clang .*? \> \S+.sb/;
+ return 1 if $line =~ / xcodebuild\[[0-9]+:[0-9a-f]+\]\s+DVTAssertions: Warning in .*/;
+ return 1 if $line =~ /^(Details|Object|Method|Function|Thread):/;
+ return 1 if $line =~ /^Please file a bug at /;
+ return 1 if $line =~ /created by an unsupported XCDependencyGraph build$/;
+ return 1 if $line =~ /warning: The assignment of '.*' at ".*" uses \$\(inherited\). In the new build system this will inherit from an earlier definition of '.*' in this xcconfig file or its imports; the old build system would discard earlier definitions. This may result in changes to resolved build setting values./;
+ return 1 if $line =~ /.* com.apple.actool.compilation-results .*/;
+ return 1 if $line =~ /.*\/Assets.car/;
+ return 1 if $line =~ /.*\/assetcatalog_generated_info.plist/;
+ return 1 if $line =~ /^mount: .+ failed with/;
+ return 1 if $line =~ /^Using .+ production environment.$/;
+ return 1 if $line =~ /replacing existing signature$/;
+ return 1 if $line =~ /^Unlocking '.*\.keychain-db'$/;
+ return 1 if $line =~ /^\d+ localizable strings$/;
+ return 1 if $line =~ /^\d+ plural rules$/;
+ return 1 if $line =~ /^The list of exported symbols did not change.$/;
+ return 1 if $line =~ /^ditto: Cannot get the real path for source/;
+ return 1 if $line =~ /^Duplicate Entry Was Skipped:/;
+ return 1 if $line =~ /^Adding .*?entitlements/;
+ return 1 if $line =~ /^Making app bundle launchable/;
+ return 1 if $line =~ /^Finished adding entitlements\.$/;
+ return 1 if $line =~ /^.* will not be code signed because its settings don't specify a development team.$/;
+
+ if ($platform eq "win") {
+ return 1 if $line =~ /^\s*(touch|perl|cat|rm -f|del|python|\/usr\/bin\/g\+\+|gperf|echo|sed|if \[ \-f|WebCore\/generate-export-file) /;
+ return 1 if $line =~ /^\s*(if not exist \"|if errorlevel 1)/;
+ return 1 if $line =~ /(^\s*|MSB3073:\s+)(set |REM |cmd \/c)/;
+ return 1 if $line =~ /^\s*[cC]:\\[pP]rogram [fF]iles.*\\.*\\(CL|midl)\.exe /;
+ return 1 if $line =~ /^\s*Processing .*\.(acf|h|idl)\s*$/;
+ return 1 if $line =~ /^\s*printf /;
+ return 1 if $line =~ /^\s*\/usr\/bin\/bash\s*/;
+ return 1 if $line =~ /^\s*offlineasm: Nothing changed/;
+ return 1 if $line =~ / \d+ File\(s\) copied/;
+ return 1 if $line =~ /^\s*File not found - \*\.h/;
+ return 1 if $line =~ /mkdir\s+\"/;
+ return 1 if $line =~ /xcopy \/y \/d \"/;
+ return 1 if $line =~ /\.obj\"\s*$/;
+ return 1 if $line =~ /:\s+(cmd \/c|set)\s+/;
+ return 1 if $line =~ /MSB3073:\s+$/;
+ return 1 if $line =~ /MSB3073:\s+if not exist/;
+ return 1 if $line =~ /which.exe bash/;
+ } else {
+ return 1 if $line =~ /^(touch|perl|cat|rm -f|python|\/usr\/bin\/g\+\+|\/bin\/ln|gperf|echo|sed|if \[ \-f|WebCore\/generate-export-file|write-file|chmod) /;
+ return 1 if $line =~ /^ / && !shouldShowSubsequentLine($previousLine);
+ return 1 if $line =~ /^printf /;
+ return 1 if $line =~ /^offlineasm: Nothing changed/;
+ }
+ return 1 if $line =~ /^Showing first/;
+
+ return 0;
+}
</ins></span></pre></div>
<a id="trunkToolsScriptsprepareChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/prepare-ChangeLog (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/prepare-ChangeLog 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/prepare-ChangeLog 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -63,16 +63,23 @@
</span><span class="cx"> use List::Util qw/max/;
</span><span class="cx"> use POSIX qw(strftime);
</span><span class="cx"> use VCSUtils;
</span><del>-use webkitperl::changelog;
</del><span class="cx">
</span><ins>+sub actuallyGenerateFunctionLists($$$$$$);
+sub attributeCommand($$$);
</ins><span class="cx"> sub changeLogDate($);
</span><span class="cx"> sub changeLogEmailAddressFromArgs($$);
</span><span class="cx"> sub changeLogNameFromArgs($$);
</span><ins>+sub computeModifiedFunctions($$$);
</ins><span class="cx"> sub createPatchCommand($$$$);
</span><ins>+sub decodeEntities($);
</ins><span class="cx"> sub determinePropertyChanges($$$);
</span><span class="cx"> sub diffCommand($$$$);
</span><span class="cx"> sub diffFromToString($$$);
</span><ins>+sub extractLineRangeAfterChange($);
+sub extractLineRangeBeforeChange($);
</ins><span class="cx"> sub fetchBugXMLData($$);
</span><ins>+sub fetchBugDescriptionFromBugXMLData($$$);
+sub fetchRadarURLFromBugXMLData($$);
</ins><span class="cx"> sub findChangeLogs($$);
</span><span class="cx"> sub findOriginalFileFromSvn($);
</span><span class="cx"> sub generateFileList(\%$$$\%);
</span><span class="lines">@@ -79,6 +86,7 @@
</span><span class="cx"> sub generateFunctionLists($$$$$);
</span><span class="cx"> sub generateNewChangeLogs($$$$$$$$$$$$$$$);
</span><span class="cx"> sub getLatestChangeLogs($);
</span><ins>+sub get_function_line_ranges($$);
</ins><span class="cx"> sub get_function_line_ranges_for_cpp($$);
</span><span class="cx"> sub delete_namespaces_from_ranges_for_cpp(\@\@);
</span><span class="cx"> sub is_function_in_namespace($$);
</span><span class="lines">@@ -87,6 +95,7 @@
</span><span class="cx"> sub get_function_line_ranges_for_perl($$);
</span><span class="cx"> sub get_selector_line_ranges_for_css($$);
</span><span class="cx"> sub get_function_line_ranges_for_swift($$);
</span><ins>+sub parseSwiftFunctionArgs($);
</ins><span class="cx"> sub isAddedStatus($);
</span><span class="cx"> sub isConflictStatus($$$);
</span><span class="cx"> sub isModifiedStatus($);
</span><span class="lines">@@ -304,6 +313,136 @@
</span><span class="cx"> actuallyGenerateFunctionLists($changedFiles, $functionLists, $gitCommit, $gitIndex, $mergeBase, \%delegateHash);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+sub actuallyGenerateFunctionLists($$$$$$)
+{
+ my ($changedFiles, $functionLists, $gitCommit, $gitIndex, $mergeBase, $delegateHashRef) = @_;
+
+ my %line_ranges_after_changed;
+ my %line_ranges_before_changed;
+ if (@$changedFiles) {
+ # For each file, build a list of modified lines.
+ # Use line numbers from the "after" side of each diff.
+ print STDERR " Reviewing diff to determine which lines changed.\n";
+ my $file;
+ my $diffFileHandle = $delegateHashRef->{openDiff}($changedFiles, $gitCommit, $gitIndex, $mergeBase);
+ if (!$diffFileHandle) {
+ die "The diff failed: $!.\n";
+ }
+ while (<$diffFileHandle>) {
+ my $filePath = parseDiffStartLine($_);
+ $file = $delegateHashRef->{normalizePath}($filePath) if $filePath;
+ if (defined $file) {
+ my ($before_start, $before_end) = extractLineRangeBeforeChange($_);
+ if ($before_start >= 1 && $before_end >= 1) {
+ push @{$line_ranges_before_changed{$file}}, [ $before_start, $before_end ];
+ } elsif (/DO_NOT_COMMIT/) {
+ print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n";
+ }
+ my ($after_start, $after_end) = extractLineRangeAfterChange($_);
+ if ($after_start >= 1 && $after_end >= 1) {
+ push @{$line_ranges_after_changed{$file}}, [ $after_start, $after_end ];
+ } elsif (/DO_NOT_COMMIT/) {
+ print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n";
+ }
+ }
+ }
+ close($diffFileHandle);
+ }
+
+ # For each source file, convert line range to function list.
+ print STDERR " Extracting affected function names from source files.\n";
+ my %filesToExamine = map { $_ => 1 } (keys(%line_ranges_before_changed), keys(%line_ranges_after_changed));
+ foreach my $file (keys %filesToExamine) {
+ my %saw_function;
+
+ # Find all the functions in the file.
+ my $sourceFileHandle = $delegateHashRef->{openFile}($file);
+ next unless $sourceFileHandle;
+ my @afterChangeFunctionRanges = get_function_line_ranges($sourceFileHandle, $file);
+ close($sourceFileHandle);
+
+ # Find modified functions in the file.
+ if ($line_ranges_after_changed{$file}) {
+ my @change_ranges = (@{$line_ranges_after_changed{$file}}, []);
+ my @functions = computeModifiedFunctions($file, \@change_ranges, \@afterChangeFunctionRanges);
+
+ # Format the list of functions.
+ if (@functions) {
+ $functionLists->{$file} = "" if !defined $functionLists->{$file};
+ $functionLists->{$file} .= "\n (" . join("):\n (", @functions) . "):";
+ }
+ }
+ # Find the deleted functions in the original file.
+ if ($line_ranges_before_changed{$file}) {
+ my $originalFileHandle = $delegateHashRef->{openOriginalFile}($file, $gitCommit, $gitIndex, $mergeBase);
+ next unless $originalFileHandle;
+ my @beforeChangeFunctionRanges = get_function_line_ranges($originalFileHandle, $file);
+ close($originalFileHandle);
+
+ my %existsAfterChange = map { $_->[2] => 1 } @afterChangeFunctionRanges;
+
+ my @functions;
+ my %sawFunctions;
+ for my $functionRange (@beforeChangeFunctionRanges) {
+ my $functionName = $functionRange->[2];
+ if (!$existsAfterChange{$functionName} && !$sawFunctions{$functionName}) {
+ push @functions, $functionName;
+ $sawFunctions{$functionName} = 1;
+ }
+ }
+
+ # Format the list of deleted functions.
+ if (@functions) {
+ $functionLists->{$file} = "" if !defined $functionLists->{$file};
+ $functionLists->{$file} .= "\n (" . join("): Deleted.\n (", @functions) . "): Deleted.";
+ }
+ }
+ }
+}
+
+sub computeModifiedFunctions($$$)
+{
+ my ($file, $changedLineRanges, $functionRanges) = @_;
+
+ my %sawFunction;
+
+ # Find all the modified functions.
+ my @functions;
+ my @change_ranges = @{$changedLineRanges};
+ my @change_range = (0, 0);
+ FUNCTION: foreach my $function_range_ref (@{$functionRanges}) {
+ my @function_range = @{$function_range_ref};
+
+ # FIXME: This is a hack. If the function name is empty, skip it.
+ # The cpp, python, javascript, perl, css and java parsers
+ # are not perfectly implemented and sometimes function names cannot be retrieved
+ # correctly. As you can see in get_function_line_ranges_XXXX(), those parsers
+ # are not intended to implement real parsers but intended to just retrieve function names
+ # for most practical syntaxes.
+ next unless $function_range[2];
+
+ # Advance to successive change ranges.
+ for (;; @change_range = @{shift @change_ranges}) {
+ last FUNCTION unless @change_range;
+
+ # If past this function, move on to the next one.
+ next FUNCTION if $change_range[0] > $function_range[1];
+
+ # If an overlap with this function range, record the function name.
+ if ($change_range[1] >= $function_range[0]
+ and $change_range[0] <= $function_range[1]) {
+ if (!$sawFunction{$function_range[2]}) {
+ $sawFunction{$function_range[2]} = 1;
+ push @functions, $function_range[2];
+ }
+ next FUNCTION;
+ }
+ }
+ }
+
+ return @functions;
+}
+
</ins><span class="cx"> sub changeLogDate($)
</span><span class="cx"> {
</span><span class="cx"> my ($timeZone) = @_;
</span><span class="lines">@@ -361,6 +500,32 @@
</span><span class="cx"> return $xmlData;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+sub fetchBugDescriptionFromBugXMLData($$$)
+{
+ my ($bugURL, $bugNumber, $bugXMLData) = @_;
+
+ if ($bugXMLData !~ /<short_desc>(.*)<\/short_desc>/) {
+ print STDERR " Bug $bugNumber has no bug description. Maybe you set wrong bug ID?\n";
+ print STDERR " The bug URL: $bugURL\n";
+ exit 1;
+ }
+
+ my $bugDescription = decodeEntities($1);
+ print STDERR " Description from bug $bugNumber:\n \"$bugDescription\".\n";
+ return $bugDescription;
+}
+
+sub fetchRadarURLFromBugXMLData($$)
+{
+ my ($bugNumber, $bugXMLData) = @_;
+
+ return "" if $bugXMLData !~ m|<thetext>\s*((<)?rdar://(problem/)?\d+(>)?)|;
+
+ my $bugRadarURL = decodeEntities($1);
+ print STDERR " Radar URL from bug $bugNumber:\n \"$bugRadarURL\".\n";
+ return $bugRadarURL;
+}
+
</ins><span class="cx"> sub findChangeLogs($$)
</span><span class="cx"> {
</span><span class="cx"> my ($functionLists, $requireChangeLogToExist) = @_;
</span><span class="lines">@@ -596,6 +761,35 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+sub get_function_line_ranges($$)
+{
+ my ($file_handle, $file_name) = @_;
+
+ # Try to determine the source language based on the file extension.
+
+ return get_function_line_ranges_for_cpp($file_handle, $file_name) if $file_name =~ /\.(c|cpp|m|mm|h)$/;
+ return get_function_line_ranges_for_java($file_handle, $file_name) if $file_name =~ /\.java$/;
+ return get_function_line_ranges_for_javascript($file_handle, $file_name) if $file_name =~ /\.js$/;
+ return get_selector_line_ranges_for_css($file_handle, $file_name) if $file_name =~ /\.css$/;
+ return get_function_line_ranges_for_perl($file_handle, $file_name) if $file_name =~ /\.p[lm]$/;
+ return get_function_line_ranges_for_python($file_handle, $file_name) if $file_name =~ /\.py$/ or $file_name =~ /master\.cfg$/;
+ return get_function_line_ranges_for_swift($file_handle, $file_name) if $file_name =~ /\.swift$/;
+
+ # Try to determine the source language based on the script interpreter.
+
+ my $first_line = <$file_handle>;
+ seek($file_handle, 0, 0);
+
+ return () unless $first_line =~ m|^#!(?:/usr/bin/env\s+)?(\S+)|;
+ my $interpreter = $1;
+
+ return get_function_line_ranges_for_perl($file_handle, $file_name) if $interpreter =~ /perl$/;
+ return get_function_line_ranges_for_python($file_handle, $file_name) if $interpreter =~ /python$/;
+
+ return ();
+}
+
+
</ins><span class="cx"> sub method_decl_to_selector($)
</span><span class="cx"> {
</span><span class="cx"> (my $method_decl) = @_;
</span><span class="lines">@@ -623,6 +817,1222 @@
</span><span class="cx"> return $_;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+
+
+# Read a file and get all the line ranges of the things that look like C functions.
+# A function name is the last word before an open parenthesis before the outer
+# level open brace. A function starts at the first character after the last close
+# brace or semicolon before the function name and ends at the close brace.
+# Comment handling is simple-minded but will work for all but pathological cases.
+#
+# Result is a list of triples: [ start_line, end_line, function_name ].
+
+sub get_function_line_ranges_for_cpp($$)
+{
+ my ($file_handle, $file_name) = @_;
+
+ my @ranges;
+
+ my $in_comment = 0;
+ my $in_macro = 0;
+ my $in_method_declaration = 0;
+ my $in_parentheses = 0;
+ my $quotation_mark;
+ my $in_braces = 0;
+ my $in_toplevel_array_brace = 0;
+ my $brace_start = 0;
+ my $brace_end = 0;
+ my $namespace_start = -1;
+ my $skip_til_brace_or_semicolon = 0;
+ my $equal_observed = 0;
+
+ my $word = "";
+ my $interface_name = "";
+
+ my $potential_method_char = "";
+ my $potential_method_spec = "";
+
+ my $potential_start = 0;
+ my $potential_name = "";
+
+ my $start = 0;
+ my $name = "";
+
+ my $next_word_could_be_namespace = 0;
+ my $potential_namespace = "";
+ my @namespaces;
+ my @all_namespaces;
+
+ while (<$file_handle>) {
+ # Handle continued quoted string.
+ if ($quotation_mark) {
+ if (!s-([^\\]|\\.)*$quotation_mark--) {
+ if (!m-\\$-) {
+ warn "mismatched quotes at line $. in $file_name\n";
+ undef $quotation_mark;
+ }
+ next;
+ }
+ undef $quotation_mark;
+ }
+
+ # Handle continued multi-line comment.
+ if ($in_comment) {
+ next unless s-.*\*/--;
+ $in_comment = 0;
+ }
+
+ # Handle continued macro.
+ if ($in_macro) {
+ $in_macro = 0 unless /\\$/;
+ next;
+ }
+
+ # Handle start of macro (or any preprocessor directive).
+ if (/^\s*\#/) {
+ $in_macro = 1 if /^([^\\]|\\.)*\\$/;
+ next;
+ }
+
+ # Handle comments and quoted text.
+ while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy
+ my $match = $1;
+ if ($match eq "/*") {
+ if (!s-/\*.*?\*/--) {
+ s-/\*.*--;
+ $in_comment = 1;
+ }
+ } elsif ($match eq "//") {
+ s-//.*--;
+ } else { # ' or "
+ if (!s-$match([^\\]|\\.)*?$match--) {
+ if (!s-$match.*\\$--) {
+ warn "mismatched quotes at line $. in $file_name\n";
+ s-$match.*--;
+ } else {
+ $quotation_mark = $match;
+ }
+ }
+ }
+ }
+
+
+ # continued method declaration
+ if ($in_method_declaration) {
+ my $original = $_;
+ my $method_cont = $_;
+
+ chomp $method_cont;
+ $method_cont =~ s/[;\{].*//;
+ $potential_method_spec = "${potential_method_spec} ${method_cont}";
+
+ $_ = $original;
+ if (/;/) {
+ $potential_start = 0;
+ $potential_method_spec = "";
+ $potential_method_char = "";
+ $in_method_declaration = 0;
+ s/^[^;\{]*//;
+ } elsif (/{/) {
+ my $selector = method_decl_to_selector ($potential_method_spec);
+ $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]";
+
+ $potential_method_spec = "";
+ $potential_method_char = "";
+ $in_method_declaration = 0;
+
+ $_ = $original;
+ s/^[^;{]*//;
+ } elsif (/\@end/) {
+ $in_method_declaration = 0;
+ $interface_name = "";
+ $_ = $original;
+ } else {
+ next;
+ }
+ }
+
+
+ # start of method declaration
+ if ((my $method_char, my $method_spec) = m&^([-+])([^0-9;][^;]*);?$&) {
+ my $original = $_;
+
+ if ($interface_name) {
+ chomp $method_spec;
+ $method_spec =~ s/\{.*//;
+
+ $potential_method_char = $method_char;
+ $potential_method_spec = $method_spec;
+ $potential_start = $.;
+ $in_method_declaration = 1;
+ } else {
+ warn "declaring a method but don't have interface on line $. in $file_name\n";
+ }
+ $_ = $original;
+ if (/\{/) {
+ my $selector = method_decl_to_selector ($potential_method_spec);
+ $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]";
+
+ $potential_method_spec = "";
+ $potential_method_char = "";
+ $in_method_declaration = 0;
+ $_ = $original;
+ s/^[^{]*//;
+ } elsif (/\@end/) {
+ $in_method_declaration = 0;
+ $interface_name = "";
+ $_ = $original;
+ } else {
+ next;
+ }
+ }
+
+
+ # Find function, interface and method names.
+ while (m&((?:[[:word:]]+::)*operator(?:[ \t]*\(\)|[^()]*)|[[:word:]<>:~]+|[(){}:;=])|\@(?:implementation|interface|protocol)\s+(\w+)[^{]*&g) {
+ # Skip an array definition at the top level.
+ # e.g. static int arr[] = { 1, 2, 3 };
+ if ($1) {
+ if ($1 eq "=" and !$in_parentheses and !$in_braces) {
+ $equal_observed = 1;
+ } elsif ($1 eq "{" and $equal_observed) {
+ # This '{' is the beginning of an array definition, not the beginning of a method.
+ $in_toplevel_array_brace = 1;
+ $in_braces++;
+ $equal_observed = 0;
+ next;
+ } elsif ($1 !~ /[ \t]/) {
+ $equal_observed = 0;
+ }
+ }
+
+ # interface name
+ if ($2) {
+ $interface_name = $2;
+ next;
+ }
+
+ # Open parenthesis.
+ if ($1 eq "(") {
+ $potential_name = $word unless $in_parentheses || $skip_til_brace_or_semicolon || grep { $word eq $_ } ("CF_ENUM", "CF_OPTIONS", "NS_ENUM", "NS_OPTIONS");
+ $in_parentheses++;
+ next;
+ }
+
+ # Close parenthesis.
+ if ($1 eq ")") {
+ $in_parentheses--;
+ next;
+ }
+
+ if ($1 eq "const" and !$in_parentheses) {
+ $potential_name .= " const";
+ next;
+ }
+
+ if ($1 eq "volatile" and !$in_parentheses) {
+ $potential_name .= " volatile";
+ next;
+ }
+
+ # C++ auto function() -> type
+ if ($1 eq ">") {
+ $skip_til_brace_or_semicolon = 1 unless ($in_parentheses || $in_braces);
+ next;
+ }
+
+ # C++ constructor initializers
+ if ($1 eq ":") {
+ $skip_til_brace_or_semicolon = 1 unless ($in_parentheses || $in_braces);
+ }
+
+ # Open brace.
+ if ($1 eq "{") {
+ $skip_til_brace_or_semicolon = 0;
+
+ if (!$in_braces) {
+ if ($namespace_start >= 0 and $namespace_start < $potential_start) {
+ push @ranges, [ $namespace_start . "", $potential_start - 1, $name ];
+ }
+
+ if ($potential_namespace) {
+ push @namespaces, $potential_namespace;
+ push @all_namespaces, $potential_namespace;
+ $potential_namespace = "";
+ $name = $namespaces[-1];
+ $namespace_start = $. + 1;
+ next;
+ }
+
+ # Promote potential name to real function name at the
+ # start of the outer level set of braces (function body?).
+ if ($potential_start) {
+ $start = $potential_start;
+ $name = $potential_name;
+ if (@namespaces && $name && (length($name) < 2 || substr($name,1,1) ne "[")) {
+ $name = join ('::', @namespaces, $name);
+ }
+ }
+ }
+
+ $in_method_declaration = 0;
+
+ $brace_start = $. if (!$in_braces);
+ $in_braces++;
+ next;
+ }
+
+ # Close brace.
+ if ($1 eq "}") {
+ if (!$in_braces && @namespaces) {
+ if ($namespace_start >= 0 and $namespace_start < $.) {
+ push @ranges, [ $namespace_start . "", $. - 1, $name ];
+ }
+
+ pop @namespaces;
+ if (@namespaces) {
+ $name = $namespaces[-1];
+ $namespace_start = $. + 1;
+ } else {
+ $name = "";
+ $namespace_start = -1;
+ }
+ next;
+ }
+
+ $in_braces--;
+ $brace_end = $. if (!$in_braces);
+
+ # End of an outer level set of braces.
+ # This could be a function body.
+ if (!$in_braces and $name) {
+ # This is the end of an array definition at the top level, not the end of a method.
+ if ($in_toplevel_array_brace) {
+ $in_toplevel_array_brace = 0;
+ next;
+ }
+
+ push @ranges, [ $start, $., $name ];
+ if (@namespaces) {
+ $name = $namespaces[-1];
+ $namespace_start = $. + 1;
+ } else {
+ $name = "";
+ $namespace_start = -1;
+ }
+ }
+
+ $potential_start = 0;
+ $potential_name = "";
+ next;
+ }
+
+ # Semicolon.
+ if ($1 eq ";") {
+ $skip_til_brace_or_semicolon = 0;
+ $potential_start = 0;
+ $potential_name = "";
+ $in_method_declaration = 0;
+ next;
+ }
+
+ # Ignore "const" method qualifier.
+ if ($1 eq "const") {
+ next;
+ }
+
+ if ($1 eq "namespace" || $1 eq "class" || $1 eq "struct") {
+ $next_word_could_be_namespace = 1;
+ next;
+ }
+
+ # Word.
+ $word = $1;
+ if (!$skip_til_brace_or_semicolon) {
+ if ($next_word_could_be_namespace) {
+ $potential_namespace = $word;
+ $next_word_could_be_namespace = 0;
+ } elsif ($potential_namespace) {
+ $potential_namespace = "";
+ }
+
+ if (!$in_parentheses) {
+ $potential_start = 0;
+ $potential_name = "";
+ }
+ if (!$potential_start) {
+ $potential_start = $.;
+ $potential_name = "";
+ }
+ }
+ }
+ }
+
+ warn "missing close braces in $file_name (probable start at $brace_start)\n" if ($in_braces > 0);
+ warn "too many close braces in $file_name (probable start at $brace_end)\n" if ($in_braces < 0);
+
+ warn "mismatched parentheses in $file_name\n" if $in_parentheses;
+
+ return delete_namespaces_from_ranges_for_cpp(@ranges, @all_namespaces);
+}
+
+
+# Take in references to an array of line ranges for C functions in a given file
+# and an array of namespaces declared in that file and return an updated
+# list of line ranges with the namespaces removed.
+
+sub delete_namespaces_from_ranges_for_cpp(\@\@)
+{
+ my ($ranges, $namespaces) = @_;
+ return grep {!is_function_in_namespace($namespaces, $$_[2])} @$ranges;
+}
+
+
+sub is_function_in_namespace($$)
+{
+ my ($namespaces, $function_name) = @_;
+ return grep {$_ eq $function_name} @$namespaces;
+}
+
+
+# Read a file and get all the line ranges of the things that look like Java
+# classes, interfaces and methods.
+#
+# A class or interface name is the word that immediately follows
+# `class' or `interface' when followed by an open curly brace and not
+# a semicolon. It can appear at the top level, or inside another class
+# or interface block, but not inside a function block
+#
+# A class or interface starts at the first character after the first close
+# brace or after the function name and ends at the close brace.
+#
+# A function name is the last word before an open parenthesis before
+# an open brace rather than a semicolon. It can appear at top level or
+# inside a class or interface block, but not inside a function block.
+#
+# A function starts at the first character after the first close
+# brace or after the function name and ends at the close brace.
+#
+# Comment handling is simple-minded but will work for all but pathological cases.
+#
+# Result is a list of triples: [ start_line, end_line, function_name ].
+
+sub get_function_line_ranges_for_java($$)
+{
+ my ($file_handle, $file_name) = @_;
+
+ my @current_scopes;
+
+ my @ranges;
+
+ my $in_comment = 0;
+ my $in_macro = 0;
+ my $in_parentheses = 0;
+ my $in_braces = 0;
+ my $in_non_block_braces = 0;
+ my $class_or_interface_just_seen = 0;
+ my $in_class_declaration = 0;
+
+ my $word = "";
+
+ my $potential_start = 0;
+ my $potential_name = "";
+ my $potential_name_is_class_or_interface = 0;
+
+ my $start = 0;
+ my $name = "";
+ my $current_name_is_class_or_interface = 0;
+
+ while (<$file_handle>) {
+ # Handle continued multi-line comment.
+ if ($in_comment) {
+ next unless s-.*\*/--;
+ $in_comment = 0;
+ }
+
+ # Handle continued macro.
+ if ($in_macro) {
+ $in_macro = 0 unless /\\$/;
+ next;
+ }
+
+ # Handle start of macro (or any preprocessor directive).
+ if (/^\s*\#/) {
+ $in_macro = 1 if /^([^\\]|\\.)*\\$/;
+ next;
+ }
+
+ # Handle comments and quoted text.
+ while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy
+ my $match = $1;
+ if ($match eq "/*") {
+ if (!s-/\*.*?\*/--) {
+ s-/\*.*--;
+ $in_comment = 1;
+ }
+ } elsif ($match eq "//") {
+ s-//.*--;
+ } else { # ' or "
+ if (!s-$match([^\\]|\\.)*?$match--) {
+ warn "mismatched quotes at line $. in $file_name\n";
+ s-$match.*--;
+ }
+ }
+ }
+
+ # Find function names.
+ while (m-(\w+|[(){};])-g) {
+ # Open parenthesis.
+ if ($1 eq "(") {
+ if (!$in_parentheses) {
+ $potential_name = $word;
+ $potential_name_is_class_or_interface = 0;
+ }
+ $in_parentheses++;
+ next;
+ }
+
+ # Close parenthesis.
+ if ($1 eq ")") {
+ $in_parentheses--;
+ next;
+ }
+
+ # Open brace.
+ if ($1 eq "{") {
+ $in_class_declaration = 0;
+
+ # Promote potential name to real function name at the
+ # start of the outer level set of braces (function/class/interface body?).
+ if (!$in_non_block_braces
+ and (!$in_braces or $current_name_is_class_or_interface)
+ and $potential_start) {
+ if ($name) {
+ push @ranges, [ $start, ($. - 1),
+ join ('.', @current_scopes) ];
+ }
+
+
+ $current_name_is_class_or_interface = $potential_name_is_class_or_interface;
+
+ $start = $potential_start;
+ $name = $potential_name;
+
+ push (@current_scopes, $name);
+ } else {
+ $in_non_block_braces++;
+ }
+
+ $potential_name = "";
+ $potential_start = 0;
+
+ $in_braces++;
+ next;
+ }
+
+ # Close brace.
+ if ($1 eq "}") {
+ $in_braces--;
+
+ # End of an outer level set of braces.
+ # This could be a function body.
+ if (!$in_non_block_braces) {
+ if ($name) {
+ push @ranges, [ $start, $.,
+ join ('.', @current_scopes) ];
+
+ pop (@current_scopes);
+
+ if (@current_scopes) {
+ $current_name_is_class_or_interface = 1;
+
+ $start = $. + 1;
+ $name = $current_scopes[$#current_scopes-1];
+ } else {
+ $current_name_is_class_or_interface = 0;
+ $start = 0;
+ $name = "";
+ }
+ }
+ } else {
+ $in_non_block_braces-- if $in_non_block_braces;
+ }
+
+ $potential_start = 0;
+ $potential_name = "";
+ next;
+ }
+
+ # Semicolon.
+ if ($1 eq ";") {
+ $potential_start = 0;
+ $potential_name = "";
+ next;
+ }
+
+ if ($1 eq "class") {
+ $in_class_declaration = 1;
+ }
+ if ($1 eq "class" or (!$in_class_declaration and $1 eq "interface")) {
+ $class_or_interface_just_seen = 1;
+ next;
+ }
+
+ # Word.
+ $word = $1;
+ if (!$in_parentheses) {
+ if ($class_or_interface_just_seen) {
+ $potential_name = $word;
+ $potential_start = $.;
+ $class_or_interface_just_seen = 0;
+ $potential_name_is_class_or_interface = 1;
+ next;
+ }
+ }
+ if (!$potential_start) {
+ $potential_start = $.;
+ $potential_name = "";
+ }
+ $class_or_interface_just_seen = 0;
+ }
+ }
+
+ warn "mismatched braces in $file_name\n" if $in_braces;
+ warn "mismatched parentheses in $file_name\n" if $in_parentheses;
+
+ return @ranges;
+}
+
+
+
+# Read a file and get all the line ranges of the things that look like
+# JavaScript functions or methods.
+#
+# A function name is the word that immediately follows `function' when
+# followed by an open curly brace. It can appear at the top level,
+# or inside other functions. For example:
+#
+# function name() { // (name)
+# function inner() { } // (name.inner)
+# }
+#
+# An anonymous function name is the identifier on the left hand side of
+# an assignment with the equals operator or object notation that has a
+# value starting with `function' followed an open curly brace.
+# For example:
+#
+# namespace = {
+# name: function() {} // (namespace.name)
+# }
+# namespace.Foo = function() {} // (namespace.Foo)
+#
+# A getter or setter name is the word that immediately follows `get' or
+# `set' when followed by params and an open curly brace. For example:
+#
+# namespace = {
+# get foo() {} // (namespace.get foo)
+# }
+#
+# A method name is the word immediately before parenthesis, with an open
+# curly brace immediately following closing parenthesis. For a class expression
+# we take the assignment identifier instead of the class name for namespacing.
+#
+# namespace.Foo = class DoesNotMatter extends Bar {
+# constructor() {} // (namespace.Foo)
+# static staticMethod() {} // (namespace.Foo.staticMethod)
+# instanceMethod() {} // (namespace.Foo.prototype.instanceMethod)
+# get getter() {} // (namespace.Foo.prototype.get getter)
+# }
+# class ClassName {
+# constructor() {} // (ClassName)
+# method() {} // (ClassName.prototype.method)
+# }
+#
+# Methods may exist in object literals, outside of classes.
+#
+# Foo.prototype = {
+# method() {}, // (Foo.prototype.method)
+# otherMethod() {} // (Foo.prototype.otherMethod)
+# }
+#
+# Comment handling is simple-minded but will work for all but pathological cases.
+#
+# Result is a list of triples: [ start_line, end_line, function_name ].
+
+sub get_function_line_ranges_for_javascript($$)
+{
+ my ($fileHandle, $fileName) = @_;
+
+ my @currentScopes;
+ my @currentIdentifiers;
+ my @currentParsingMode = ("global");
+ my @currentFunctionNames;
+ my @currentFunctionDepths;
+ my @currentFunctionStartLines;
+
+ my @ranges;
+
+ my $inComment = 0;
+ my $inQuotedText = "";
+ my $inExtends = 0;
+ my $inMethod = 0;
+ my $inAnonymousFunctionParameters = 0;
+ my $parenthesesDepth = 0;
+ my $globalParenthesesDepth = 0;
+ my $bracesDepth = 0;
+
+ my $classJustSeen = 0;
+ my $parenthesisJustSeen = 0;
+ my $functionJustSeen = 0;
+ my $getterJustSeen = 0;
+ my $setterJustSeen = 0;
+ my $asyncJustSeen = 0;
+ my $assignmentJustSeen = 0;
+ my $staticOrContructorSeen = 0;
+
+ my $currentToken = "";
+ my $lastToken = "";
+ my $possibleMethodName = "";
+ my $word = "";
+
+ while (<$fileHandle>) {
+ # Handle continued multi-line comment.
+ if ($inComment) {
+ next unless s-.*\*/--;
+ $inComment = 0;
+ }
+
+ # Handle continued quoted text.
+ if ($inQuotedText ne "") {
+ next if /\\$/;
+ s-([^\\]|\\.)*?$inQuotedText--;
+ $inQuotedText = "";
+ }
+
+ # Handle comments and quoted text.
+ while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy
+ my $match = $1;
+ if ($match eq '/*') {
+ if (!s-/\*.*?\*/--) {
+ s-/\*.*--;
+ $inComment = 1;
+ }
+ } elsif ($match eq '//') {
+ s-//.*--;
+ } else { # ' or "
+ if (!s-$match([^\\]|\\.)*?$match-string_appeared_here-) {
+ $inQuotedText = $match if /\\$/;
+ warn "mismatched quotes at line $. in $fileName\n" if $inQuotedText eq "";
+ s-$match.*--;
+ }
+ }
+ }
+
+ # Find function names.
+ while (m-(\w+|[(){}=:;,.])-g) {
+ # Skip everything until "{" after extends.
+ if ($inExtends) {
+ next if $1 ne '{';
+ $inExtends = 0;
+ }
+
+ $lastToken = $currentToken;
+ $currentToken = $1;
+
+ # Open parenthesis.
+ if ($1 eq '(') {
+ $parenthesesDepth++;
+ $globalParenthesesDepth++ if $currentParsingMode[$#currentParsingMode] eq "global";
+ $possibleMethodName = join('.', @currentIdentifiers);
+ $inAnonymousFunctionParameters = 1 if $functionJustSeen;
+ $functionJustSeen = 0;
+ next;
+ }
+
+ # Close parenthesis.
+ if ($1 eq ')') {
+ $parenthesesDepth--;
+ $globalParenthesesDepth-- if $currentParsingMode[$#currentParsingMode] eq "global";
+ @currentIdentifiers = () if $inAnonymousFunctionParameters;
+ $inAnonymousFunctionParameters = 0;
+ $parenthesisJustSeen = 1;
+ next;
+ }
+
+ # Open brace.
+ if ($1 eq '{') {
+ my $methodName = "";
+ my $mode = $currentParsingMode[$#currentParsingMode];
+
+ # Method.
+ if (($mode eq 'class' or $mode eq 'global') and $parenthesisJustSeen and ($staticOrContructorSeen or $possibleMethodName)) {
+ if ($mode eq 'class') {
+ $methodName = join('.', $staticOrContructorSeen ? "" : "prototype", $possibleMethodName);
+ } else {
+ $methodName = $possibleMethodName;
+ }
+
+ $methodName =~ s/\.{2,}/\./g; # Removes consecutive periods.
+ $methodName =~ s/\.$//; # Remove trailing period.
+
+ my $currentMethod = join('.', @currentScopes, $methodName);
+ $currentMethod =~ s/\.{2,}/\./g; # Removes consecutive periods.
+ $currentMethod =~ s/\.$//; # Remove trailing period.
+
+ push(@currentParsingMode, "method");
+ push(@currentFunctionNames, $currentMethod);
+ push(@currentFunctionDepths, $bracesDepth);
+ push(@currentFunctionStartLines, $.);
+ }
+
+ $bracesDepth++;
+ $functionJustSeen = 0;
+
+ push(@currentScopes, join('.', $methodName ? $methodName : @currentIdentifiers));
+ @currentIdentifiers = ();
+
+ $staticOrContructorSeen = 0;
+ next;
+ }
+
+ # Close brace.
+ if ($1 eq '}') {
+ $bracesDepth--;
+ $functionJustSeen = 0;
+
+ if (@currentFunctionDepths and $bracesDepth == $currentFunctionDepths[$#currentFunctionDepths]) {
+ pop(@currentFunctionDepths);
+ pop(@currentParsingMode);
+
+ my $currentName = pop(@currentFunctionNames);
+ my $start = pop(@currentFunctionStartLines);
+
+ $currentName =~ s/^\.//g; # Removes leading periods.
+
+ push(@ranges, [$start, $., $currentName]);
+ }
+
+ pop(@currentScopes);
+ @currentIdentifiers = ();
+
+ next;
+ }
+
+ # Dot.
+ if ($1 eq '.') {
+ next;
+ }
+
+ # Semicolon or comma.
+ if ($1 eq ';' or $1 eq ',') {
+ @currentIdentifiers = ();
+ next;
+ }
+
+ # Class.
+ if ($1 eq 'class') {
+ $classJustSeen = 1;
+ next;
+ }
+
+ # Extends.
+ if ($1 eq 'extends') {
+ $inExtends = 1;
+ next;
+ }
+
+ # Function.
+ if ($1 eq 'function') {
+ $functionJustSeen = 1;
+
+ if ($assignmentJustSeen) {
+ my $currentFunction = join('.', (@currentScopes, @currentIdentifiers));
+ $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods.
+
+ push(@currentParsingMode, "function");
+ push(@currentFunctionNames, $currentFunction);
+ push(@currentFunctionDepths, $bracesDepth);
+ push(@currentFunctionStartLines, $.);
+ }
+
+ next;
+ }
+
+ # Getter prefix.
+ if ($1 eq 'get') {
+ next if $lastToken eq '.'; # Avoid map.get(...).
+ $getterJustSeen = 1;
+ next;
+ }
+
+ # Setter prefix.
+ if ($1 eq 'set') {
+ next if $lastToken eq '.'; # Avoid map.set(...).
+ $setterJustSeen = 1;
+ next;
+ }
+
+ # Async prefix.
+ if ($1 eq 'async') {
+ next if $lastToken eq '.';
+ $asyncJustSeen = 1;
+ next;
+ }
+
+ # Static.
+ if ($1 eq 'static' or $1 eq 'constructor') {
+ $staticOrContructorSeen = 1;
+ next;
+ }
+
+ # Assignment operator.
+ if ($1 eq '=' or $1 eq ':') {
+ $assignmentJustSeen = 1;
+ next;
+ }
+
+ next if $parenthesesDepth > $globalParenthesesDepth;
+
+ # Word.
+ $word = $1;
+
+ if ($classJustSeen) {
+ push(@currentIdentifiers, $word) if !$assignmentJustSeen;
+
+ my $currentClass = join('.', (@currentScopes, @currentIdentifiers));
+ $currentClass =~ s/\.{2,}/\./g; # Removes consecutive periods.
+
+ push(@currentParsingMode, "class");
+ push(@currentFunctionNames, $currentClass);
+ push(@currentFunctionDepths, $bracesDepth);
+ push(@currentFunctionStartLines, $.);
+ } elsif ($getterJustSeen or $setterJustSeen or $asyncJustSeen) {
+ $word = "get $word" if $getterJustSeen;
+ $word = "set $word" if $setterJustSeen;
+ $word = "async $word" if $asyncJustSeen;
+
+ push(@currentIdentifiers, $word);
+
+ my $mode = $currentParsingMode[$#currentParsingMode];
+ my $currentFunction = join('.', (@currentScopes, ($mode eq 'class' and !$staticOrContructorSeen) ? "prototype" : "", @currentIdentifiers));
+ $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods.
+
+ push(@currentParsingMode, "function");
+ push(@currentFunctionNames, $currentFunction);
+ push(@currentFunctionDepths, $bracesDepth);
+ push(@currentFunctionStartLines, $.);
+ } elsif ($functionJustSeen and !$assignmentJustSeen) {
+ push(@currentIdentifiers, $word);
+
+ my $currentFunction = join('.', (@currentScopes, @currentIdentifiers));
+ $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods.
+
+ push(@currentParsingMode, "function");
+ push(@currentFunctionNames, $currentFunction);
+ push(@currentFunctionDepths, $bracesDepth);
+ push(@currentFunctionStartLines, $.);
+ } elsif ($word ne 'if' and $word ne 'for' and $word ne 'do' and $word ne 'while' and $word ne 'which' and $word ne 'var') {
+ push(@currentIdentifiers, $word);
+ }
+
+ $classJustSeen = 0;
+ $parenthesisJustSeen = 0;
+ $functionJustSeen = 0;
+ $getterJustSeen = 0;
+ $setterJustSeen = 0;
+ $asyncJustSeen = 0;
+ $assignmentJustSeen = 0;
+ }
+ }
+
+ warn "mismatched braces in $fileName\n" if $bracesDepth;
+ warn "mismatched parentheses in $fileName\n" if $parenthesesDepth;
+
+ return @ranges;
+}
+
+# Read a file and get all the line ranges of the things that look like Perl functions. Functions
+# start on a line that starts with "sub ", and end on the first line starting with "}" thereafter.
+#
+# Result is a list of triples: [ start_line, end_line, function ].
+
+sub get_function_line_ranges_for_perl($$)
+{
+ my ($fileHandle, $fileName) = @_;
+
+ my @ranges;
+
+ my $currentFunction = "";
+ my $start = 0;
+ my $hereDocumentIdentifier = "";
+
+ while (<$fileHandle>) {
+ chomp;
+ if (!$hereDocumentIdentifier) {
+ if (/^sub\s+([\w_][\w\d_]*)/) {
+ # Skip over forward declarations, which don't contain a brace and end with a semicolon.
+ next if /;\s*$/;
+
+ if ($currentFunction) {
+ warn "nested functions found at top-level at $fileName:$.\n";
+ next;
+ }
+ $currentFunction = $1;
+ $start = $.;
+ }
+ if (/<<\s*[\"\']?([\w_][\w_\d]*)/) {
+ # Enter here-document.
+ $hereDocumentIdentifier = $1;
+ }
+ if (index($_, "}") == 0) {
+ next unless $start;
+ push(@ranges, [$start, $., $currentFunction]);
+ $currentFunction = "";
+ $start = 0;
+ }
+ } elsif ($_ eq $hereDocumentIdentifier) {
+ # Escape from here-document.
+ $hereDocumentIdentifier = "";
+ }
+ }
+
+ return @ranges;
+}
+
+# Read a file and get all the line ranges of the things that look like Python classes, methods, or functions.
+#
+# FIXME: Maybe we should use Python's ast module to do the parsing for us?
+#
+# Result is a list of triples: [ start_line, end_line, function ].
+
+sub get_function_line_ranges_for_python($$)
+{
+ my ($fileHandle, $fileName) = @_;
+
+ my @ranges;
+
+ my $multilineStringLiteralSentinelRegEx = qr#(?:"""|''')#;
+ my $multilineStringLiteralStartRegEx = qr#\s*$multilineStringLiteralSentinelRegEx#;
+ my $multilineStringLiteralEndRegEx = qr#$multilineStringLiteralSentinelRegEx\s*$#;
+
+ my @scopeStack = ({ line => 0, indent => -1, name => undef });
+ my $lastLine = 0;
+ my $inComment = 0;
+ until ($lastLine) {
+ $_ = <$fileHandle>;
+ unless ($_) {
+ # To pop out all popped scopes, run the loop once more after
+ # we encountered the end of the file.
+ $_ = "pass\n";
+ $.++;
+ $lastLine = 1;
+ }
+ chomp;
+ next unless /^(\s*)([^#].*)$/; # Skip non-indented lines that begin with a comment.
+
+ my $indent = length $1;
+ my $rest = $2;
+ my $scope = $scopeStack[-1];
+
+ if ($indent <= $scope->{indent}) {
+ # Find all the scopes that we have just exited.
+ my $i = 0;
+ for (; $i < @scopeStack; ++$i) {
+ last if $indent <= $scopeStack[$i]->{indent};
+ }
+ my @poppedScopes = splice @scopeStack, $i;
+
+ # For each scope that was just exited, add a range that goes from the start of that
+ # scope to the start of the next nested scope, or to the line just before this one for
+ # the innermost scope.
+ for ($i = 0; $i < @poppedScopes; ++$i) {
+ my $lineAfterEnd = $i + 1 == @poppedScopes ? $. : $poppedScopes[$i + 1]->{line};
+ push @ranges, [$poppedScopes[$i]->{line}, $lineAfterEnd - 1, $poppedScopes[$i]->{name}];
+ }
+ @scopeStack or warn "Popped off last scope at $fileName:$.\n";
+
+ # Set the now-current scope to start at the current line. Any lines within this scope
+ # before this point should already have been added to @ranges.
+ $scope = $scopeStack[-1];
+ $scope->{line} = $.;
+ }
+
+ # Skip multi-line string literals and docstrings.
+ next if /$multilineStringLiteralStartRegEx.*$multilineStringLiteralEndRegEx/;
+ if (!$inComment && /$multilineStringLiteralStartRegEx/) {
+ $inComment = 1;
+ } elsif ($inComment && /$multilineStringLiteralEndRegEx/) {
+ $inComment = 0;
+ }
+ next if $inComment;
+
+ next if /^\s*[#'"]/; # Skip indented and non-indented lines that begin with a comment or string literal (includes docstrings).
+
+ next unless $rest =~ /(?:class|def)\s+(\w+)/;
+ my $name = $1;
+ my $fullName = $scope->{name} ? join('.', $scope->{name}, $name) : $name;
+ push @scopeStack, { line => $., indent => $indent, name => $fullName };
+
+ if ($scope->{indent} >= 0) {
+ push @ranges, [$scope->{line}, $. - 1, $scope->{name}];
+ }
+ }
+
+ return @ranges;
+}
+
+# Read a file and get all the line ranges of the things that look like CSS selectors. A selector is
+# anything before an opening brace on a line. A selector starts at the line containing the opening
+# brace and ends at the closing brace.
+#
+# Result is a list of triples: [ start_line, end_line, selector ].
+
+sub get_selector_line_ranges_for_css($$)
+{
+ my ($fileHandle, $fileName) = @_;
+
+ my @ranges;
+
+ my $inComment = 0;
+ my $inBrace = 0;
+ my @stack;
+ my $context;
+ my @currentParseMode = ("global");
+ my @groupingStack;
+ my $selectorBraces = 0;
+
+ while (<$fileHandle>) {
+ foreach my $token (split m-(\{|\}|/\*|\*/)-, $_) {
+ if ($token eq "{") {
+ if (!$inComment) {
+ $inBrace += 1;
+ $selectorBraces += 1 if $currentParseMode[$#currentParseMode] eq "selector";
+ warn "mismatched opening brace found in $fileName:$.\n" if $selectorBraces > 1;
+ }
+ } elsif ($token eq "}") {
+ if (!$inComment) {
+ if (!$inBrace or $currentParseMode[$#currentParseMode] eq "global") {
+ warn "mismatched closing brace found in $fileName:$.\n";
+ next;
+ }
+
+ $inBrace -= 1;
+
+ my $parseMode = pop(@currentParseMode);
+ if ($parseMode eq "selector") {
+ my $name = pop(@stack);
+ my $startLine = pop(@stack);
+ my $endLine = $.;
+ my $groupingPrefix = join(" ", @groupingStack);
+ if (length $groupingPrefix) {
+ $groupingPrefix .= " "
+ }
+ push(@ranges, [$startLine, $endLine, $groupingPrefix . $name]);
+ } elsif ($parseMode eq "media") {
+ pop(@groupingStack);
+ }
+
+ $selectorBraces = 0;
+ }
+ } elsif ($token eq "/*") {
+ $inComment = 1;
+ } elsif ($token eq "*/") {
+ warn "mismatched comment found in $fileName:$.\n" if !$inComment;
+ $inComment = 0;
+ } else {
+ if (!$inComment and $currentParseMode[$#currentParseMode] ne "selector" and $token !~ /^[\s\t]*$/) {
+ $token =~ s/^[\s\t]*|[\s\t]*$//g;
+ my $startLine = $.;
+ if ($token =~ /^\@media/) {
+ push(@currentParseMode, "media");
+ push(@groupingStack, $token);
+ } else {
+ push(@currentParseMode, "selector");
+ push(@stack, ($startLine, $token));
+ }
+ }
+ }
+ }
+ }
+
+ # Sort by start line.
+ return sort {$a->[0] <=> $b->[0]} @ranges;
+}
+
+# Read a file and get all the line ranges of the things that look like Swift classes, methods,
+# or functions.
+#
+# Result is a list of triples: [ start_line, end_line, function ].
+
+sub get_function_line_ranges_for_swift($$)
+{
+ my ($fileHandle, $fileName) = @_;
+
+ my @ranges;
+
+ my $currentFunction = "";
+ my $currentType = "";
+ my $functionStart = 0;
+ my $typeStart = 0;
+ my $functionScopeDepth = 0;
+ my $typeScopeDepth = 0;
+ my $scopeDepth = 0;
+
+ while (<$fileHandle>) {
+ chomp;
+ next if (/^\s*\/\/.*/);
+ if (/func\s+([\w_][\w\d_]*)\((.*)\)/ || /var\s+([\w_][\w\d_]*):\s+/) {
+ $functionScopeDepth = $scopeDepth;
+ $currentFunction = $1;
+ if ($2) {
+ $currentFunction = "$currentFunction(". parseSwiftFunctionArgs($2) . ")";
+ }
+ if ($currentType) {
+ $currentFunction = "$currentType.$currentFunction";
+ }
+ $functionStart = $.;
+ } elsif (/(?:class|struct|enum|protocol|extension)\s+([\w_][\w\d_]*)/) {
+ $typeScopeDepth = $scopeDepth;
+ $currentType = $1;
+ $typeStart = $.;
+ }
+ if (index($_, "{") > -1) {
+ $scopeDepth++;
+ }
+ if (index($_, "}") > -1) {
+ $scopeDepth--;
+ }
+ if ($scopeDepth == $functionScopeDepth) {
+ next unless $functionStart;
+ push(@ranges, [$functionStart, $., $currentFunction]);
+ $currentFunction = "";
+ $functionStart = 0;
+ } elsif ($scopeDepth == $typeScopeDepth) {
+ next unless $typeStart;
+ $currentType = "";
+ $typeStart = 0;
+ }
+ }
+
+ return @ranges;
+}
+
+sub parseSwiftFunctionArgs($)
+{
+ my ($functionArgs) = @_;
+ my @words = split /, /, $functionArgs;
+ my $argCount = scalar(@words);
+ if ($argCount == 0) {
+ return "";
+ } elsif ($argCount > 0) {
+ # If the first argument is unnamed, give it the name "_"
+ $words[0] =~ s/^(\w+: .*)/_ $1/;
+ return join("", map { $_ =~ s/^(\w+).*/$1/; "$_:" } @words);
+ } else {
+ warn "Unknown argument count.\n";
+ }
+}
+
</ins><span class="cx"> sub processPaths(\@)
</span><span class="cx"> {
</span><span class="cx"> my ($paths) = @_;
</span><span class="lines">@@ -676,8 +2086,8 @@
</span><span class="cx"> {
</span><span class="cx"> my ($paths, $gitCommit, $gitIndex, $mergeBase) = @_;
</span><span class="cx">
</span><del>- # The function overlap detection logic in webkitperl::changelog::computeModifiedFunctions()
- # assumes that its line ranges were from a unified diff without any context lines.
</del><ins>+ # The function overlap detection logic in computeModifiedFunctions() assumes that its line
+ # ranges were from a unified diff without any context lines.
</ins><span class="cx"> my $command;
</span><span class="cx"> if (isSVN()) {
</span><span class="cx"> my @escapedPaths = map(escapeSubversionPath($_), @$paths);
</span><span class="lines">@@ -710,6 +2120,47 @@
</span><span class="cx"> return "$command 2>&1";
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+sub attributeCommand($$$)
+{
+ my ($attributeCache, $file, $attr) = @_;
+
+ my $result;
+ if (isSVN()) {
+ my $devNull = File::Spec->devnull();
+ my $foundAttribute = 0;
+ my $subPath = ".";
+ my (@directoryParts) = File::Spec->splitdir($file);
+ foreach my $part (@directoryParts) {
+ if ($part eq ".") {
+ next;
+ }
+ $subPath = File::Spec->join($subPath, $part);
+ $subPath =~ s/^\.\///;
+ if ($foundAttribute || exists $attributeCache->{$attr}{$subPath} && $attributeCache->{$attr}{$subPath} eq "1") {
+ $attributeCache->{$attr}{$subPath} = "1";
+ $foundAttribute = 1;
+ next;
+ }
+ my $command = SVN . " propget $attr '$subPath'";
+ my $attrib = $attributeCache->{$attr}{$subPath} || `$command 2> $devNull`;
+ chomp $attrib;
+ if ($attrib eq "1") {
+ $foundAttribute = 1;
+ }
+ $attributeCache->{$attr}{$subPath} = $attrib || "0";
+ }
+ $result = $attributeCache->{$attr}{$file};
+ } elsif (isGit()) {
+ my $command = GIT . " check-attr $attr -- '$file'";
+ $result = `$command`;
+ chomp $result;
+ $result =~ s/.*\W(\w)/$1/;
+ }
+
+ $result =~ s/\D//g;
+ return int($result || 0);
+}
+
</ins><span class="cx"> sub createPatchCommand($$$$)
</span><span class="cx"> {
</span><span class="cx"> my ($changedFilesString, $gitCommit, $gitIndex, $mergeBase) = @_;
</span><span class="lines">@@ -1008,6 +2459,34 @@
</span><span class="cx"> return $description;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+sub extractLineRangeAfterChange($)
+{
+ my ($string) = @_;
+ my $chunkRange = parseChunkRange($string);
+ if (!$chunkRange) {
+ return (-1, -1); # Malformed
+ }
+ if (!$chunkRange->{newStartingLine} || !$chunkRange->{newLineCount}) {
+ # Deletion; no lines exist after change.
+ return ($chunkRange->{newStartingLine}, $chunkRange->{newStartingLine});
+ }
+ return ($chunkRange->{newStartingLine}, $chunkRange->{newStartingLine} + $chunkRange->{newLineCount} - 1);
+}
+
+sub extractLineRangeBeforeChange($)
+{
+ my ($string) = @_;
+ my $chunkRange = parseChunkRange($string);
+ if (!$chunkRange) {
+ return (-1, -1); # Malformed
+ }
+ if (!$chunkRange->{startingLine} || !$chunkRange->{lineCount}) {
+ # Addition; no lines existed before change.
+ return ($chunkRange->{startingLine}, $chunkRange->{startingLine});
+ }
+ return ($chunkRange->{startingLine}, $chunkRange->{startingLine} + $chunkRange->{lineCount} - 1);
+}
+
</ins><span class="cx"> sub testListForChangeLog(@)
</span><span class="cx"> {
</span><span class="cx"> my (@tests) = @_;
</span><span class="lines">@@ -1094,3 +2573,14 @@
</span><span class="cx"> $string =~ s/\r?\n/$endl/g;
</span><span class="cx"> return $string;
</span><span class="cx"> }
</span><ins>+
+sub decodeEntities($)
+{
+ my ($text) = @_;
+ $text =~ s/\</</g;
+ $text =~ s/\>/>/g;
+ $text =~ s/\"/\"/g;
+ $text =~ s/\'/\'/g;
+ $text =~ s/\&/\&/g;
+ return $text;
+}
</ins></span></pre></div>
<a id="trunkToolsScriptsrunleaks"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/run-leaks (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/run-leaks 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/run-leaks 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -33,12 +33,11 @@
</span><span class="cx">
</span><span class="cx"> use File::Basename;
</span><span class="cx"> use Getopt::Long;
</span><del>-use Carp;
-use FindBin;
-use lib $FindBin::RealBin;
-use webkitperl::leaks qw(runLeaks, parseLeaksOutput);
</del><span class="cx">
</span><ins>+sub runLeaks($$);
+sub parseLeaksOutput(\@);
</ins><span class="cx"> sub removeMatchingRecords(\@$\@);
</span><ins>+sub reportError($);
</ins><span class="cx">
</span><span class="cx"> sub main()
</span><span class="cx"> {
</span><span class="lines">@@ -72,7 +71,7 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (!$pidOrExecutableName) {
</span><del>- carp "Missing argument: pid | executable.";
</del><ins>+ reportError("Missing argument: pid | executable.");
</ins><span class="cx"> print STDERR $usage;
</span><span class="cx"> return 1;
</span><span class="cx"> }
</span><span class="lines">@@ -84,6 +83,9 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> my $parsedOutput = parseLeaksOutput(@$leaksOutput);
</span><ins>+ if (!$parsedOutput) {
+ return 1;
+ }
</ins><span class="cx">
</span><span class="cx"> # Filter output.
</span><span class="cx"> # FIXME: Adjust the overall leak count in the output accordingly. This will affect callers, notably leakdetector.py.
</span><span class="lines">@@ -112,6 +114,111 @@
</span><span class="cx">
</span><span class="cx"> exit(main());
</span><span class="cx">
</span><ins>+# Returns the output of the leaks tool in list form.
+sub runLeaks($$)
+{
+ my ($target, $memgraphPath) = @_;
+
+ if (defined $memgraphPath) {
+ `/usr/bin/leaks \"$target\" --outputGraph=\"$memgraphPath\"`;
+ $target = $memgraphPath;
+ }
+
+ # To get a result we can parse, we need to pass --list to all versions of the leaks tool
+ # that recognize it, but there is no certain way to tell in advance (the version of the
+ # tool is not always tied to OS version, and --help doesn't document the option in some
+ # of the tool versions where it's supported and needed).
+ my @leaksOutput = `/usr/bin/leaks \"$target\" --list`;
+
+ # FIXME: Remove the fallback once macOS Mojave is the oldest supported version.
+ my $leaksExitCode = $? >> 8;
+ if ($leaksExitCode > 1) {
+ @leaksOutput = `/usr/bin/leaks \"$target\"`;
+ }
+
+ return \@leaksOutput;
+}
+
+# Returns a list of hash references with the keys { address, size, type, callStack, leaksOutput }
+sub parseLeaksOutput(\@)
+{
+ my ($leaksOutput) = @_;
+
+ # Format:
+ # Process 00000: 1234 nodes malloced for 1234 KB
+ # Process 00000: XX leaks for XXX total leaked bytes.
+ # Leak: 0x00000000 size=1234 [instance of 'blah']
+ # 0x00000000 0x00000000 0x00000000 0x00000000 a..d.e.e
+ # ...
+ # Call stack: leak_caller() | leak() | malloc
+ #
+ # We treat every line except for Process 00000: and Leak: as optional
+
+ my $leakCount;
+ my @parsedOutput = ();
+ my $parsingLeak = 0;
+ my $parsedLeakCount = 0;
+ for my $line (@$leaksOutput) {
+ if ($line =~ /^Process \d+: (\d+) leaks?/) {
+ $leakCount = $1;
+ }
+
+ if ($line eq "\n") {
+ $parsingLeak = 0;
+ }
+
+ if ($line =~ /^Leak: /) {
+ $parsingLeak = 1;
+ $parsedLeakCount++;
+ my ($address) = ($line =~ /Leak: ([[:xdigit:]x]+)/);
+ if (!defined($address)) {
+ reportError("Could not parse Leak address.");
+ return;
+ }
+
+ my ($size) = ($line =~ /size=([[:digit:]]+)/);
+ if (!defined($size)) {
+ reportError("Could not parse Leak size.");
+ return;
+ }
+
+ # FIXME: This code seems wrong, the leaks tool doesn't actually use single quotes.
+ # We should reconcile with other format changes that happened since, such as the
+ # addition of zone information.
+ my ($type) = ($line =~ /'([^']+)'/); #'
+ if (!defined($type)) {
+ $type = ""; # The leaks tool sometimes omits the type.
+ }
+
+ my %leak = (
+ "address" => $address,
+ "size" => $size,
+ "type" => $type,
+ "callStack" => "", # The leaks tool sometimes omits the call stack.
+ "leaksOutput" => $line
+ );
+ push(@parsedOutput, \%leak);
+ } elsif ($parsingLeak) {
+ $parsedOutput[$#parsedOutput]->{"leaksOutput"} .= $line;
+ if ($line =~ /Call stack:/) {
+ $parsedOutput[$#parsedOutput]->{"callStack"} = $line;
+ }
+ } else {
+ my %nonLeakLine = (
+ "leaksOutput" => $line
+ );
+ push(@parsedOutput, \%nonLeakLine);
+ }
+ }
+
+ if ($parsedLeakCount != $leakCount) {
+ reportError("Parsed leak count ($parsedLeakCount) does not match leak count reported by leaks tool ($leakCount).");
+ return;
+ }
+
+ return \@parsedOutput;
+}
+
</ins><span class="cx"> sub removeMatchingRecords(\@$\@)
</span><span class="cx"> {
</span><span class="cx"> my ($recordList, $key, $regexpList) = @_;
</span><span class="lines">@@ -129,3 +236,10 @@
</span><span class="cx"> $i++;
</span><span class="cx"> }
</span><span class="cx"> }
</span><ins>+
+sub reportError($)
+{
+ my ($errorMessage) = @_;
+
+ print STDERR basename($0) . ": $errorMessage\n";
+}
</ins></span></pre></div>
<a id="trunkToolsScriptswebkitdirspm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitdirs.pm (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitdirs.pm 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitdirs.pm 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -47,7 +47,6 @@
</span><span class="cx"> use POSIX;
</span><span class="cx"> use Time::HiRes qw(usleep);
</span><span class="cx"> use VCSUtils;
</span><del>-use webkitperl::dirs;
</del><span class="cx">
</span><span class="cx"> BEGIN {
</span><span class="cx"> use Exporter ();
</span><span class="lines">@@ -146,6 +145,7 @@
</span><span class="cx"> my $simulatorIdiom;
</span><span class="cx"> my $configurationForVisualStudio;
</span><span class="cx"> my $configurationProductDir;
</span><ins>+my $sourceDir;
</ins><span class="cx"> my $currentSVNRevision;
</span><span class="cx"> my $didLoadIPhoneSimulatorNotification;
</span><span class="cx"> my $nmPath;
</span><span class="lines">@@ -195,6 +195,24 @@
</span><span class="cx"> return $sdkPlatformDirectory;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+sub determineSourceDir
+{
+ return if $sourceDir;
+ $sourceDir = $FindBin::Bin;
+ $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
+
+ # walks up path checking each directory to see if it is the main WebKit project dir,
+ # defined by containing Sources, WebCore, and JavaScriptCore.
+ until ((-d File::Spec->catdir($sourceDir, "Source") && -d File::Spec->catdir($sourceDir, "Source", "WebCore") && -d File::Spec->catdir($sourceDir, "Source", "JavaScriptCore")) || (-d File::Spec->catdir($sourceDir, "Internal") && -d File::Spec->catdir($sourceDir, "OpenSource")))
+ {
+ if ($sourceDir !~ s|/[^/]+$||) {
+ die "Could not find top level webkit directory above source directory using FindBin.\n";
+ }
+ }
+
+ $sourceDir = File::Spec->catdir($sourceDir, "OpenSource") if -d File::Spec->catdir($sourceDir, "OpenSource");
+}
+
</ins><span class="cx"> sub currentPerlPath()
</span><span class="cx"> {
</span><span class="cx"> my $thisPerl = $^X;
</span><span class="lines">@@ -204,6 +222,12 @@
</span><span class="cx"> return $thisPerl;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+# used for scripts which are stored in a non-standard location
+sub setSourceDir($)
+{
+ ($sourceDir) = @_;
+}
+
</ins><span class="cx"> sub determineNinjaVersion
</span><span class="cx"> {
</span><span class="cx"> chomp(my $ninjaVersion = `ninja --version`);
</span><span class="lines">@@ -233,8 +257,8 @@
</span><span class="cx"> sub determineBaseProductDir
</span><span class="cx"> {
</span><span class="cx"> return if defined $baseProductDir;
</span><ins>+ determineSourceDir();
</ins><span class="cx">
</span><del>- my $sourceDir = sourceDir();
</del><span class="cx"> my $setSharedPrecompsDir;
</span><span class="cx"> my $indexDataStoreDir;
</span><span class="cx"> $baseProductDir = $ENV{"WEBKIT_OUTPUTDIR"};
</span><span class="lines">@@ -832,7 +856,8 @@
</span><span class="cx"> # We always update the current SVN revision here, and leave the caching
</span><span class="cx"> # to currentSVNRevision(), so that changes to the SVN revision while the
</span><span class="cx"> # script is running can be picked up by calling this function again.
</span><del>- $currentSVNRevision = svnRevisionForDirectory(sourceDir());
</del><ins>+ determineSourceDir();
+ $currentSVNRevision = svnRevisionForDirectory($sourceDir);
</ins><span class="cx"> return $currentSVNRevision;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -839,7 +864,8 @@
</span><span class="cx">
</span><span class="cx"> sub chdirWebKit
</span><span class="cx"> {
</span><del>- chdir sourceDir() or die;
</del><ins>+ determineSourceDir();
+ chdir $sourceDir or die;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> sub baseProductDir
</span><span class="lines">@@ -848,6 +874,12 @@
</span><span class="cx"> return $baseProductDir;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+sub sourceDir
+{
+ determineSourceDir();
+ return $sourceDir;
+}
+
</ins><span class="cx"> sub productDir
</span><span class="cx"> {
</span><span class="cx"> determineConfigurationProductDir();
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlLoadAsModulepm"></a>
<div class="addfile"><h4>Added: trunk/Tools/Scripts/webkitperl/LoadAsModule.pm (0 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/LoadAsModule.pm (rev 0)
+++ trunk/Tools/Scripts/webkitperl/LoadAsModule.pm 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -0,0 +1,80 @@
</span><ins>+#!/usr/bin/env perl
+#
+# Copyright (C) 2011 Apple Inc. All rights reserved.
+# Copyright (C) 2011 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Imports Perl scripts into a package for easy unit testing.
+
+package LoadAsModule;
+
+use strict;
+use warnings;
+
+use File::Spec;
+use FindBin;
+use lib File::Spec->catdir($FindBin::Bin, "..", "..");
+use webkitdirs;
+
+use base 'Exporter';
+use vars qw(@EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
+
+@EXPORT = ();
+@EXPORT_OK = ();
+%EXPORT_TAGS = ();
+$VERSION = '1.0';
+
+sub readFile($);
+
+sub import
+{
+ my ($self, $package, $script) = @_;
+ my $scriptPath = File::Spec->catfile(sourceDir(), "Tools", "Scripts", $script);
+ eval "
+ package $package;
+
+ use strict;
+ use warnings;
+
+ use base 'Exporter';
+ use vars qw(\@EXPORT \@EXPORT_OK \%EXPORT_TAGS \$VERSION);
+
+ \@EXPORT = ();
+ \@EXPORT_OK = ();
+ \%EXPORT_TAGS = ();
+ \$VERSION = '1.0';
+
+ sub {" . readFile($scriptPath) . "}
+ ";
+}
+
+sub readFile($)
+{
+ my $path = shift;
+ local $/ = undef; # Read in the whole file at once.
+ open FILE, "<", $path or die "Cannot open $path: $!";
+ my $contents = <FILE>;
+ close FILE;
+ return $contents;
+};
+
+1;
</ins></span></pre></div>
<a id="trunkToolsScriptswebkitperlchangelogpm"></a>
<div class="delfile"><h4>Deleted: trunk/Tools/Scripts/webkitperl/changelog.pm (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/changelog.pm 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/changelog.pm 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -1,1525 +0,0 @@
</span><del>-#!/usr/bin/env perl
-
-# Copyright (C) 2007 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# 3. Neither the name of Apple Inc. ("Apple") nor the names of
-# its contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package webkitperl::changelog;
-
-use strict;
-use warnings;
-use Carp;
-use File::Spec;
-use VCSUtils;
-
-BEGIN {
- use Exporter ();
- our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
- $VERSION = 1.00;
- @ISA = qw(Exporter);
- @EXPORT = qw(
- actuallyGenerateFunctionLists
- attributeCommand
- extractLineRangeAfterChange
- extractLineRangeBeforeChange
- fetchBugDescriptionFromBugXMLData
- fetchRadarURLFromBugXMLData
- );
- %EXPORT_TAGS = ();
- @EXPORT_OK = qw(
- get_function_line_ranges_for_cpp
- get_selector_line_ranges_for_css
- get_function_line_ranges_for_java
- get_function_line_ranges_for_javascript
- get_function_line_ranges_for_perl
- get_function_line_ranges_for_python
- get_function_line_ranges_for_swift
- );
-}
-
-use constant SVN => "svn";
-use constant GIT => "git";
-
-sub actuallyGenerateFunctionLists {
- my ($changedFiles, $functionLists, $gitCommit, $gitIndex, $mergeBase, $delegateHashRef) = @_;
-
- my %line_ranges_after_changed;
- my %line_ranges_before_changed;
- if (@$changedFiles) {
- # For each file, build a list of modified lines.
- # Use line numbers from the "after" side of each diff.
- print STDERR " Reviewing diff to determine which lines changed.\n";
- my $file;
- my $diffFileHandle = $delegateHashRef->{openDiff}($changedFiles, $gitCommit, $gitIndex, $mergeBase);
- if (!$diffFileHandle) {
- die "The diff failed: $!.\n";
- }
- while (<$diffFileHandle>) {
- my $filePath = parseDiffStartLine($_);
- $file = $delegateHashRef->{normalizePath}($filePath) if $filePath;
- if (defined $file) {
- my ($before_start, $before_end) = extractLineRangeBeforeChange($_);
- if ($before_start >= 1 && $before_end >= 1) {
- push @{$line_ranges_before_changed{$file}}, [ $before_start, $before_end ];
- } elsif (/DO_NOT_COMMIT/) {
- print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n";
- }
- my ($after_start, $after_end) = extractLineRangeAfterChange($_);
- if ($after_start >= 1 && $after_end >= 1) {
- push @{$line_ranges_after_changed{$file}}, [ $after_start, $after_end ];
- } elsif (/DO_NOT_COMMIT/) {
- print STDERR "WARNING: file $file contains the string DO_NOT_COMMIT, line $.\n";
- }
- }
- }
- close($diffFileHandle);
- }
-
- # For each source file, convert line range to function list.
- print STDERR " Extracting affected function names from source files.\n";
- my %filesToExamine = map { $_ => 1 } (keys(%line_ranges_before_changed), keys(%line_ranges_after_changed));
- foreach my $file (keys %filesToExamine) {
- my %saw_function;
-
- # Find all the functions in the file.
- my $sourceFileHandle = $delegateHashRef->{openFile}($file);
- next unless $sourceFileHandle;
- my @afterChangeFunctionRanges = get_function_line_ranges($sourceFileHandle, $file);
- close($sourceFileHandle);
-
- # Find modified functions in the file.
- if ($line_ranges_after_changed{$file}) {
- my @change_ranges = (@{$line_ranges_after_changed{$file}}, []);
- my @functions = computeModifiedFunctions($file, \@change_ranges, \@afterChangeFunctionRanges);
-
- # Format the list of functions.
- if (@functions) {
- $functionLists->{$file} = "" if !defined $functionLists->{$file};
- $functionLists->{$file} .= "\n (" . join("):\n (", @functions) . "):";
- }
- }
- # Find the deleted functions in the original file.
- if ($line_ranges_before_changed{$file}) {
- my $originalFileHandle = $delegateHashRef->{openOriginalFile}($file, $gitCommit, $gitIndex, $mergeBase);
- next unless $originalFileHandle;
- my @beforeChangeFunctionRanges = get_function_line_ranges($originalFileHandle, $file);
- close($originalFileHandle);
-
- my %existsAfterChange = map { $_->[2] => 1 } @afterChangeFunctionRanges;
-
- my @functions;
- my %sawFunctions;
- for my $functionRange (@beforeChangeFunctionRanges) {
- my $functionName = $functionRange->[2];
- if (!$existsAfterChange{$functionName} && !$sawFunctions{$functionName}) {
- push @functions, $functionName;
- $sawFunctions{$functionName} = 1;
- }
- }
-
- # Format the list of deleted functions.
- if (@functions) {
- $functionLists->{$file} = "" if !defined $functionLists->{$file};
- $functionLists->{$file} .= "\n (" . join("): Deleted.\n (", @functions) . "): Deleted.";
- }
- }
- }
-}
-
-sub extractLineRangeAfterChange {
- my $string = shift;
- my $chunkRange = parseChunkRange($string);
- if (!$chunkRange) {
- return (-1, -1); # Malformed
- }
- if (!$chunkRange->{newStartingLine} || !$chunkRange->{newLineCount}) {
- # Deletion; no lines exist after change.
- return ($chunkRange->{newStartingLine}, $chunkRange->{newStartingLine});
- }
- return ($chunkRange->{newStartingLine}, $chunkRange->{newStartingLine} + $chunkRange->{newLineCount} - 1);
-}
-
-sub extractLineRangeBeforeChange {
- my $string = shift;
- my $chunkRange = parseChunkRange($string);
- if (!$chunkRange) {
- return (-1, -1); # Malformed
- }
- if (!$chunkRange->{startingLine} || !$chunkRange->{lineCount}) {
- # Addition; no lines existed before change.
- return ($chunkRange->{startingLine}, $chunkRange->{startingLine});
- }
- return ($chunkRange->{startingLine}, $chunkRange->{startingLine} + $chunkRange->{lineCount} - 1);
-}
-
-sub attributeCommand {
- my ($attributeCache, $file, $attr) = @_;
-
- my $result;
- if (isSVN()) {
- my $devNull = File::Spec->devnull();
- my $foundAttribute = 0;
- my $subPath = ".";
- my (@directoryParts) = File::Spec->splitdir($file);
- foreach my $part (@directoryParts) {
- if ($part eq ".") {
- next;
- }
- $subPath = File::Spec->join($subPath, $part);
- $subPath =~ s/^\.\///;
- if ($foundAttribute || exists $attributeCache->{$attr}{$subPath} && $attributeCache->{$attr}{$subPath} eq "1") {
- $attributeCache->{$attr}{$subPath} = "1";
- $foundAttribute = 1;
- next;
- }
- my $command = SVN . " propget $attr '$subPath'";
- my $attrib = $attributeCache->{$attr}{$subPath} || `$command 2> $devNull`;
- chomp $attrib;
- if ($attrib eq "1") {
- $foundAttribute = 1;
- }
- $attributeCache->{$attr}{$subPath} = $attrib || "0";
- }
- $result = $attributeCache->{$attr}{$file};
- } elsif (isGit()) {
- my $command = GIT . " check-attr $attr -- '$file'";
- $result = `$command`;
- chomp $result;
- $result =~ s/.*\W(\w)/$1/;
- }
-
- $result =~ s/\D//g;
- return int($result || 0);
-}
-
-sub fetchBugDescriptionFromBugXMLData {
- my ($bugURL, $bugNumber, $bugXMLData) = @_;
-
- if ($bugXMLData !~ /<short_desc>(.*)<\/short_desc>/) {
- print STDERR " Bug $bugNumber has no bug description. Maybe you set wrong bug ID?\n";
- print STDERR " The bug URL: $bugURL\n";
- exit 1;
- }
-
- my $bugDescription = decodeEntities($1);
- print STDERR " Description from bug $bugNumber:\n \"$bugDescription\".\n";
- return $bugDescription;
-}
-
-sub fetchRadarURLFromBugXMLData {
- my ($bugNumber, $bugXMLData) = @_;
-
- return "" if $bugXMLData !~ m|<thetext>\s*((<)?rdar://(problem/)?\d+(>)?)|;
-
- my $bugRadarURL = decodeEntities($1);
- print STDERR " Radar URL from bug $bugNumber:\n \"$bugRadarURL\".\n";
- return $bugRadarURL;
-}
-
-sub computeModifiedFunctions {
- my ($file, $changedLineRanges, $functionRanges) = @_;
-
- my %sawFunction;
-
- # Find all the modified functions.
- my @functions;
- my @change_ranges = @{$changedLineRanges};
- my @change_range = (0, 0);
- FUNCTION: foreach my $function_range_ref (@{$functionRanges}) {
- my @function_range = @{$function_range_ref};
-
- # FIXME: This is a hack. If the function name is empty, skip it.
- # The cpp, python, javascript, perl, css and java parsers
- # are not perfectly implemented and sometimes function names cannot be retrieved
- # correctly. As you can see in get_function_line_ranges_XXXX(), those parsers
- # are not intended to implement real parsers but intended to just retrieve function names
- # for most practical syntaxes.
- next unless $function_range[2];
-
- # Advance to successive change ranges.
- for (;; @change_range = @{shift @change_ranges}) {
- last FUNCTION unless @change_range;
-
- # If past this function, move on to the next one.
- next FUNCTION if $change_range[0] > $function_range[1];
-
- # If an overlap with this function range, record the function name.
- if ($change_range[1] >= $function_range[0]
- and $change_range[0] <= $function_range[1]) {
- if (!$sawFunction{$function_range[2]}) {
- $sawFunction{$function_range[2]} = 1;
- push @functions, $function_range[2];
- }
- next FUNCTION;
- }
- }
- }
-
- return @functions;
-}
-
-sub get_function_line_ranges {
- my ($file_handle, $file_name) = @_;
-
- # Try to determine the source language based on the file extension.
-
- return get_function_line_ranges_for_cpp($file_handle, $file_name) if $file_name =~ /\.(c|cpp|m|mm|h)$/;
- return get_function_line_ranges_for_java($file_handle, $file_name) if $file_name =~ /\.java$/;
- return get_function_line_ranges_for_javascript($file_handle, $file_name) if $file_name =~ /\.js$/;
- return get_selector_line_ranges_for_css($file_handle, $file_name) if $file_name =~ /\.css$/;
- return get_function_line_ranges_for_perl($file_handle, $file_name) if $file_name =~ /\.p[lm]$/;
- return get_function_line_ranges_for_python($file_handle, $file_name) if $file_name =~ /\.py$/ or $file_name =~ /master\.cfg$/;
- return get_function_line_ranges_for_swift($file_handle, $file_name) if $file_name =~ /\.swift$/;
-
- # Try to determine the source language based on the script interpreter.
-
- my $first_line = <$file_handle>;
- seek($file_handle, 0, 0);
-
- return () unless $first_line =~ m|^#!(?:/usr/bin/env\s+)?(\S+)|;
- my $interpreter = $1;
-
- return get_function_line_ranges_for_perl($file_handle, $file_name) if $interpreter =~ /perl$/;
- return get_function_line_ranges_for_python($file_handle, $file_name) if $interpreter =~ /python$/;
-
- return ();
-}
-
-# Read a file and get all the line ranges of the things that look like C functions.
-# A function name is the last word before an open parenthesis before the outer
-# level open brace. A function starts at the first character after the last close
-# brace or semicolon before the function name and ends at the close brace.
-# Comment handling is simple-minded but will work for all but pathological cases.
-#
-# Result is a list of triples: [ start_line, end_line, function_name ].
-
-sub get_function_line_ranges_for_cpp {
- my ($file_handle, $file_name) = @_;
-
- my @ranges;
-
- my $in_comment = 0;
- my $in_macro = 0;
- my $in_method_declaration = 0;
- my $in_parentheses = 0;
- my $quotation_mark;
- my $in_braces = 0;
- my $in_toplevel_array_brace = 0;
- my $brace_start = 0;
- my $brace_end = 0;
- my $namespace_start = -1;
- my $skip_til_brace_or_semicolon = 0;
- my $equal_observed = 0;
-
- my $word = "";
- my $interface_name = "";
-
- my $potential_method_char = "";
- my $potential_method_spec = "";
-
- my $potential_start = 0;
- my $potential_name = "";
-
- my $start = 0;
- my $name = "";
-
- my $next_word_could_be_namespace = 0;
- my $potential_namespace = "";
- my @namespaces;
- my @all_namespaces;
-
- while (<$file_handle>) {
- # Handle continued quoted string.
- if ($quotation_mark) {
- if (!s-([^\\]|\\.)*$quotation_mark--) {
- if (!m-\\$-) {
- warn "mismatched quotes at line $. in $file_name\n";
- undef $quotation_mark;
- }
- next;
- }
- undef $quotation_mark;
- }
-
- # Handle continued multi-line comment.
- if ($in_comment) {
- next unless s-.*\*/--;
- $in_comment = 0;
- }
-
- # Handle continued macro.
- if ($in_macro) {
- $in_macro = 0 unless /\\$/;
- next;
- }
-
- # Handle start of macro (or any preprocessor directive).
- if (/^\s*\#/) {
- $in_macro = 1 if /^([^\\]|\\.)*\\$/;
- next;
- }
-
- # Handle comments and quoted text.
- while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy
- my $match = $1;
- if ($match eq "/*") {
- if (!s-/\*.*?\*/--) {
- s-/\*.*--;
- $in_comment = 1;
- }
- } elsif ($match eq "//") {
- s-//.*--;
- } else { # ' or "
- if (!s-$match([^\\]|\\.)*?$match--) {
- if (!s-$match.*\\$--) {
- warn "mismatched quotes at line $. in $file_name\n";
- s-$match.*--;
- } else {
- $quotation_mark = $match;
- }
- }
- }
- }
-
-
- # continued method declaration
- if ($in_method_declaration) {
- my $original = $_;
- my $method_cont = $_;
-
- chomp $method_cont;
- $method_cont =~ s/[;\{].*//;
- $potential_method_spec = "${potential_method_spec} ${method_cont}";
-
- $_ = $original;
- if (/;/) {
- $potential_start = 0;
- $potential_method_spec = "";
- $potential_method_char = "";
- $in_method_declaration = 0;
- s/^[^;\{]*//;
- } elsif (/{/) {
- my $selector = method_decl_to_selector ($potential_method_spec);
- $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]";
-
- $potential_method_spec = "";
- $potential_method_char = "";
- $in_method_declaration = 0;
-
- $_ = $original;
- s/^[^;{]*//;
- } elsif (/\@end/) {
- $in_method_declaration = 0;
- $interface_name = "";
- $_ = $original;
- } else {
- next;
- }
- }
-
-
- # start of method declaration
- if ((my $method_char, my $method_spec) = m&^([-+])([^0-9;][^;]*);?$&) {
- my $original = $_;
-
- if ($interface_name) {
- chomp $method_spec;
- $method_spec =~ s/\{.*//;
-
- $potential_method_char = $method_char;
- $potential_method_spec = $method_spec;
- $potential_start = $.;
- $in_method_declaration = 1;
- } else {
- warn "declaring a method but don't have interface on line $. in $file_name\n";
- }
- $_ = $original;
- if (/\{/) {
- my $selector = method_decl_to_selector ($potential_method_spec);
- $potential_name = "${potential_method_char}\[${interface_name} ${selector}\]";
-
- $potential_method_spec = "";
- $potential_method_char = "";
- $in_method_declaration = 0;
- $_ = $original;
- s/^[^{]*//;
- } elsif (/\@end/) {
- $in_method_declaration = 0;
- $interface_name = "";
- $_ = $original;
- } else {
- next;
- }
- }
-
-
- # Find function, interface and method names.
- while (m&((?:[[:word:]]+::)*operator(?:[ \t]*\(\)|[^()]*)|[[:word:]<>:~]+|[(){}:;=])|\@(?:implementation|interface|protocol)\s+(\w+)[^{]*&g) {
- # Skip an array definition at the top level.
- # e.g. static int arr[] = { 1, 2, 3 };
- if ($1) {
- if ($1 eq "=" and !$in_parentheses and !$in_braces) {
- $equal_observed = 1;
- } elsif ($1 eq "{" and $equal_observed) {
- # This '{' is the beginning of an array definition, not the beginning of a method.
- $in_toplevel_array_brace = 1;
- $in_braces++;
- $equal_observed = 0;
- next;
- } elsif ($1 !~ /[ \t]/) {
- $equal_observed = 0;
- }
- }
-
- # interface name
- if ($2) {
- $interface_name = $2;
- next;
- }
-
- # Open parenthesis.
- if ($1 eq "(") {
- $potential_name = $word unless $in_parentheses || $skip_til_brace_or_semicolon || grep { $word eq $_ } ("CF_ENUM", "CF_OPTIONS", "NS_ENUM", "NS_OPTIONS");
- $in_parentheses++;
- next;
- }
-
- # Close parenthesis.
- if ($1 eq ")") {
- $in_parentheses--;
- next;
- }
-
- if ($1 eq "const" and !$in_parentheses) {
- $potential_name .= " const";
- next;
- }
-
- if ($1 eq "volatile" and !$in_parentheses) {
- $potential_name .= " volatile";
- next;
- }
-
- # C++ auto function() -> type
- if ($1 eq ">") {
- $skip_til_brace_or_semicolon = 1 unless ($in_parentheses || $in_braces);
- next;
- }
-
- # C++ constructor initializers
- if ($1 eq ":") {
- $skip_til_brace_or_semicolon = 1 unless ($in_parentheses || $in_braces);
- }
-
- # Open brace.
- if ($1 eq "{") {
- $skip_til_brace_or_semicolon = 0;
-
- if (!$in_braces) {
- if ($namespace_start >= 0 and $namespace_start < $potential_start) {
- push @ranges, [ $namespace_start . "", $potential_start - 1, $name ];
- }
-
- if ($potential_namespace) {
- push @namespaces, $potential_namespace;
- push @all_namespaces, $potential_namespace;
- $potential_namespace = "";
- $name = $namespaces[-1];
- $namespace_start = $. + 1;
- next;
- }
-
- # Promote potential name to real function name at the
- # start of the outer level set of braces (function body?).
- if ($potential_start) {
- $start = $potential_start;
- $name = $potential_name;
- if (@namespaces && $name && (length($name) < 2 || substr($name,1,1) ne "[")) {
- $name = join ('::', @namespaces, $name);
- }
- }
- }
-
- $in_method_declaration = 0;
-
- $brace_start = $. if (!$in_braces);
- $in_braces++;
- next;
- }
-
- # Close brace.
- if ($1 eq "}") {
- if (!$in_braces && @namespaces) {
- if ($namespace_start >= 0 and $namespace_start < $.) {
- push @ranges, [ $namespace_start . "", $. - 1, $name ];
- }
-
- pop @namespaces;
- if (@namespaces) {
- $name = $namespaces[-1];
- $namespace_start = $. + 1;
- } else {
- $name = "";
- $namespace_start = -1;
- }
- next;
- }
-
- $in_braces--;
- $brace_end = $. if (!$in_braces);
-
- # End of an outer level set of braces.
- # This could be a function body.
- if (!$in_braces and $name) {
- # This is the end of an array definition at the top level, not the end of a method.
- if ($in_toplevel_array_brace) {
- $in_toplevel_array_brace = 0;
- next;
- }
-
- push @ranges, [ $start, $., $name ];
- if (@namespaces) {
- $name = $namespaces[-1];
- $namespace_start = $. + 1;
- } else {
- $name = "";
- $namespace_start = -1;
- }
- }
-
- $potential_start = 0;
- $potential_name = "";
- next;
- }
-
- # Semicolon.
- if ($1 eq ";") {
- $skip_til_brace_or_semicolon = 0;
- $potential_start = 0;
- $potential_name = "";
- $in_method_declaration = 0;
- next;
- }
-
- # Ignore "const" method qualifier.
- if ($1 eq "const") {
- next;
- }
-
- if ($1 eq "namespace" || $1 eq "class" || $1 eq "struct") {
- $next_word_could_be_namespace = 1;
- next;
- }
-
- # Word.
- $word = $1;
- if (!$skip_til_brace_or_semicolon) {
- if ($next_word_could_be_namespace) {
- $potential_namespace = $word;
- $next_word_could_be_namespace = 0;
- } elsif ($potential_namespace) {
- $potential_namespace = "";
- }
-
- if (!$in_parentheses) {
- $potential_start = 0;
- $potential_name = "";
- }
- if (!$potential_start) {
- $potential_start = $.;
- $potential_name = "";
- }
- }
- }
- }
-
- warn "missing close braces in $file_name (probable start at $brace_start)\n" if ($in_braces > 0);
- warn "too many close braces in $file_name (probable start at $brace_end)\n" if ($in_braces < 0);
-
- warn "mismatched parentheses in $file_name\n" if $in_parentheses;
-
- return delete_namespaces_from_ranges_for_cpp(\@ranges, \@all_namespaces);
-}
-
-
-# Take in references to an array of line ranges for C functions in a given file
-# and an array of namespaces declared in that file and return an updated
-# list of line ranges with the namespaces removed.
-
-sub delete_namespaces_from_ranges_for_cpp {
- my ($ranges_ref, $namespaces_ref) = @_;
- return grep {!is_function_in_namespace($namespaces_ref, $$_[2])} @$ranges_ref;
-}
-
-
-sub is_function_in_namespace {
- my ($namespaces_ref, $function_name) = @_;
- return grep {$_ eq $function_name} @$namespaces_ref;
-}
-
-
-# Read a file and get all the line ranges of the things that look like Java
-# classes, interfaces and methods.
-#
-# A class or interface name is the word that immediately follows
-# `class' or `interface' when followed by an open curly brace and not
-# a semicolon. It can appear at the top level, or inside another class
-# or interface block, but not inside a function block
-#
-# A class or interface starts at the first character after the first close
-# brace or after the function name and ends at the close brace.
-#
-# A function name is the last word before an open parenthesis before
-# an open brace rather than a semicolon. It can appear at top level or
-# inside a class or interface block, but not inside a function block.
-#
-# A function starts at the first character after the first close
-# brace or after the function name and ends at the close brace.
-#
-# Comment handling is simple-minded but will work for all but pathological cases.
-#
-# Result is a list of triples: [ start_line, end_line, function_name ].
-
-sub get_function_line_ranges_for_java {
- my ($file_handle, $file_name) = @_;
-
- my @current_scopes;
-
- my @ranges;
-
- my $in_comment = 0;
- my $in_macro = 0;
- my $in_parentheses = 0;
- my $in_braces = 0;
- my $in_non_block_braces = 0;
- my $class_or_interface_just_seen = 0;
- my $in_class_declaration = 0;
-
- my $word = "";
-
- my $potential_start = 0;
- my $potential_name = "";
- my $potential_name_is_class_or_interface = 0;
-
- my $start = 0;
- my $name = "";
- my $current_name_is_class_or_interface = 0;
-
- while (<$file_handle>) {
- # Handle continued multi-line comment.
- if ($in_comment) {
- next unless s-.*\*/--;
- $in_comment = 0;
- }
-
- # Handle continued macro.
- if ($in_macro) {
- $in_macro = 0 unless /\\$/;
- next;
- }
-
- # Handle start of macro (or any preprocessor directive).
- if (/^\s*\#/) {
- $in_macro = 1 if /^([^\\]|\\.)*\\$/;
- next;
- }
-
- # Handle comments and quoted text.
- while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy
- my $match = $1;
- if ($match eq "/*") {
- if (!s-/\*.*?\*/--) {
- s-/\*.*--;
- $in_comment = 1;
- }
- } elsif ($match eq "//") {
- s-//.*--;
- } else { # ' or "
- if (!s-$match([^\\]|\\.)*?$match--) {
- warn "mismatched quotes at line $. in $file_name\n";
- s-$match.*--;
- }
- }
- }
-
- # Find function names.
- while (m-(\w+|[(){};])-g) {
- # Open parenthesis.
- if ($1 eq "(") {
- if (!$in_parentheses) {
- $potential_name = $word;
- $potential_name_is_class_or_interface = 0;
- }
- $in_parentheses++;
- next;
- }
-
- # Close parenthesis.
- if ($1 eq ")") {
- $in_parentheses--;
- next;
- }
-
- # Open brace.
- if ($1 eq "{") {
- $in_class_declaration = 0;
-
- # Promote potential name to real function name at the
- # start of the outer level set of braces (function/class/interface body?).
- if (!$in_non_block_braces
- and (!$in_braces or $current_name_is_class_or_interface)
- and $potential_start) {
- if ($name) {
- push @ranges, [ $start, ($. - 1),
- join ('.', @current_scopes) ];
- }
-
-
- $current_name_is_class_or_interface = $potential_name_is_class_or_interface;
-
- $start = $potential_start;
- $name = $potential_name;
-
- push (@current_scopes, $name);
- } else {
- $in_non_block_braces++;
- }
-
- $potential_name = "";
- $potential_start = 0;
-
- $in_braces++;
- next;
- }
-
- # Close brace.
- if ($1 eq "}") {
- $in_braces--;
-
- # End of an outer level set of braces.
- # This could be a function body.
- if (!$in_non_block_braces) {
- if ($name) {
- push @ranges, [ $start, $.,
- join ('.', @current_scopes) ];
-
- pop (@current_scopes);
-
- if (@current_scopes) {
- $current_name_is_class_or_interface = 1;
-
- $start = $. + 1;
- $name = $current_scopes[$#current_scopes-1];
- } else {
- $current_name_is_class_or_interface = 0;
- $start = 0;
- $name = "";
- }
- }
- } else {
- $in_non_block_braces-- if $in_non_block_braces;
- }
-
- $potential_start = 0;
- $potential_name = "";
- next;
- }
-
- # Semicolon.
- if ($1 eq ";") {
- $potential_start = 0;
- $potential_name = "";
- next;
- }
-
- if ($1 eq "class") {
- $in_class_declaration = 1;
- }
- if ($1 eq "class" or (!$in_class_declaration and $1 eq "interface")) {
- $class_or_interface_just_seen = 1;
- next;
- }
-
- # Word.
- $word = $1;
- if (!$in_parentheses) {
- if ($class_or_interface_just_seen) {
- $potential_name = $word;
- $potential_start = $.;
- $class_or_interface_just_seen = 0;
- $potential_name_is_class_or_interface = 1;
- next;
- }
- }
- if (!$potential_start) {
- $potential_start = $.;
- $potential_name = "";
- }
- $class_or_interface_just_seen = 0;
- }
- }
-
- warn "mismatched braces in $file_name\n" if $in_braces;
- warn "mismatched parentheses in $file_name\n" if $in_parentheses;
-
- return @ranges;
-}
-
-
-
-# Read a file and get all the line ranges of the things that look like
-# JavaScript functions or methods.
-#
-# A function name is the word that immediately follows `function' when
-# followed by an open curly brace. It can appear at the top level,
-# or inside other functions. For example:
-#
-# function name() { // (name)
-# function inner() { } // (name.inner)
-# }
-#
-# An anonymous function name is the identifier on the left hand side of
-# an assignment with the equals operator or object notation that has a
-# value starting with `function' followed an open curly brace.
-# For example:
-#
-# namespace = {
-# name: function() {} // (namespace.name)
-# }
-# namespace.Foo = function() {} // (namespace.Foo)
-#
-# A getter or setter name is the word that immediately follows `get' or
-# `set' when followed by params and an open curly brace. For example:
-#
-# namespace = {
-# get foo() {} // (namespace.get foo)
-# }
-#
-# A method name is the word immediately before parenthesis, with an open
-# curly brace immediately following closing parenthesis. For a class expression
-# we take the assignment identifier instead of the class name for namespacing.
-#
-# namespace.Foo = class DoesNotMatter extends Bar {
-# constructor() {} // (namespace.Foo)
-# static staticMethod() {} // (namespace.Foo.staticMethod)
-# instanceMethod() {} // (namespace.Foo.prototype.instanceMethod)
-# get getter() {} // (namespace.Foo.prototype.get getter)
-# }
-# class ClassName {
-# constructor() {} // (ClassName)
-# method() {} // (ClassName.prototype.method)
-# }
-#
-# Methods may exist in object literals, outside of classes.
-#
-# Foo.prototype = {
-# method() {}, // (Foo.prototype.method)
-# otherMethod() {} // (Foo.prototype.otherMethod)
-# }
-#
-# Comment handling is simple-minded but will work for all but pathological cases.
-#
-# Result is a list of triples: [ start_line, end_line, function_name ].
-
-sub get_function_line_ranges_for_javascript {
- my ($fileHandle, $fileName) = @_;
-
- my @currentScopes;
- my @currentIdentifiers;
- my @currentParsingMode = ("global");
- my @currentFunctionNames;
- my @currentFunctionDepths;
- my @currentFunctionStartLines;
-
- my @ranges;
-
- my $inComment = 0;
- my $inQuotedText = "";
- my $inExtends = 0;
- my $inMethod = 0;
- my $inAnonymousFunctionParameters = 0;
- my $parenthesesDepth = 0;
- my $globalParenthesesDepth = 0;
- my $bracesDepth = 0;
-
- my $classJustSeen = 0;
- my $parenthesisJustSeen = 0;
- my $functionJustSeen = 0;
- my $getterJustSeen = 0;
- my $setterJustSeen = 0;
- my $asyncJustSeen = 0;
- my $assignmentJustSeen = 0;
- my $staticOrContructorSeen = 0;
-
- my $currentToken = "";
- my $lastToken = "";
- my $possibleMethodName = "";
- my $word = "";
-
- while (<$fileHandle>) {
- # Handle continued multi-line comment.
- if ($inComment) {
- next unless s-.*\*/--;
- $inComment = 0;
- }
-
- # Handle continued quoted text.
- if ($inQuotedText ne "") {
- next if /\\$/;
- s-([^\\]|\\.)*?$inQuotedText--;
- $inQuotedText = "";
- }
-
- # Handle comments and quoted text.
- while (m-(/\*|//|\'|\")-) { # \' and \" keep emacs perl mode happy
- my $match = $1;
- if ($match eq '/*') {
- if (!s-/\*.*?\*/--) {
- s-/\*.*--;
- $inComment = 1;
- }
- } elsif ($match eq '//') {
- s-//.*--;
- } else { # ' or "
- if (!s-$match([^\\]|\\.)*?$match-string_appeared_here-) {
- $inQuotedText = $match if /\\$/;
- warn "mismatched quotes at line $. in $fileName\n" if $inQuotedText eq "";
- s-$match.*--;
- }
- }
- }
-
- # Find function names.
- while (m-(\w+|[(){}=:;,.])-g) {
- # Skip everything until "{" after extends.
- if ($inExtends) {
- next if $1 ne '{';
- $inExtends = 0;
- }
-
- $lastToken = $currentToken;
- $currentToken = $1;
-
- # Open parenthesis.
- if ($1 eq '(') {
- $parenthesesDepth++;
- $globalParenthesesDepth++ if $currentParsingMode[$#currentParsingMode] eq "global";
- $possibleMethodName = join('.', @currentIdentifiers);
- $inAnonymousFunctionParameters = 1 if $functionJustSeen;
- $functionJustSeen = 0;
- next;
- }
-
- # Close parenthesis.
- if ($1 eq ')') {
- $parenthesesDepth--;
- $globalParenthesesDepth-- if $currentParsingMode[$#currentParsingMode] eq "global";
- @currentIdentifiers = () if $inAnonymousFunctionParameters;
- $inAnonymousFunctionParameters = 0;
- $parenthesisJustSeen = 1;
- next;
- }
-
- # Open brace.
- if ($1 eq '{') {
- my $methodName = "";
- my $mode = $currentParsingMode[$#currentParsingMode];
-
- # Method.
- if (($mode eq 'class' or $mode eq 'global') and $parenthesisJustSeen and ($staticOrContructorSeen or $possibleMethodName)) {
- if ($mode eq 'class') {
- $methodName = join('.', $staticOrContructorSeen ? "" : "prototype", $possibleMethodName);
- } else {
- $methodName = $possibleMethodName;
- }
-
- $methodName =~ s/\.{2,}/\./g; # Removes consecutive periods.
- $methodName =~ s/\.$//; # Remove trailing period.
-
- my $currentMethod = join('.', @currentScopes, $methodName);
- $currentMethod =~ s/\.{2,}/\./g; # Removes consecutive periods.
- $currentMethod =~ s/\.$//; # Remove trailing period.
-
- push(@currentParsingMode, "method");
- push(@currentFunctionNames, $currentMethod);
- push(@currentFunctionDepths, $bracesDepth);
- push(@currentFunctionStartLines, $.);
- }
-
- $bracesDepth++;
- $functionJustSeen = 0;
-
- push(@currentScopes, join('.', $methodName ? $methodName : @currentIdentifiers));
- @currentIdentifiers = ();
-
- $staticOrContructorSeen = 0;
- next;
- }
-
- # Close brace.
- if ($1 eq '}') {
- $bracesDepth--;
- $functionJustSeen = 0;
-
- if (@currentFunctionDepths and $bracesDepth == $currentFunctionDepths[$#currentFunctionDepths]) {
- pop(@currentFunctionDepths);
- pop(@currentParsingMode);
-
- my $currentName = pop(@currentFunctionNames);
- my $start = pop(@currentFunctionStartLines);
-
- $currentName =~ s/^\.//g; # Removes leading periods.
-
- push(@ranges, [$start, $., $currentName]);
- }
-
- pop(@currentScopes);
- @currentIdentifiers = ();
-
- next;
- }
-
- # Dot.
- if ($1 eq '.') {
- next;
- }
-
- # Semicolon or comma.
- if ($1 eq ';' or $1 eq ',') {
- @currentIdentifiers = ();
- next;
- }
-
- # Class.
- if ($1 eq 'class') {
- $classJustSeen = 1;
- next;
- }
-
- # Extends.
- if ($1 eq 'extends') {
- $inExtends = 1;
- next;
- }
-
- # Function.
- if ($1 eq 'function') {
- $functionJustSeen = 1;
-
- if ($assignmentJustSeen) {
- my $currentFunction = join('.', (@currentScopes, @currentIdentifiers));
- $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods.
-
- push(@currentParsingMode, "function");
- push(@currentFunctionNames, $currentFunction);
- push(@currentFunctionDepths, $bracesDepth);
- push(@currentFunctionStartLines, $.);
- }
-
- next;
- }
-
- # Getter prefix.
- if ($1 eq 'get') {
- next if $lastToken eq '.'; # Avoid map.get(...).
- $getterJustSeen = 1;
- next;
- }
-
- # Setter prefix.
- if ($1 eq 'set') {
- next if $lastToken eq '.'; # Avoid map.set(...).
- $setterJustSeen = 1;
- next;
- }
-
- # Async prefix.
- if ($1 eq 'async') {
- next if $lastToken eq '.';
- $asyncJustSeen = 1;
- next;
- }
-
- # Static.
- if ($1 eq 'static' or $1 eq 'constructor') {
- $staticOrContructorSeen = 1;
- next;
- }
-
- # Assignment operator.
- if ($1 eq '=' or $1 eq ':') {
- $assignmentJustSeen = 1;
- next;
- }
-
- next if $parenthesesDepth > $globalParenthesesDepth;
-
- # Word.
- $word = $1;
-
- if ($classJustSeen) {
- push(@currentIdentifiers, $word) if !$assignmentJustSeen;
-
- my $currentClass = join('.', (@currentScopes, @currentIdentifiers));
- $currentClass =~ s/\.{2,}/\./g; # Removes consecutive periods.
-
- push(@currentParsingMode, "class");
- push(@currentFunctionNames, $currentClass);
- push(@currentFunctionDepths, $bracesDepth);
- push(@currentFunctionStartLines, $.);
- } elsif ($getterJustSeen or $setterJustSeen or $asyncJustSeen) {
- $word = "get $word" if $getterJustSeen;
- $word = "set $word" if $setterJustSeen;
- $word = "async $word" if $asyncJustSeen;
-
- push(@currentIdentifiers, $word);
-
- my $mode = $currentParsingMode[$#currentParsingMode];
- my $currentFunction = join('.', (@currentScopes, ($mode eq 'class' and !$staticOrContructorSeen) ? "prototype" : "", @currentIdentifiers));
- $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods.
-
- push(@currentParsingMode, "function");
- push(@currentFunctionNames, $currentFunction);
- push(@currentFunctionDepths, $bracesDepth);
- push(@currentFunctionStartLines, $.);
- } elsif ($functionJustSeen and !$assignmentJustSeen) {
- push(@currentIdentifiers, $word);
-
- my $currentFunction = join('.', (@currentScopes, @currentIdentifiers));
- $currentFunction =~ s/\.{2,}/\./g; # Removes consecutive periods.
-
- push(@currentParsingMode, "function");
- push(@currentFunctionNames, $currentFunction);
- push(@currentFunctionDepths, $bracesDepth);
- push(@currentFunctionStartLines, $.);
- } elsif ($word ne 'if' and $word ne 'for' and $word ne 'do' and $word ne 'while' and $word ne 'which' and $word ne 'var') {
- push(@currentIdentifiers, $word);
- }
-
- $classJustSeen = 0;
- $parenthesisJustSeen = 0;
- $functionJustSeen = 0;
- $getterJustSeen = 0;
- $setterJustSeen = 0;
- $asyncJustSeen = 0;
- $assignmentJustSeen = 0;
- }
- }
-
- warn "mismatched braces in $fileName\n" if $bracesDepth;
- warn "mismatched parentheses in $fileName\n" if $parenthesesDepth;
-
- return @ranges;
-}
-
-# Read a file and get all the line ranges of the things that look like Perl functions. Functions
-# start on a line that starts with "sub ", and end on the first line starting with "}" thereafter.
-#
-# Result is a list of triples: [ start_line, end_line, function ].
-
-sub get_function_line_ranges_for_perl {
- my ($fileHandle, $fileName) = @_;
-
- my @ranges;
-
- my $currentFunction = "";
- my $start = 0;
- my $hereDocumentIdentifier = "";
-
- while (<$fileHandle>) {
- chomp;
- if (!$hereDocumentIdentifier) {
- if (/^sub\s+([\w_][\w\d_]*)/) {
- # Skip over forward declarations, which don't contain a brace and end with a semicolon.
- next if /;\s*$/;
-
- if ($currentFunction) {
- warn "nested functions found at top-level at $fileName:$.\n";
- next;
- }
- $currentFunction = $1;
- $start = $.;
- }
- if (/<<\s*[\"\']?([\w_][\w_\d]*)/) {
- # Enter here-document.
- $hereDocumentIdentifier = $1;
- }
- if (index($_, "}") == 0) {
- next unless $start;
- push(@ranges, [$start, $., $currentFunction]);
- $currentFunction = "";
- $start = 0;
- }
- } elsif ($_ eq $hereDocumentIdentifier) {
- # Escape from here-document.
- $hereDocumentIdentifier = "";
- }
- }
-
- return @ranges;
-}
-
-# Read a file and get all the line ranges of the things that look like Python classes, methods, or functions.
-#
-# FIXME: Maybe we should use Python's ast module to do the parsing for us?
-#
-# Result is a list of triples: [ start_line, end_line, function ].
-
-sub get_function_line_ranges_for_python {
- my ($fileHandle, $fileName) = @_;
-
- my @ranges;
-
- my $multilineStringLiteralSentinelRegEx = qr#(?:"""|''')#;
- my $multilineStringLiteralStartRegEx = qr#\s*$multilineStringLiteralSentinelRegEx#;
- my $multilineStringLiteralEndRegEx = qr#$multilineStringLiteralSentinelRegEx\s*$#;
-
- my @scopeStack = ({ line => 0, indent => -1, name => undef });
- my $lastLine = 0;
- my $inComment = 0;
- until ($lastLine) {
- $_ = <$fileHandle>;
- unless ($_) {
- # To pop out all popped scopes, run the loop once more after
- # we encountered the end of the file.
- $_ = "pass\n";
- $.++;
- $lastLine = 1;
- }
- chomp;
- next unless /^(\s*)([^#].*)$/; # Skip non-indented lines that begin with a comment.
-
- my $indent = length $1;
- my $rest = $2;
- my $scope = $scopeStack[-1];
-
- if ($indent <= $scope->{indent}) {
- # Find all the scopes that we have just exited.
- my $i = 0;
- for (; $i < @scopeStack; ++$i) {
- last if $indent <= $scopeStack[$i]->{indent};
- }
- my @poppedScopes = splice @scopeStack, $i;
-
- # For each scope that was just exited, add a range that goes from the start of that
- # scope to the start of the next nested scope, or to the line just before this one for
- # the innermost scope.
- for ($i = 0; $i < @poppedScopes; ++$i) {
- my $lineAfterEnd = $i + 1 == @poppedScopes ? $. : $poppedScopes[$i + 1]->{line};
- push @ranges, [$poppedScopes[$i]->{line}, $lineAfterEnd - 1, $poppedScopes[$i]->{name}];
- }
- @scopeStack or warn "Popped off last scope at $fileName:$.\n";
-
- # Set the now-current scope to start at the current line. Any lines within this scope
- # before this point should already have been added to @ranges.
- $scope = $scopeStack[-1];
- $scope->{line} = $.;
- }
-
- # Skip multi-line string literals and docstrings.
- next if /$multilineStringLiteralStartRegEx.*$multilineStringLiteralEndRegEx/;
- if (!$inComment && /$multilineStringLiteralStartRegEx/) {
- $inComment = 1;
- } elsif ($inComment && /$multilineStringLiteralEndRegEx/) {
- $inComment = 0;
- }
- next if $inComment;
-
- next if /^\s*[#'"]/; # Skip indented and non-indented lines that begin with a comment or string literal (includes docstrings).
-
- next unless $rest =~ /(?:class|def)\s+(\w+)/;
- my $name = $1;
- my $fullName = $scope->{name} ? join('.', $scope->{name}, $name) : $name;
- push @scopeStack, { line => $., indent => $indent, name => $fullName };
-
- if ($scope->{indent} >= 0) {
- push @ranges, [$scope->{line}, $. - 1, $scope->{name}];
- }
- }
-
- return @ranges;
-}
-
-# Read a file and get all the line ranges of the things that look like CSS selectors. A selector is
-# anything before an opening brace on a line. A selector starts at the line containing the opening
-# brace and ends at the closing brace.
-#
-# Result is a list of triples: [ start_line, end_line, selector ].
-
-sub get_selector_line_ranges_for_css {
- my ($fileHandle, $fileName) = @_;
-
- my @ranges;
-
- my $inComment = 0;
- my $inBrace = 0;
- my @stack;
- my $context;
- my @currentParseMode = ("global");
- my @groupingStack;
- my $selectorBraces = 0;
-
- while (<$fileHandle>) {
- foreach my $token (split m-(\{|\}|/\*|\*/)-, $_) {
- if ($token eq "{") {
- if (!$inComment) {
- $inBrace += 1;
- $selectorBraces += 1 if $currentParseMode[$#currentParseMode] eq "selector";
- warn "mismatched opening brace found in $fileName:$.\n" if $selectorBraces > 1;
- }
- } elsif ($token eq "}") {
- if (!$inComment) {
- if (!$inBrace or $currentParseMode[$#currentParseMode] eq "global") {
- warn "mismatched closing brace found in $fileName:$.\n";
- next;
- }
-
- $inBrace -= 1;
-
- my $parseMode = pop(@currentParseMode);
- if ($parseMode eq "selector") {
- my $name = pop(@stack);
- my $startLine = pop(@stack);
- my $endLine = $.;
- my $groupingPrefix = join(" ", @groupingStack);
- if (length $groupingPrefix) {
- $groupingPrefix .= " "
- }
- push(@ranges, [$startLine, $endLine, $groupingPrefix . $name]);
- } elsif ($parseMode eq "media") {
- pop(@groupingStack);
- }
-
- $selectorBraces = 0;
- }
- } elsif ($token eq "/*") {
- $inComment = 1;
- } elsif ($token eq "*/") {
- warn "mismatched comment found in $fileName:$.\n" if !$inComment;
- $inComment = 0;
- } else {
- if (!$inComment and $currentParseMode[$#currentParseMode] ne "selector" and $token !~ /^[\s\t]*$/) {
- $token =~ s/^[\s\t]*|[\s\t]*$//g;
- my $startLine = $.;
- if ($token =~ /^\@media/) {
- push(@currentParseMode, "media");
- push(@groupingStack, $token);
- } else {
- push(@currentParseMode, "selector");
- push(@stack, ($startLine, $token));
- }
- }
- }
- }
- }
-
- # Sort by start line.
- return sort {$a->[0] <=> $b->[0]} @ranges;
-}
-
-# Read a file and get all the line ranges of the things that look like Swift classes, methods,
-# or functions.
-#
-# Result is a list of triples: [ start_line, end_line, function ].
-
-sub get_function_line_ranges_for_swift {
- my ($fileHandle, $fileName) = @_;
-
- my @ranges;
-
- my $currentFunction = "";
- my $currentType = "";
- my $functionStart = 0;
- my $typeStart = 0;
- my $functionScopeDepth = 0;
- my $typeScopeDepth = 0;
- my $scopeDepth = 0;
-
- while (<$fileHandle>) {
- chomp;
- next if (/^\s*\/\/.*/);
- if (/func\s+([\w_][\w\d_]*)\((.*)\)/ || /var\s+([\w_][\w\d_]*):\s+/) {
- $functionScopeDepth = $scopeDepth;
- $currentFunction = $1;
- if ($2) {
- $currentFunction = "$currentFunction(". parseSwiftFunctionArgs($2) . ")";
- }
- if ($currentType) {
- $currentFunction = "$currentType.$currentFunction";
- }
- $functionStart = $.;
- } elsif (/(?:class|struct|enum|protocol|extension)\s+([\w_][\w\d_]*)/) {
- $typeScopeDepth = $scopeDepth;
- $currentType = $1;
- $typeStart = $.;
- }
- if (index($_, "{") > -1) {
- $scopeDepth++;
- }
- if (index($_, "}") > -1) {
- $scopeDepth--;
- }
- if ($scopeDepth == $functionScopeDepth) {
- next unless $functionStart;
- push(@ranges, [$functionStart, $., $currentFunction]);
- $currentFunction = "";
- $functionStart = 0;
- } elsif ($scopeDepth == $typeScopeDepth) {
- next unless $typeStart;
- $currentType = "";
- $typeStart = 0;
- }
- }
-
- return @ranges;
-}
-
-sub parseSwiftFunctionArgs {
- my $functionArgs = shift;
-
- my @words = split /, /, $functionArgs;
- my $argCount = scalar(@words);
- if ($argCount == 0) {
- return "";
- } elsif ($argCount > 0) {
- # If the first argument is unnamed, give it the name "_"
- $words[0] =~ s/^(\w+: .*)/_ $1/;
- return join("", map { $_ =~ s/^(\w+).*/$1/; "$_:" } @words);
- } else {
- warn "Unknown argument count.\n";
- }
-}
-
-sub decodeEntities {
- my $text = shift;
- $text =~ s/\</</g;
- $text =~ s/\>/>/g;
- $text =~ s/\"/\"/g;
- $text =~ s/\'/\'/g;
- $text =~ s/\&/\&/g;
- return $text;
-}
-
-1;
</del></span></pre></div>
<a id="trunkToolsScriptswebkitperldirspm"></a>
<div class="delfile"><h4>Deleted: trunk/Tools/Scripts/webkitperl/dirs.pm (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/dirs.pm 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/dirs.pm 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -1,81 +0,0 @@
</span><del>-#!/usr/bin/env perl
-
-# Copyright (C) 2007 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# 3. Neither the name of Apple Inc. ("Apple") nor the names of
-# its contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package webkitperl::dirs;
-
-use strict;
-use warnings;
-use Carp;
-use File::Spec;
-
-BEGIN {
- use Exporter ();
- our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
- $VERSION = 1.00;
- @ISA = qw(Exporter);
- @EXPORT = qw(
- sourceDir
- setSourceDir
- );
- %EXPORT_TAGS = ();
- @EXPORT_OK = ();
-}
-
-my $sourceDir;
-
-sub sourceDir
-{
- determineSourceDir();
- return $sourceDir;
-}
-
-# used for scripts which are stored in a non-standard location
-sub setSourceDir($)
-{
- ($sourceDir) = @_;
-}
-
-sub determineSourceDir
-{
- return if $sourceDir;
- $sourceDir = $FindBin::RealBin;
- $sourceDir =~ s|/+$||; # Remove trailing '/' as we would die later
-
- # walks up path checking each directory to see if it is the main WebKit project dir,
- # defined by containing Sources, WebCore, and JavaScriptCore.
- until ((-d File::Spec->catdir($sourceDir, "Source") && -d File::Spec->catdir($sourceDir, "Source", "WebCore") && -d File::Spec->catdir($sourceDir, "Source", "JavaScriptCore")) || (-d File::Spec->catdir($sourceDir, "Internal") && -d File::Spec->catdir($sourceDir, "OpenSource")))
- {
- if ($sourceDir !~ s|/[^/]+$||) {
- croak "Could not find top level webkit directory above source directory using FindBin.";
- }
- }
-
- $sourceDir = File::Spec->catdir($sourceDir, "OpenSource") if -d File::Spec->catdir($sourceDir, "OpenSource");
-}
-
-1;
</del></span></pre></div>
<a id="trunkToolsScriptswebkitperlfilterbuildwebkit_unittestshouldIgnoreLine_unittestspl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/filter-build-webkit_unittest/shouldIgnoreLine_unittests.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/filter-build-webkit_unittest/shouldIgnoreLine_unittests.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/filter-build-webkit_unittest/shouldIgnoreLine_unittests.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -31,11 +31,14 @@
</span><span class="cx"> use English;
</span><span class="cx"> use FindBin;
</span><span class="cx"> use Test::More;
</span><del>-use lib File::Spec->catdir($FindBin::RealBin, "..", "..");
-use webkitperl::build::output qw(shouldIgnoreLine);
</del><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
+use LoadAsModule qw(FilterBuildWebKit filter-build-webkit);
</ins><span class="cx">
</span><span class="cx"> sub description($);
</span><span class="cx">
</span><ins>+@FilterBuildWebKit::EXPORT_OK = qw(shouldIgnoreLine);
+FilterBuildWebKit->import(@FilterBuildWebKit::EXPORT_OK);
+
</ins><span class="cx"> #
</span><span class="cx"> # Test whitespace
</span><span class="cx"> #
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlleakspm"></a>
<div class="delfile"><h4>Deleted: trunk/Tools/Scripts/webkitperl/leaks.pm (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/leaks.pm 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/leaks.pm 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -1,154 +0,0 @@
</span><del>-#!/usr/bin/env perl
-
-# Copyright (C) 2007 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# 3. Neither the name of Apple Inc. ("Apple") nor the names of
-# its contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# Script to run the Mac OS X leaks tool with more expressive '-exclude' lists.
-
-package webkitperl::leaks;
-
-use strict;
-use warnings;
-use Carp;
-
-use File::Basename;
-
-BEGIN {
- use Exporter ();
- our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
- $VERSION = 1.00;
- @ISA = qw(Exporter);
- @EXPORT = qw(
- runLeaks
- parseLeaksOutput
- );
- %EXPORT_TAGS = ();
- @EXPORT_OK = ();
-}
-
-# Returns the output of the leaks tool in list form.
-sub runLeaks($$)
-{
- my ($target, $memgraphPath) = @_;
-
- if (defined $memgraphPath) {
- `/usr/bin/leaks \"$target\" --outputGraph=\"$memgraphPath\"`;
- $target = $memgraphPath;
- }
-
- # To get a result we can parse, we need to pass --list to all versions of the leaks tool
- # that recognize it, but there is no certain way to tell in advance (the version of the
- # tool is not always tied to OS version, and --help doesn't document the option in some
- # of the tool versions where it's supported and needed).
- my @leaksOutput = `/usr/bin/leaks \"$target\" --list`;
-
- # FIXME: Remove the fallback once macOS Mojave is the oldest supported version.
- my $leaksExitCode = $? >> 8;
- if ($leaksExitCode > 1) {
- @leaksOutput = `/usr/bin/leaks \"$target\"`;
- }
-
- return \@leaksOutput;
-}
-
-# Returns a list of hash references with the keys { address, size, type, callStack, leaksOutput }
-sub parseLeaksOutput(\@)
-{
- my ($leaksOutput) = @_;
-
- # Format:
- # Process 00000: 1234 nodes malloced for 1234 KB
- # Process 00000: XX leaks for XXX total leaked bytes.
- # Leak: 0x00000000 size=1234 [instance of 'blah']
- # 0x00000000 0x00000000 0x00000000 0x00000000 a..d.e.e
- # ...
- # Call stack: leak_caller() | leak() | malloc
- #
- # We treat every line except for Process 00000: and Leak: as optional
-
- my $leakCount;
- my @parsedOutput = ();
- my $parsingLeak = 0;
- my $parsedLeakCount = 0;
- for my $line (@$leaksOutput) {
- if ($line =~ /^Process \d+: (\d+) leaks?/) {
- $leakCount = $1;
- }
-
- if ($line eq "\n") {
- $parsingLeak = 0;
- }
-
- if ($line =~ /^Leak: /) {
- $parsingLeak = 1;
- $parsedLeakCount++;
- my ($address) = ($line =~ /Leak: ([[:xdigit:]x]+)/);
- if (!defined($address)) {
- croak "Could not parse Leak address.";
- }
-
- my ($size) = ($line =~ /size=([[:digit:]]+)/);
- if (!defined($size)) {
- croak "Could not parse Leak size.";
- }
-
- # FIXME: This code seems wrong, the leaks tool doesn't actually use single quotes.
- # We should reconcile with other format changes that happened since, such as the
- # addition of zone information.
- my ($type) = ($line =~ /'([^']+)'/); #'
- if (!defined($type)) {
- $type = ""; # The leaks tool sometimes omits the type.
- }
-
- my %leak = (
- "address" => $address,
- "size" => $size,
- "type" => $type,
- "callStack" => "", # The leaks tool sometimes omits the call stack.
- "leaksOutput" => $line
- );
- push(@parsedOutput, \%leak);
- } elsif ($parsingLeak) {
- $parsedOutput[$#parsedOutput]->{"leaksOutput"} .= $line;
- if ($line =~ /Call stack:/) {
- $parsedOutput[$#parsedOutput]->{"callStack"} = $line;
- }
- } else {
- my %nonLeakLine = (
- "leaksOutput" => $line
- );
- push(@parsedOutput, \%nonLeakLine);
- }
- }
-
- if ($parsedLeakCount != $leakCount) {
- croak "Parsed leak count ($parsedLeakCount) does not match leak count reported by leaks tool ($leakCount).";
- }
-
- return \@parsedOutput;
-}
-
-1;
</del></span></pre></div>
<a id="trunkToolsScriptswebkitperlprepareChangeLog_unittestextractLineRangeBeforeAndAfterChangepl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/extractLineRangeBeforeAndAfterChange.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -27,11 +27,8 @@
</span><span class="cx">
</span><span class="cx"> use Test::More;
</span><span class="cx"> use FindBin;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
-use webkitperl::changelog qw(
- extractLineRangeBeforeChange
- extractLineRangeAfterChange
-);
</del><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
+use LoadAsModule qw(PrepareChangeLog prepare-ChangeLog);
</ins><span class="cx">
</span><span class="cx"> my @testCaseHashRefs = (
</span><span class="cx"> ####
</span><span class="lines">@@ -200,10 +197,10 @@
</span><span class="cx"> my $expectedResults = $testCase->{expectedResults};
</span><span class="cx">
</span><span class="cx"> my $testNameStart = "extractLineRangeBeforeChange(): $testCase->{testName}: comparing";
</span><del>- my @got = extractLineRangeBeforeChange($testCase->{inputText});
</del><ins>+ my @got = PrepareChangeLog::extractLineRangeBeforeChange($testCase->{inputText});
</ins><span class="cx"> is_deeply(\@got, $expectedResults->{beforeChange}, "$testNameStart return value.");
</span><span class="cx">
</span><span class="cx"> $testNameStart = "extractLineRangeAfterChange(): $testCase->{testName}: comparing";
</span><del>- @got = extractLineRangeAfterChange($testCase->{inputText});
</del><ins>+ @got = PrepareChangeLog::extractLineRangeAfterChange($testCase->{inputText});
</ins><span class="cx"> is_deeply(\@got, $expectedResults->{afterChange}, "$testNameStart return value.");
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlprepareChangeLog_unittestfetchRadarURLFromBugXMLDatapl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/fetchRadarURLFromBugXMLData.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/fetchRadarURLFromBugXMLData.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/fetchRadarURLFromBugXMLData.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -27,8 +27,8 @@
</span><span class="cx">
</span><span class="cx"> use Test::More;
</span><span class="cx"> use FindBin;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
-use webkitperl::changelog qw(fetchRadarURLFromBugXMLData);
</del><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
+use LoadAsModule qw(PrepareChangeLog prepare-ChangeLog);
</ins><span class="cx">
</span><span class="cx"> # NOTE: A Bugzilla Comment XML looks like:
</span><span class="cx"> #
</span><span class="lines">@@ -156,6 +156,6 @@
</span><span class="cx">
</span><span class="cx"> foreach my $testCase (@testCaseHashRefs) {
</span><span class="cx"> my $expected = $testCase->{expected};
</span><del>- my $got = fetchRadarURLFromBugXMLData(152839, $testCase->{inputText});
</del><ins>+ my $got = PrepareChangeLog::fetchRadarURLFromBugXMLData(152839, $testCase->{inputText});
</ins><span class="cx"> is($got, $expected, $testCase->{testName});
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlprepareChangeLog_unittestfilenameWithParenthesespl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/filenameWithParentheses.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/filenameWithParentheses.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/filenameWithParentheses.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -30,10 +30,11 @@
</span><span class="cx"> use File::Temp;
</span><span class="cx"> use FindBin;
</span><span class="cx"> use Test::More;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
-use webkitperl::changelog qw(attributeCommand);
</del><span class="cx"> use VCSUtils;
</span><span class="cx">
</span><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
+use LoadAsModule qw(PrepareChangeLog prepare-ChangeLog);
+
</ins><span class="cx"> plan(tests => 1);
</span><span class="cx">
</span><span class="cx"> sub writeFileWithContent($$)
</span><span class="lines">@@ -53,7 +54,7 @@
</span><span class="cx"> my $attributeCache = {};
</span><span class="cx"> my $result = 0;
</span><span class="cx"> chdir $temporaryDirectory;
</span><del>-$result = attributeCommand($attributeCache, $filename, "foo");
</del><ins>+$result = PrepareChangeLog::attributeCommand($attributeCache, $filename, "foo");
</ins><span class="cx"> chdir '..';
</span><span class="cx">
</span><span class="cx"> is($result, 1, "Should successfully get foo attribute from file with parentheses in name.");
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlprepareChangeLog_unittestgenerateFunctionListspl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/generateFunctionLists.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/generateFunctionLists.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/generateFunctionLists.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -28,10 +28,11 @@
</span><span class="cx"> use File::Temp;
</span><span class="cx"> use FindBin;
</span><span class="cx"> use Test::More;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
-use webkitperl::changelog qw(actuallyGenerateFunctionLists);
</del><span class="cx"> use VCSUtils;
</span><span class="cx">
</span><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
+use LoadAsModule qw(PrepareChangeLog prepare-ChangeLog);
+
</ins><span class="cx"> my $EXAMPLE_CPP = <<EOF;
</span><span class="cx"> #include <cassert>
</span><span class="cx"> #include <cmath>
</span><span class="lines">@@ -535,7 +536,7 @@
</span><span class="cx"> );
</span><span class="cx"> my %functionLists;
</span><span class="cx"> discardOutput(sub {
</span><del>- actuallyGenerateFunctionLists([$filename], \%functionLists, undef, undef, undef, \%delegateHash);
</del><ins>+ PrepareChangeLog::actuallyGenerateFunctionLists([$filename], \%functionLists, undef, undef, undef, \%delegateHash);
</ins><span class="cx"> });
</span><span class="cx"> chomp(my $expected = $testCase->{expected});
</span><span class="cx"> is($functionLists{$filename}, $expected, $testCase->{testName});
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlprepareChangeLog_unittestparser_unittestspl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/parser_unittests.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/parser_unittests.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/prepare-ChangeLog_unittest/parser_unittests.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -31,20 +31,11 @@
</span><span class="cx"> use FindBin;
</span><span class="cx"> use Getopt::Long;
</span><span class="cx"> use Test::More;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
-use VCSUtils;
-use webkitperl::changelog qw(
- get_function_line_ranges_for_cpp
- get_selector_line_ranges_for_css
- get_function_line_ranges_for_java
- get_function_line_ranges_for_javascript
- get_function_line_ranges_for_perl
- get_function_line_ranges_for_python
- get_function_line_ranges_for_swift
-);
-use webkitperl::dirs;
</del><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
+use LoadAsModule qw(PrepareChangeLog prepare-ChangeLog);
</ins><span class="cx">
</span><span class="cx"> sub captureOutput($);
</span><ins>+sub convertAbsolutePathToRelativeUnixPath($$);
</ins><span class="cx"> sub readTestFiles($);
</span><span class="cx">
</span><span class="cx"> use constant EXPECTED_RESULTS_SUFFIX => "-expected.txt";
</span><span class="lines">@@ -78,7 +69,7 @@
</span><span class="cx"> plan(tests => scalar @testSet);
</span><span class="cx"> foreach my $test (@testSet) {
</span><span class="cx"> open FH, "< $test->{inputFile}" or die "Cannot open $test->{inputFile}: $!";
</span><del>- my $parser = eval "\\&$test->{method}";
</del><ins>+ my $parser = eval "\\&PrepareChangeLog::$test->{method}";
</ins><span class="cx"> my @ranges;
</span><span class="cx"> my ($stdout, $stderr) = captureOutput(sub { @ranges = $parser->(\*FH, $test->{inputFile}); });
</span><span class="cx"> close FH;
</span><span class="lines">@@ -139,10 +130,11 @@
</span><span class="cx"> return ($stdout, $stderr);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-sub convertAbsolutePathToRelativeUnixPath {
</del><ins>+sub convertAbsolutePathToRelativeUnixPath($$)
+{
</ins><span class="cx"> my ($string, $path) = @_;
</span><del>- my $sourceDir = unixPath(sourceDir());
- my $relativeUnixPath = unixPath($path);
</del><ins>+ my $sourceDir = LoadAsModule::unixPath(LoadAsModule::sourceDir());
+ my $relativeUnixPath = LoadAsModule::unixPath($path);
</ins><span class="cx"> $sourceDir .= "/" unless $sourceDir =~ m-/$-;
</span><span class="cx"> my $quotedSourceDir = quotemeta($sourceDir);
</span><span class="cx"> $relativeUnixPath =~ s/$quotedSourceDir//;
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlrunleaks_unittestrunleaksreportv10pl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v1.0.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v1.0.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v1.0.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -29,9 +29,9 @@
</span><span class="cx">
</span><span class="cx"> use File::Spec;
</span><span class="cx"> use FindBin;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
</del><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
</ins><span class="cx"> use Test::More;
</span><del>-use webkitperl::leaks qw(parseLeaksOutput);
</del><ins>+use LoadAsModule qw(RunLeaks run-leaks);
</ins><span class="cx">
</span><span class="cx"> my @input = split(/\n/, <<EOF);
</span><span class="cx"> Process 1602: 86671 nodes malloced for 13261 KB
</span><span class="lines">@@ -162,7 +162,7 @@
</span><span class="cx"> },
</span><span class="cx"> ];
</span><span class="cx">
</span><del>-my $actualOutput = parseLeaksOutput(@input);
</del><ins>+my $actualOutput = RunLeaks::parseLeaksOutput(@input);
</ins><span class="cx">
</span><span class="cx"> plan(tests => 1);
</span><span class="cx"> is_deeply($actualOutput, $expectedOutput, "leaks Report Version 1.0 - no call stack");
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlrunleaks_unittestrunleaksreportv20newpl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-new.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-new.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-new.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -30,9 +30,9 @@
</span><span class="cx">
</span><span class="cx"> use File::Spec;
</span><span class="cx"> use FindBin;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
</del><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
</ins><span class="cx"> use Test::More;
</span><del>-use webkitperl::leaks qw(parseLeaksOutput);
</del><ins>+use LoadAsModule qw(RunLeaks run-leaks);
</ins><span class="cx">
</span><span class="cx"> my @input = split(/\n/, <<EOF);
</span><span class="cx"> Process: DumpRenderTree [29903]
</span><span class="lines">@@ -168,7 +168,7 @@
</span><span class="cx"> },
</span><span class="cx"> ];
</span><span class="cx">
</span><del>-my $actualOutput = parseLeaksOutput(@input);
</del><ins>+my $actualOutput = RunLeaks::parseLeaksOutput(@input);
</ins><span class="cx">
</span><span class="cx"> plan(tests => 1);
</span><span class="cx"> is_deeply($actualOutput, $expectedOutput, "leaks Report Version 2.0 (new)");
</span></span></pre></div>
<a id="trunkToolsScriptswebkitperlrunleaks_unittestrunleaksreportv20oldpl"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-old.pl (284418 => 284419)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-old.pl 2021-10-19 00:44:15 UTC (rev 284418)
+++ trunk/Tools/Scripts/webkitperl/run-leaks_unittest/run-leaks-report-v2.0-old.pl 2021-10-19 00:48:12 UTC (rev 284419)
</span><span class="lines">@@ -30,9 +30,9 @@
</span><span class="cx">
</span><span class="cx"> use File::Spec;
</span><span class="cx"> use FindBin;
</span><del>-use lib File::Spec->catdir($FindBin::Bin, "..", "..");
</del><ins>+use lib File::Spec->catdir($FindBin::Bin, "..");
</ins><span class="cx"> use Test::More;
</span><del>-use webkitperl::leaks qw(parseLeaksOutput);
</del><ins>+use LoadAsModule qw(RunLeaks run-leaks);
</ins><span class="cx">
</span><span class="cx"> my @input = split(/\n/, <<EOF);
</span><span class="cx"> leaks Report Version: 2.0
</span><span class="lines">@@ -118,7 +118,7 @@
</span><span class="cx"> },
</span><span class="cx"> ];
</span><span class="cx">
</span><del>-my $actualOutput = parseLeaksOutput(@input);
</del><ins>+my $actualOutput = RunLeaks::parseLeaksOutput(@input);
</ins><span class="cx">
</span><span class="cx"> plan(tests => 1);
</span><span class="cx"> is_deeply($actualOutput, $expectedOutput, "leaks Report Version 2.0 (old)");
</span></span></pre>
</div>
</div>
</body>
</html>