summaryrefslogtreecommitdiff
path: root/deps/v8/src/js/regexp.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/js/regexp.js')
-rw-r--r--deps/v8/src/js/regexp.js555
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;
});