<!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*((&lt;)?rdar://(problem/)?\d+(&gt;)?)|;
+
+    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/\&lt;/</g;
+    $text =~ s/\&gt;/>/g;
+    $text =~ s/\&quot;/\"/g;
+    $text =~ s/\&apos;/\'/g;
+    $text =~ s/\&amp;/\&/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*((&lt;)?rdar://(problem/)?\d+(&gt;)?)|;
-
-    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/\&lt;/</g;
-    $text =~ s/\&gt;/>/g;
-    $text =~ s/\&quot;/\"/g;
-    $text =~ s/\&apos;/\'/g;
-    $text =~ s/\&amp;/\&/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>