Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(833)

Unified Diff: src/js/regexp.js

Issue 2398423002: [regexp] Port RegExp.prototype[@@replace] (Closed)
Patch Set: Smi::kZero Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/js/prologue.js ('k') | src/objects.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/js/regexp.js
diff --git a/src/js/regexp.js b/src/js/regexp.js
index fe8975e0f07d29477e1f8439c5fe7bba887ae954..aa531e4e1ab15857c3e8187535467cd16a1db28d 100644
--- a/src/js/regexp.js
+++ b/src/js/regexp.js
@@ -11,26 +11,10 @@
// -------------------------------------------------------------------
// Imports
-var GlobalArray = global.Array;
-var GlobalObject = global.Object;
var GlobalRegExp = global.RegExp;
var GlobalRegExpPrototype = GlobalRegExp.prototype;
-var InternalArray = utils.InternalArray;
-var MaxSimple;
-var MinSimple;
var RegExpExecJS = GlobalRegExp.prototype.exec;
var matchSymbol = utils.ImportNow("match_symbol");
-var replaceSymbol = utils.ImportNow("replace_symbol");
-var searchSymbol = utils.ImportNow("search_symbol");
-var speciesSymbol = utils.ImportNow("species_symbol");
-var splitSymbol = utils.ImportNow("split_symbol");
-var SpeciesConstructor;
-
-utils.Import(function(from) {
- MaxSimple = from.MaxSimple;
- MinSimple = from.MinSimple;
- SpeciesConstructor = from.SpeciesConstructor;
-});
// -------------------------------------------------------------------
@@ -71,11 +55,6 @@ function RegExpInitialize(object, pattern, flags) {
}
-function DoRegExpExec(regexp, string, index) {
- return %_RegExpExec(regexp, string, index, RegExpLastMatchInfo);
-}
-
-
// This is kind of performance sensitive, so we want to avoid unnecessary
// type checks on inputs. But we also don't want to inline it several times
// manually, so we use a macro :-)
@@ -101,228 +80,6 @@ macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING)
return result;
endmacro
-
-// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
-// Also takes an optional exec method in case our caller
-// has already fetched exec.
-function RegExpSubclassExec(regexp, string, exec) {
- if (IS_UNDEFINED(exec)) {
- exec = regexp.exec;
- }
- if (IS_CALLABLE(exec)) {
- var result = %_Call(exec, regexp, string);
- if (!IS_RECEIVER(result) && !IS_NULL(result)) {
- throw %make_type_error(kInvalidRegExpExecResult);
- }
- return result;
- }
- return %_Call(RegExpExecJS, regexp, string);
-}
-%SetForceInlineFlag(RegExpSubclassExec);
-
-
-// Legacy implementation of RegExp.prototype[Symbol.replace] which
-// doesn't properly call the underlying exec method.
-
-// TODO(lrn): This array will survive indefinitely if replace is never
-// called again. However, it will be empty, since the contents are cleared
-// in the finally block.
-var reusableReplaceArray = new InternalArray(4);
-
-// Helper function for replacing regular expressions with the result of a
-// function application in String.prototype.replace.
-function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
- var resultArray = reusableReplaceArray;
- if (resultArray) {
- reusableReplaceArray = null;
- } else {
- // Inside a nested replace (replace called from the replacement function
- // of another replace) or we have failed to set the reusable array
- // back due to an exception in a replacement function. Create a new
- // array to use in the future, or until the original is written back.
- resultArray = new InternalArray(16);
- }
- var res = %RegExpExecMultiple(regexp,
- subject,
- RegExpLastMatchInfo,
- resultArray);
- regexp.lastIndex = 0;
- if (IS_NULL(res)) {
- // No matches at all.
- reusableReplaceArray = resultArray;
- return subject;
- }
- var len = res.length;
- if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) {
- // If the number of captures is two then there are no explicit captures in
- // the regexp, just the implicit capture that captures the whole match. In
- // this case we can simplify quite a bit and end up with something faster.
- // The builder will consist of some integers that indicate slices of the
- // input string and some replacements that were returned from the replace
- // function.
- var match_start = 0;
- for (var i = 0; i < len; i++) {
- var elem = res[i];
- if (%_IsSmi(elem)) {
- // Integers represent slices of the original string.
- if (elem > 0) {
- match_start = (elem >> 11) + (elem & 0x7ff);
- } else {
- match_start = res[++i] - elem;
- }
- } else {
- var func_result = replace(elem, match_start, subject);
- // Overwrite the i'th element in the results with the string we got
- // back from the callback function.
- res[i] = TO_STRING(func_result);
- match_start += elem.length;
- }
- }
- } else {
- for (var i = 0; i < len; i++) {
- var elem = res[i];
- if (!%_IsSmi(elem)) {
- // elem must be an Array.
- // Use the apply argument as backing for global RegExp properties.
- var func_result = %reflect_apply(replace, UNDEFINED, elem);
- // Overwrite the i'th element in the results with the string we got
- // back from the callback function.
- res[i] = TO_STRING(func_result);
- }
- }
- }
- var result = %StringBuilderConcat(res, len, subject);
- resultArray.length = 0;
- reusableReplaceArray = resultArray;
- return result;
-}
-
-
-// Compute the string of a given regular expression capture.
-function CaptureString(string, lastCaptureInfo, index) {
- // Scale the index.
- var scaled = index << 1;
- // Compute start and end.
- var start = lastCaptureInfo[CAPTURE(scaled)];
- // If start isn't valid, return undefined.
- if (start < 0) return;
- var end = lastCaptureInfo[CAPTURE(scaled + 1)];
- return %_SubString(string, start, end);
-}
-
-
-function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
- var matchInfo = DoRegExpExec(regexp, subject, 0);
- if (IS_NULL(matchInfo)) {
- regexp.lastIndex = 0;
- return subject;
- }
- var index = matchInfo[CAPTURE0];
- var result = %_SubString(subject, 0, index);
- var endOfMatch = matchInfo[CAPTURE1];
- // Compute the parameter list consisting of the match, captures, index,
- // and subject for the replace function invocation.
- // The number of captures plus one for the match.
- var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
- var replacement;
- if (m == 1) {
- // No captures, only the match, which is always valid.
- var s = %_SubString(subject, index, endOfMatch);
- // Don't call directly to avoid exposing the built-in global object.
- replacement = replace(s, index, subject);
- } else {
- var parameters = new InternalArray(m + 2);
- for (var j = 0; j < m; j++) {
- parameters[j] = CaptureString(subject, matchInfo, j);
- }
- parameters[j] = index;
- parameters[j + 1] = subject;
-
- replacement = %reflect_apply(replace, UNDEFINED, parameters);
- }
-
- result += replacement; // The add method converts to string if necessary.
- // Can't use matchInfo any more from here, since the function could
- // overwrite it.
- 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)) {
- throw %make_type_error(kIncompatibleMethodReceiver,
- "RegExp.prototype.@@replace", this);
- }
- var subject = TO_STRING(string);
- var search = this;
-
- if (!IS_CALLABLE(replace)) {
- replace = TO_STRING(replace);
-
- if (!REGEXP_GLOBAL(search)) {
- // Non-global regexp search, string replace.
- var match = DoRegExpExec(search, subject, 0);
- if (match == null) {
- search.lastIndex = 0
- return subject;
- }
- if (replace.length == 0) {
- return %_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.
- search.lastIndex = 0;
- return %StringReplaceGlobalRegExpWithString(
- subject, search, replace, RegExpLastMatchInfo);
- }
-
- if (REGEXP_GLOBAL(search)) {
- // Global regexp search, function replace.
- return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
- }
- // Non-global regexp search, function replace.
- return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
-}
-
-
// ES#sec-getsubstitution
// GetSubstitution(matched, str, position, captures, replacement)
// Expand the $-expressions in the string and return a new string with
@@ -408,120 +165,8 @@ function GetSubstitution(matched, string, position, captures, replacement) {
return result;
}
-
-// ES#sec-advancestringindex
-// AdvanceStringIndex ( S, index, unicode )
-function AdvanceStringIndex(string, index, unicode) {
- var increment = 1;
- if (unicode) {
- var first = %_StringCharCodeAt(string, index);
- if (first >= 0xD800 && first <= 0xDBFF && string.length > index + 1) {
- var second = %_StringCharCodeAt(string, index + 1);
- if (second >= 0xDC00 && second <= 0xDFFF) {
- increment = 2;
- }
- }
- }
- return increment;
-}
-
-
-function SetAdvancedStringIndex(regexp, string, unicode) {
- var lastIndex = regexp.lastIndex;
- regexp.lastIndex = lastIndex +
- AdvanceStringIndex(string, lastIndex, unicode);
-}
-
-
-// ES#sec-regexp.prototype-@@replace
-// RegExp.prototype [ @@replace ] ( string, replaceValue )
-function RegExpSubclassReplace(string, replace) {
- if (!IS_RECEIVER(this)) {
- throw %make_type_error(kIncompatibleMethodReceiver,
- "RegExp.prototype.@@replace", this);
- }
- string = TO_STRING(string);
- var length = string.length;
- var functionalReplace = IS_CALLABLE(replace);
- if (!functionalReplace) replace = TO_STRING(replace);
- var global = TO_BOOLEAN(this.global);
- if (global) {
- var unicode = TO_BOOLEAN(this.unicode);
- this.lastIndex = 0;
- }
-
- // TODO(adamk): this fast path is wrong as we doesn't ensure that 'exec'
- // is actually a data property on RegExp.prototype.
- var exec;
- if (IS_REGEXP(this)) {
- exec = this.exec;
- if (exec === RegExpExecJS) {
- return %_Call(RegExpReplace, this, string, replace);
- }
- }
-
- var results = new InternalArray();
- var result, replacement;
- while (true) {
- result = RegExpSubclassExec(this, string, exec);
- // Ensure exec will be read again on the next loop through.
- exec = UNDEFINED;
- if (IS_NULL(result)) {
- break;
- } else {
- results.push(result);
- if (!global) break;
- var matchStr = TO_STRING(result[0]);
- if (matchStr === "") SetAdvancedStringIndex(this, string, unicode);
- }
- }
- var accumulatedResult = "";
- var nextSourcePosition = 0;
- for (var i = 0; i < results.length; i++) {
- result = results[i];
- var capturesLength = MaxSimple(TO_LENGTH(result.length), 0);
- var matched = TO_STRING(result[0]);
- var matchedLength = matched.length;
- var position = MaxSimple(MinSimple(TO_INTEGER(result.index), length), 0);
- var captures = new InternalArray();
- for (var n = 0; n < capturesLength; n++) {
- var capture = result[n];
- if (!IS_UNDEFINED(capture)) capture = TO_STRING(capture);
- captures[n] = capture;
- }
- if (functionalReplace) {
- var parameters = new InternalArray(capturesLength + 2);
- for (var j = 0; j < capturesLength; j++) {
- parameters[j] = captures[j];
- }
- parameters[j] = position;
- parameters[j + 1] = string;
- replacement = %reflect_apply(replace, UNDEFINED, parameters, 0,
- parameters.length);
- } else {
- const capturesWrapper = new ArrayCaptureWrapper(captures);
- replacement = GetSubstitution(matched, string, position, capturesWrapper,
- replace);
- }
- if (position >= nextSourcePosition) {
- accumulatedResult +=
- %_SubString(string, nextSourcePosition, position) + replacement;
- nextSourcePosition = position + matchedLength;
- }
- }
- if (nextSourcePosition >= length) return accumulatedResult;
- return accumulatedResult + %_SubString(string, nextSourcePosition, length);
-}
-%FunctionRemovePrototype(RegExpSubclassReplace);
-
-
-
// -------------------------------------------------------------------
-utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
- replaceSymbol, RegExpSubclassReplace,
-]);
-
%InstallToContext(["regexp_last_match_info", RegExpLastMatchInfo]);
// -------------------------------------------------------------------
@@ -556,7 +201,6 @@ utils.Export(function(to) {
to.InternalRegExpMatch = InternalRegExpMatch;
to.InternalRegExpReplace = InternalRegExpReplace;
to.IsRegExp = IsRegExp;
- to.RegExpExec = DoRegExpExec;
to.RegExpInitialize = RegExpInitialize;
to.RegExpLastMatchInfo = RegExpLastMatchInfo;
});
« no previous file with comments | « src/js/prologue.js ('k') | src/objects.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698