Index: src/js/regexp.js |
diff --git a/src/js/regexp.js b/src/js/regexp.js |
index 611f780612d35a6628f3f9524d852c37a2238294..aaa9ed9b4f4c9c4f55db4ed086bf0a158c4b15ca 100644 |
--- a/src/js/regexp.js |
+++ b/src/js/regexp.js |
@@ -267,8 +267,12 @@ function RegExpExecJS(string) { |
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) |
-function RegExpSubclassExec(regexp, string) { |
- var exec = regexp.exec; |
+// 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)) { |
@@ -278,6 +282,7 @@ function RegExpSubclassExec(regexp, string) { |
} |
return %_Call(RegExpExecJS, regexp, string); |
} |
+%SetForceInlineFlag(RegExpSubclassExec); |
// One-element cache for the simplified test regexp. |
@@ -483,6 +488,20 @@ function RegExpSubclassSplit(string, limit) { |
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"; |
@@ -502,7 +521,9 @@ function RegExpSubclassSplit(string, limit) { |
var stringIndex = prevStringIndex; |
while (stringIndex < size) { |
splitter.lastIndex = stringIndex; |
- result = RegExpSubclassExec(splitter, string); |
+ 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 { |
@@ -561,20 +582,23 @@ function RegExpSubclassMatch(string) { |
if (!global) return RegExpSubclassExec(this, string); |
var unicode = this.unicode; |
this.lastIndex = 0; |
- var array = []; |
+ var array = new InternalArray(); |
var n = 0; |
var result; |
while (true) { |
result = RegExpSubclassExec(this, string); |
if (IS_NULL(result)) { |
if (n === 0) return null; |
- return array; |
+ break; |
} |
var matchStr = TO_STRING(result[0]); |
- %AddElement(array, n, matchStr); |
+ array[n] = matchStr; |
if (matchStr === "") SetAdvancedStringIndex(this, string, unicode); |
n++; |
} |
+ var resultArray = []; |
+ %MoveArrayContents(array, resultArray); |
+ return resultArray; |
} |
%FunctionRemovePrototype(RegExpSubclassMatch); |
@@ -871,15 +895,32 @@ function RegExpSubclassReplace(string, replace) { |
var length = string.length; |
var functionalReplace = IS_CALLABLE(replace); |
if (!functionalReplace) replace = TO_STRING(replace); |
- var global = this.global; |
+ var global = TO_BOOLEAN(this.global); |
if (global) { |
- var unicode = this.unicode; |
+ 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); |
+ result = RegExpSubclassExec(this, string, exec); |
+ // Ensure exec will be read again on the next loop through. |
+ exec = UNDEFINED; |
if (IS_NULL(result)) { |
break; |
} else { |
@@ -1046,8 +1087,9 @@ function RegExpGetGlobal() { |
} |
throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.global"); |
} |
- return !!REGEXP_GLOBAL(this); |
+ return TO_BOOLEAN(REGEXP_GLOBAL(this)); |
} |
+%SetForceInlineFlag(RegExpGetGlobal); |
// ES6 21.2.5.5. |
@@ -1060,7 +1102,7 @@ function RegExpGetIgnoreCase() { |
} |
throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.ignoreCase"); |
} |
- return !!REGEXP_IGNORE_CASE(this); |
+ return TO_BOOLEAN(REGEXP_IGNORE_CASE(this)); |
} |
@@ -1074,7 +1116,7 @@ function RegExpGetMultiline() { |
} |
throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.multiline"); |
} |
- return !!REGEXP_MULTILINE(this); |
+ return TO_BOOLEAN(REGEXP_MULTILINE(this)); |
} |
@@ -1103,8 +1145,9 @@ function RegExpGetSticky() { |
} |
throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.sticky"); |
} |
- return !!REGEXP_STICKY(this); |
+ return TO_BOOLEAN(REGEXP_STICKY(this)); |
} |
+%SetForceInlineFlag(RegExpGetSticky); |
// ------------------------------------------------------------------- |