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; |
}); |