diff options
Diffstat (limited to 'deps/v8/src/js/regexp.js')
-rw-r--r-- | deps/v8/src/js/regexp.js | 555 |
1 files changed, 511 insertions, 44 deletions
diff --git a/deps/v8/src/js/regexp.js b/deps/v8/src/js/regexp.js index e80d0190f4..cc8cb41de1 100644 --- a/deps/v8/src/js/regexp.js +++ b/deps/v8/src/js/regexp.js @@ -4,26 +4,37 @@ (function(global, utils) { +'use strict'; + %CheckIsBootstrapping(); // ------------------------------------------------------------------- // Imports +var AddIndexedProperty; var ExpandReplacement; +var GlobalArray = global.Array; var GlobalObject = global.Object; var GlobalRegExp = global.RegExp; var GlobalRegExpPrototype; var InternalArray = utils.InternalArray; var InternalPackedArray = utils.InternalPackedArray; var MakeTypeError; +var MaxSimple; +var MinSimple; var matchSymbol = utils.ImportNow("match_symbol"); var replaceSymbol = utils.ImportNow("replace_symbol"); var searchSymbol = utils.ImportNow("search_symbol"); var splitSymbol = utils.ImportNow("split_symbol"); +var SpeciesConstructor; utils.Import(function(from) { + AddIndexedProperty = from.AddIndexedProperty; ExpandReplacement = from.ExpandReplacement; MakeTypeError = from.MakeTypeError; + MaxSimple = from.MaxSimple; + MinSimple = from.MinSimple; + SpeciesConstructor = from.SpeciesConstructor; }); // ------------------------------------------------------------------- @@ -44,6 +55,7 @@ var RegExpLastMatchInfo = new InternalPackedArray( // ------------------------------------------------------------------- +// ES#sec-isregexp IsRegExp ( argument ) function IsRegExp(o) { if (!IS_RECEIVER(o)) return false; var is_regexp = o[matchSymbol]; @@ -52,7 +64,8 @@ function IsRegExp(o) { } -// ES6 section 21.2.3.2.2 +// ES#sec-regexpinitialize +// Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) function RegExpInitialize(object, pattern, flags) { pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern); flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags); @@ -70,6 +83,8 @@ function PatternFlags(pattern) { } +// ES#sec-regexp-pattern-flags +// RegExp ( pattern, flags ) function RegExpConstructor(pattern, flags) { var newtarget = new.target; var pattern_is_regexp = IsRegExp(pattern); @@ -94,11 +109,12 @@ function RegExpConstructor(pattern, flags) { if (IS_UNDEFINED(flags)) flags = input_pattern.flags; } - var object = %NewObject(GlobalRegExp, newtarget); + var object = %_NewObject(GlobalRegExp, newtarget); return RegExpInitialize(object, pattern, flags); } +// ES#sec-regexp.prototype.compile RegExp.prototype.compile (pattern, flags) function RegExpCompileJS(pattern, flags) { if (!IS_REGEXP(this)) { throw MakeTypeError(kIncompatibleMethodReceiver, @@ -163,6 +179,54 @@ function RegExpExecNoTests(regexp, string, start) { } +// ES#sec-regexp.prototype.exec +// RegExp.prototype.exec ( string ) +function RegExpSubclassExecJS(string) { + if (!IS_REGEXP(this)) { + throw MakeTypeError(kIncompatibleMethodReceiver, + 'RegExp.prototype.exec', this); + } + + string = TO_STRING(string); + var lastIndex = this.lastIndex; + + // Conversion is required by the ES2015 specification (RegExpBuiltinExec + // algorithm, step 4) even if the value is discarded for non-global RegExps. + var i = TO_LENGTH(lastIndex); + + var global = TO_BOOLEAN(REGEXP_GLOBAL(this)); + var sticky = TO_BOOLEAN(REGEXP_STICKY(this)); + var updateLastIndex = global || sticky; + if (updateLastIndex) { + if (i > string.length) { + this.lastIndex = 0; + return null; + } + } else { + i = 0; + } + + // matchIndices is either null or the RegExpLastMatchInfo array. + // TODO(littledan): Whether a RegExp is sticky is compiled into the RegExp + // itself, but ES2015 allows monkey-patching this property to differ from + // the internal flags. If it differs, recompile a different RegExp? + var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo); + + if (IS_NULL(matchIndices)) { + this.lastIndex = 0; + return null; + } + + // Successful match. + if (updateLastIndex) { + this.lastIndex = RegExpLastMatchInfo[CAPTURE1]; + } + RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string); +} +%FunctionRemovePrototype(RegExpSubclassExecJS); + + +// Legacy implementation of RegExp.prototype.exec function RegExpExecJS(string) { if (!IS_REGEXP(this)) { throw MakeTypeError(kIncompatibleMethodReceiver, @@ -202,10 +266,30 @@ function RegExpExecJS(string) { } +// 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 MakeTypeError(kInvalidRegExpExecResult); + } + return result; + } + return %_Call(RegExpExecJS, regexp, string); +} +%SetForceInlineFlag(RegExpSubclassExec); + + // One-element cache for the simplified test regexp. var regexp_key; var regexp_val; +// Legacy implementation of RegExp.prototype.test // Section 15.10.6.3 doesn't actually make sense, but the intention seems to be // that test is defined in terms of String.prototype.exec. However, it probably // means the original value of String.prototype.exec, which is what everybody @@ -259,6 +343,19 @@ function RegExpTest(string) { } } + +// ES#sec-regexp.prototype.test RegExp.prototype.test ( S ) +function RegExpSubclassTest(string) { + if (!IS_RECEIVER(this)) { + throw MakeTypeError(kIncompatibleMethodReceiver, + 'RegExp.prototype.test', this); + } + string = TO_STRING(string); + var match = RegExpSubclassExec(this, string); + return !IS_NULL(match); +} +%FunctionRemovePrototype(RegExpSubclassTest); + function TrimRegExp(regexp) { if (regexp_key !== regexp) { regexp_key = regexp; @@ -273,27 +370,14 @@ function TrimRegExp(regexp) { function RegExpToString() { - if (!IS_REGEXP(this)) { - // RegExp.prototype.toString() returns '/(?:)/' as a compatibility fix; - // a UseCounter is incremented to track it. - // TODO(littledan): Remove this workaround or standardize it - if (this === GlobalRegExpPrototype) { - %IncrementUseCounter(kRegExpPrototypeToString); - return '/(?:)/'; - } - if (!IS_RECEIVER(this)) { - throw MakeTypeError( - kIncompatibleMethodReceiver, 'RegExp.prototype.toString', this); - } - return '/' + TO_STRING(this.source) + '/' + TO_STRING(this.flags); + if (!IS_RECEIVER(this)) { + throw MakeTypeError( + kIncompatibleMethodReceiver, 'RegExp.prototype.toString', this); } - var result = '/' + REGEXP_SOURCE(this) + '/'; - if (REGEXP_GLOBAL(this)) result += 'g'; - if (REGEXP_IGNORE_CASE(this)) result += 'i'; - if (REGEXP_MULTILINE(this)) result += 'm'; - if (REGEXP_UNICODE(this)) result += 'u'; - if (REGEXP_STICKY(this)) result += 'y'; - return result; + if (this === GlobalRegExpPrototype) { + %IncrementUseCounter(kRegExpPrototypeToString); + } + return '/' + TO_STRING(this.source) + '/' + TO_STRING(this.flags); } @@ -306,7 +390,8 @@ function AtSurrogatePair(subject, index) { } -// ES6 21.2.5.11. +// Legacy implementation of RegExp.prototype[Symbol.split] which +// doesn't properly call the underlying exec, @@species methods function RegExpSplit(string, limit) { // TODO(yangguo): allow non-regexp receivers. if (!IS_REGEXP(this)) { @@ -380,9 +465,85 @@ function RegExpSplit(string, limit) { } -// ES6 21.2.5.6. +// ES#sec-regexp.prototype-@@split +// RegExp.prototype [ @@split ] ( string, limit ) +function RegExpSubclassSplit(string, limit) { + if (!IS_RECEIVER(this)) { + throw MakeTypeError(kIncompatibleMethodReceiver, + "RegExp.prototype.@@split", this); + } + string = TO_STRING(string); + var constructor = SpeciesConstructor(this, GlobalRegExp); + var flags = TO_STRING(this.flags); + + // TODO(adamk): this fast path is wrong with respect to this.global + // and this.sticky, but hopefully the spec will remove those gets + // and thus make the assumption of 'exec' having no side-effects + // more correct. Also, we doesn't ensure that 'exec' is actually + // a data property on RegExp.prototype. + var exec; + if (IS_REGEXP(this) && constructor === GlobalRegExp) { + exec = this.exec; + if (exec === RegExpSubclassExecJS) { + return %_Call(RegExpSplit, this, string, limit); + } + } + + var unicode = %StringIndexOf(flags, 'u', 0) >= 0; + var sticky = %StringIndexOf(flags, 'y', 0) >= 0; + var newFlags = sticky ? flags : flags + "y"; + var splitter = new constructor(this, newFlags); + var array = new GlobalArray(); + var arrayIndex = 0; + var lim = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit); + var size = string.length; + var prevStringIndex = 0; + if (lim === 0) return array; + var result; + if (size === 0) { + result = RegExpSubclassExec(splitter, string); + if (IS_NULL(result)) AddIndexedProperty(array, 0, string); + return array; + } + var stringIndex = prevStringIndex; + while (stringIndex < size) { + splitter.lastIndex = stringIndex; + result = RegExpSubclassExec(splitter, string, exec); + // Ensure exec will be read again on the next loop through. + exec = UNDEFINED; + if (IS_NULL(result)) { + stringIndex += AdvanceStringIndex(string, stringIndex, unicode); + } else { + var end = MinSimple(TO_LENGTH(splitter.lastIndex), size); + if (end === stringIndex) { + stringIndex += AdvanceStringIndex(string, stringIndex, unicode); + } else { + AddIndexedProperty( + array, arrayIndex, + %_SubString(string, prevStringIndex, stringIndex)); + arrayIndex++; + if (arrayIndex === lim) return array; + prevStringIndex = end; + var numberOfCaptures = MaxSimple(TO_LENGTH(result.length), 0); + for (var i = 1; i < numberOfCaptures; i++) { + AddIndexedProperty(array, arrayIndex, result[i]); + arrayIndex++; + if (arrayIndex === lim) return array; + } + stringIndex = prevStringIndex; + } + } + } + AddIndexedProperty(array, arrayIndex, + %_SubString(string, prevStringIndex, size)); + return array; +} +%FunctionRemovePrototype(RegExpSubclassSplit); + + +// Legacy implementation of RegExp.prototype[Symbol.match] which +// doesn't properly call the underlying exec method function RegExpMatch(string) { - // TODO(yangguo): allow non-regexp receivers. if (!IS_REGEXP(this)) { throw MakeTypeError(kIncompatibleMethodReceiver, "RegExp.prototype.@@match", this); @@ -396,7 +557,41 @@ function RegExpMatch(string) { } -// ES6 21.2.5.8. +// ES#sec-regexp.prototype-@@match +// RegExp.prototype [ @@match ] ( string ) +function RegExpSubclassMatch(string) { + if (!IS_RECEIVER(this)) { + throw MakeTypeError(kIncompatibleMethodReceiver, + "RegExp.prototype.@@match", this); + } + string = TO_STRING(string); + var global = this.global; + if (!global) return RegExpSubclassExec(this, string); + var unicode = this.unicode; + this.lastIndex = 0; + var array = new InternalArray(); + var n = 0; + var result; + while (true) { + result = RegExpSubclassExec(this, string); + if (IS_NULL(result)) { + if (n === 0) return null; + break; + } + var matchStr = TO_STRING(result[0]); + array[n] = matchStr; + if (matchStr === "") SetAdvancedStringIndex(this, string, unicode); + n++; + } + var resultArray = []; + %MoveArrayContents(array, resultArray); + return resultArray; +} +%FunctionRemovePrototype(RegExpSubclassMatch); + + +// 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 @@ -458,7 +653,7 @@ function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { if (!%_IsSmi(elem)) { // elem must be an Array. // Use the apply argument as backing for global RegExp properties. - var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length); + 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); @@ -512,7 +707,7 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { parameters[j] = index; parameters[j + 1] = subject; - replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2); + replacement = %reflect_apply(replace, UNDEFINED, parameters); } result += replacement; // The add method converts to string if necessary. @@ -523,7 +718,6 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { function RegExpReplace(string, replace) { - // TODO(littledan): allow non-regexp receivers. if (!IS_REGEXP(this)) { throw MakeTypeError(kIncompatibleMethodReceiver, "RegExp.prototype.@@replace", this); @@ -565,9 +759,206 @@ function RegExpReplace(string, replace) { } -// ES6 21.2.5.9. +// ES#sec-getsubstitution +// 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; + var capturesLength = captures.length; + var tailPos = position + matchLength; + var result = ""; + var pos, expansion, peek, next, scaledIndex, advance, newScaledIndex; + + var next = %StringIndexOf(replacement, '$', 0); + if (next < 0) { + result += replacement; + return result; + } + + if (next > 0) result += %_SubString(replacement, 0, next); + + while (true) { + expansion = '$'; + pos = next + 1; + if (pos < replacement.length) { + peek = %_StringCharCodeAt(replacement, pos); + if (peek == 36) { // $$ + ++pos; + result += '$'; + } else if (peek == 38) { // $& - match + ++pos; + result += matched; + } else if (peek == 96) { // $` - prefix + ++pos; + result += %_SubString(string, 0, position); + } else if (peek == 39) { // $' - suffix + ++pos; + result += %_SubString(string, tailPos, stringLength); + } else if (peek >= 48 && peek <= 57) { + // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99 + scaledIndex = (peek - 48); + advance = 1; + if (pos + 1 < replacement.length) { + next = %_StringCharCodeAt(replacement, pos + 1); + if (next >= 48 && next <= 57) { + newScaledIndex = scaledIndex * 10 + ((next - 48)); + if (newScaledIndex < capturesLength) { + scaledIndex = newScaledIndex; + advance = 2; + } + } + } + if (scaledIndex != 0 && scaledIndex < capturesLength) { + var capture = captures[scaledIndex]; + if (!IS_UNDEFINED(capture)) result += capture; + pos += advance; + } else { + result += '$'; + } + } else { + result += '$'; + } + } else { + result += '$'; + } + + // Go the the next $ in the replacement. + next = %StringIndexOf(replacement, '$', pos); + + // Return if there are no more $ characters in the replacement. If we + // haven't reached the end, we need to append the suffix. + if (next < 0) { + if (pos < replacement.length) { + result += %_SubString(replacement, pos, replacement.length); + } + return result; + } + + // Append substring between the previous and the next $ character. + if (next > pos) { + result += %_SubString(replacement, pos, next); + } + } + 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 MakeTypeError(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 with respect to this.global + // and this.sticky, but hopefully the spec will remove those gets + // and thus make the assumption of 'exec' having no side-effects + // more correct. Also, we doesn't ensure that 'exec' is actually + // a data property on RegExp.prototype, nor does the fast path + // correctly handle lastIndex setting. + var exec; + if (IS_REGEXP(this)) { + exec = this.exec; + if (exec === RegExpSubclassExecJS) { + 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 { + replacement = GetSubstitution(matched, string, position, captures, + 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); + + +// Legacy implementation of RegExp.prototype[Symbol.search] which +// doesn't properly use the overridden exec method function RegExpSearch(string) { - // TODO(yangguo): allow non-regexp receivers. if (!IS_REGEXP(this)) { throw MakeTypeError(kIncompatibleMethodReceiver, "RegExp.prototype.@@search", this); @@ -578,6 +969,24 @@ function RegExpSearch(string) { } +// ES#sec-regexp.prototype-@@search +// RegExp.prototype [ @@search ] ( string ) +function RegExpSubclassSearch(string) { + if (!IS_RECEIVER(this)) { + throw MakeTypeError(kIncompatibleMethodReceiver, + "RegExp.prototype.@@search", this); + } + string = TO_STRING(string); + var previousLastIndex = this.lastIndex; + this.lastIndex = 0; + var result = RegExpSubclassExec(this, string); + this.lastIndex = previousLastIndex; + if (IS_NULL(result)) return -1; + return result.index; +} +%FunctionRemovePrototype(RegExpSubclassSearch); + + // Getters for the static properties lastMatch, lastParen, leftContext, and // rightContext of the RegExp constructor. The properties are computed based // on the captures array of the last successful match and the subject string @@ -639,19 +1048,35 @@ function RegExpMakeCaptureGetter(n) { } +// ES6 21.2.5.3. +function RegExpGetFlags() { + if (!IS_RECEIVER(this)) { + throw MakeTypeError( + kRegExpNonObject, "RegExp.prototype.flags", TO_STRING(this)); + } + var result = ''; + if (this.global) result += 'g'; + if (this.ignoreCase) result += 'i'; + if (this.multiline) result += 'm'; + if (this.unicode) result += 'u'; + if (this.sticky) result += 'y'; + return result; +} + + // ES6 21.2.5.4. function RegExpGetGlobal() { if (!IS_REGEXP(this)) { // TODO(littledan): Remove this RegExp compat workaround if (this === GlobalRegExpPrototype) { + %IncrementUseCounter(kRegExpPrototypeOldFlagGetter); return UNDEFINED; } throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.global"); } - return !!REGEXP_GLOBAL(this); + return TO_BOOLEAN(REGEXP_GLOBAL(this)); } -%FunctionSetName(RegExpGetGlobal, "RegExp.prototype.global"); -%SetNativeFlag(RegExpGetGlobal); +%SetForceInlineFlag(RegExpGetGlobal); // ES6 21.2.5.5. @@ -659,14 +1084,13 @@ function RegExpGetIgnoreCase() { if (!IS_REGEXP(this)) { // TODO(littledan): Remove this RegExp compat workaround if (this === GlobalRegExpPrototype) { + %IncrementUseCounter(kRegExpPrototypeOldFlagGetter); return UNDEFINED; } throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.ignoreCase"); } - return !!REGEXP_IGNORE_CASE(this); + return TO_BOOLEAN(REGEXP_IGNORE_CASE(this)); } -%FunctionSetName(RegExpGetIgnoreCase, "RegExp.prototype.ignoreCase"); -%SetNativeFlag(RegExpGetIgnoreCase); // ES6 21.2.5.7. @@ -674,14 +1098,13 @@ function RegExpGetMultiline() { if (!IS_REGEXP(this)) { // TODO(littledan): Remove this RegExp compat workaround if (this === GlobalRegExpPrototype) { + %IncrementUseCounter(kRegExpPrototypeOldFlagGetter); return UNDEFINED; } throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.multiline"); } - return !!REGEXP_MULTILINE(this); + return TO_BOOLEAN(REGEXP_MULTILINE(this)); } -%FunctionSetName(RegExpGetMultiline, "RegExp.prototype.multiline"); -%SetNativeFlag(RegExpGetMultiline); // ES6 21.2.5.10. @@ -689,14 +1112,29 @@ function RegExpGetSource() { if (!IS_REGEXP(this)) { // TODO(littledan): Remove this RegExp compat workaround if (this === GlobalRegExpPrototype) { - return UNDEFINED; + %IncrementUseCounter(kRegExpPrototypeSourceGetter); + return "(?:)"; } throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.source"); } return REGEXP_SOURCE(this); } -%FunctionSetName(RegExpGetSource, "RegExp.prototype.source"); -%SetNativeFlag(RegExpGetSource); + + +// ES6 21.2.5.12. +function RegExpGetSticky() { + if (!IS_REGEXP(this)) { + // Compat fix: RegExp.prototype.sticky == undefined; UseCounter tracks it + // TODO(littledan): Remove this workaround or standardize it + if (this === GlobalRegExpPrototype) { + %IncrementUseCounter(kRegExpPrototypeStickyGetter); + return UNDEFINED; + } + throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.sticky"); + } + return TO_BOOLEAN(REGEXP_STICKY(this)); +} +%SetForceInlineFlag(RegExpGetSticky); // ------------------------------------------------------------------- @@ -718,10 +1156,12 @@ utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [ splitSymbol, RegExpSplit, ]); +utils.InstallGetter(GlobalRegExp.prototype, 'flags', RegExpGetFlags); utils.InstallGetter(GlobalRegExp.prototype, 'global', RegExpGetGlobal); utils.InstallGetter(GlobalRegExp.prototype, 'ignoreCase', RegExpGetIgnoreCase); utils.InstallGetter(GlobalRegExp.prototype, 'multiline', RegExpGetMultiline); utils.InstallGetter(GlobalRegExp.prototype, 'source', RegExpGetSource); +utils.InstallGetter(GlobalRegExp.prototype, 'sticky', RegExpGetSticky); // The properties `input` and `$_` are aliases for each other. When this // value is set the value it is set to is coerced to a string. @@ -769,12 +1209,39 @@ for (var i = 1; i < 10; ++i) { %ToFastProperties(GlobalRegExp); // ------------------------------------------------------------------- +// Internal + +var InternalRegExpMatchInfo = new InternalPackedArray(2, "", UNDEFINED, 0, 0); + +function InternalRegExpMatch(regexp, subject) { + var matchInfo = %_RegExpExec(regexp, subject, 0, InternalRegExpMatchInfo); + if (!IS_NULL(matchInfo)) { + RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, subject); + } + return null; +} + +function InternalRegExpReplace(regexp, subject, replacement) { + return %StringReplaceGlobalRegExpWithString( + subject, regexp, replacement, InternalRegExpMatchInfo); +} + +// ------------------------------------------------------------------- // Exports utils.Export(function(to) { + to.InternalRegExpMatch = InternalRegExpMatch; + to.InternalRegExpReplace = InternalRegExpReplace; + to.IsRegExp = IsRegExp; to.RegExpExec = DoRegExpExec; - to.RegExpExecNoTests = RegExpExecNoTests; + to.RegExpInitialize = RegExpInitialize; to.RegExpLastMatchInfo = RegExpLastMatchInfo; + to.RegExpSubclassExecJS = RegExpSubclassExecJS; + to.RegExpSubclassMatch = RegExpSubclassMatch; + to.RegExpSubclassReplace = RegExpSubclassReplace; + to.RegExpSubclassSearch = RegExpSubclassSearch; + to.RegExpSubclassSplit = RegExpSubclassSplit; + to.RegExpSubclassTest = RegExpSubclassTest; to.RegExpTest = RegExpTest; }); |