[regexp/string] Merge ExpandReplacement and GetSubstitution

R=littledan@chromium.org
BUG=v8:5339

Review-Url: https://codereview.chromium.org/2332333002
Cr-Commit-Position: refs/heads/master@{#39528}
This commit is contained in:
jgruber 2016-09-20 01:13:21 -07:00 committed by Commit bot
parent 5e113c7241
commit 5ab3874559
2 changed files with 48 additions and 107 deletions

View File

@ -11,7 +11,6 @@
// -------------------------------------------------------------------
// Imports
var ExpandReplacement;
var GlobalArray = global.Array;
var GlobalObject = global.Object;
var GlobalRegExp = global.RegExp;
@ -28,7 +27,6 @@ var splitSymbol = utils.ImportNow("split_symbol");
var SpeciesConstructor;
utils.Import(function(from) {
ExpandReplacement = from.ExpandReplacement;
MaxSimple = from.MaxSimple;
MinSimple = from.MinSimple;
SpeciesConstructor = from.SpeciesConstructor;
@ -565,6 +563,31 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
return result + %_SubString(subject, endOfMatch, subject.length);
}
// Wraps access to matchInfo's captures into a format understood by
// GetSubstitution.
function MatchInfoCaptureWrapper(matches, subject) {
this.length = NUMBER_OF_CAPTURES(matches) >> 1;
this.match = matches;
this.subject = subject;
}
MatchInfoCaptureWrapper.prototype.at = function(ix) {
const match = this.match;
const start = match[CAPTURE(ix << 1)];
if (start < 0) return UNDEFINED;
return %_SubString(this.subject, start, match[CAPTURE((ix << 1) + 1)]);
};
%SetForceInlineFlag(MatchInfoCaptureWrapper.prototype.at);
function ArrayCaptureWrapper(array) {
this.length = array.length;
this.array = array;
}
ArrayCaptureWrapper.prototype.at = function(ix) {
return this.array[ix];
};
%SetForceInlineFlag(ArrayCaptureWrapper.prototype.at);
function RegExpReplace(string, replace) {
if (!IS_REGEXP(this)) {
@ -588,9 +611,17 @@ function RegExpReplace(string, replace) {
return %_SubString(subject, 0, match[CAPTURE0]) +
%_SubString(subject, match[CAPTURE1], subject.length)
}
return ExpandReplacement(replace, subject, RegExpLastMatchInfo,
%_SubString(subject, 0, match[CAPTURE0])) +
%_SubString(subject, match[CAPTURE1], subject.length);
const captures = new MatchInfoCaptureWrapper(match, subject);
const start = match[CAPTURE0];
const end = match[CAPTURE1];
const prefix = %_SubString(subject, 0, start);
const matched = %_SubString(subject, start, end);
const suffix = %_SubString(subject, end, subject.length);
return prefix +
GetSubstitution(matched, subject, start, captures, replace) +
suffix;
}
// Global regexp search, string replace.
@ -612,8 +643,6 @@ function RegExpReplace(string, replace) {
// GetSubstitution(matched, str, position, captures, replacement)
// Expand the $-expressions in the string and return a new string with
// the result.
// TODO(littledan): Call this function from String.prototype.replace instead
// of the very similar ExpandReplacement in src/js/string.js
function GetSubstitution(matched, string, position, captures, replacement) {
var matchLength = matched.length;
var stringLength = string.length;
@ -662,7 +691,7 @@ function GetSubstitution(matched, string, position, captures, replacement) {
}
}
if (scaledIndex != 0 && scaledIndex < capturesLength) {
var capture = captures[scaledIndex];
var capture = captures.at(scaledIndex);
if (!IS_UNDEFINED(capture)) result += capture;
pos += advance;
} else {
@ -786,7 +815,8 @@ function RegExpSubclassReplace(string, replace) {
replacement = %reflect_apply(replace, UNDEFINED, parameters, 0,
parameters.length);
} else {
replacement = GetSubstitution(matched, string, position, captures,
const capturesWrapper = new ArrayCaptureWrapper(captures);
replacement = GetSubstitution(matched, string, position, capturesWrapper,
replace);
}
if (position >= nextSourcePosition) {
@ -1095,6 +1125,7 @@ function InternalRegExpReplace(regexp, subject, replacement) {
// Exports
utils.Export(function(to) {
to.GetSubstitution = GetSubstitution;
to.InternalRegExpMatch = InternalRegExpMatch;
to.InternalRegExpReplace = InternalRegExpReplace;
to.IsRegExp = IsRegExp;

View File

@ -10,6 +10,7 @@
// Imports
var ArrayJoin;
var GetSubstitution;
var GlobalRegExp = global.RegExp;
var GlobalString = global.String;
var IsRegExp;
@ -23,6 +24,7 @@ var splitSymbol = utils.ImportNow("split_symbol");
utils.Import(function(from) {
ArrayJoin = from.ArrayJoin;
GetSubstitution = from.GetSubstitution;
IsRegExp = from.IsRegExp;
MaxSimple = from.MaxSimple;
MinSimple = from.MinSimple;
@ -79,14 +81,6 @@ function StringMatchJS(pattern) {
}
// This has the same size as the RegExpLastMatchInfo array, and can be used
// for functions that expect that structure to be returned. It is used when
// the needle is a string rather than a regexp. In this case we can't update
// lastMatchArray without erroneously affecting the properties on the global
// RegExp object.
var reusableMatchInfo = [2, "", "", -1, -1];
// ES6, section 21.1.3.14
function StringReplace(search, replace) {
CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
@ -138,101 +132,18 @@ function StringReplace(search, replace) {
if (IS_CALLABLE(replace)) {
result += replace(search, start, subject);
} else {
reusableMatchInfo[CAPTURE0] = start;
reusableMatchInfo[CAPTURE1] = end;
result = ExpandReplacement(TO_STRING(replace),
subject,
reusableMatchInfo,
result);
// In this case, we don't have any capture groups and can get away with
// faking the captures object by simply setting its length to 1.
const captures = { length: 1 };
const matched = %_SubString(subject, start, end);
result += GetSubstitution(matched, subject, start, captures,
TO_STRING(replace));
}
return result + %_SubString(subject, end, subject.length);
}
// Expand the $-expressions in the string and return a new string with
// the result.
function ExpandReplacement(string, subject, matchInfo, result) {
var length = string.length;
var next = %StringIndexOf(string, '$', 0);
if (next < 0) {
if (length > 0) result += string;
return result;
}
if (next > 0) result += %_SubString(string, 0, next);
while (true) {
var expansion = '$';
var position = next + 1;
if (position < length) {
var peek = %_StringCharCodeAt(string, position);
if (peek == 36) { // $$
++position;
result += '$';
} else if (peek == 38) { // $& - match
++position;
result +=
%_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
} else if (peek == 96) { // $` - prefix
++position;
result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
} else if (peek == 39) { // $' - suffix
++position;
result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
} else if (peek >= 48 && peek <= 57) {
// Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
var scaled_index = (peek - 48) << 1;
var advance = 1;
var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
if (position + 1 < string.length) {
var next = %_StringCharCodeAt(string, position + 1);
if (next >= 48 && next <= 57) {
var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
if (new_scaled_index < number_of_captures) {
scaled_index = new_scaled_index;
advance = 2;
}
}
}
if (scaled_index != 0 && scaled_index < number_of_captures) {
var start = matchInfo[CAPTURE(scaled_index)];
if (start >= 0) {
result +=
%_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
}
position += advance;
} else {
result += '$';
}
} else {
result += '$';
}
} else {
result += '$';
}
// Go the the next $ in the string.
next = %StringIndexOf(string, '$', position);
// Return if there are no more $ characters in the string. If we
// haven't reached the end, we need to append the suffix.
if (next < 0) {
if (position < length) {
result += %_SubString(string, position, length);
}
return result;
}
// Append substring between the previous and the next $ character.
if (next > position) {
result += %_SubString(string, position, next);
}
}
return result;
}
// ES6 21.1.3.15.
function StringSearch(pattern) {
CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
@ -707,7 +618,6 @@ utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
// Exports
utils.Export(function(to) {
to.ExpandReplacement = ExpandReplacement;
to.StringIndexOf = StringIndexOf;
to.StringMatch = StringMatchJS;
to.StringReplace = StringReplace;