diff options
Diffstat (limited to 'js/src/builtin/RegExp.js')
-rw-r--r-- | js/src/builtin/RegExp.js | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/js/src/builtin/RegExp.js b/js/src/builtin/RegExp.js new file mode 100644 index 000000000..1ffea0105 --- /dev/null +++ b/js/src/builtin/RegExp.js @@ -0,0 +1,1032 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// ES6 draft rev34 (2015/02/20) 21.2.5.3 get RegExp.prototype.flags +function RegExpFlagsGetter() { + // Steps 1-2. + var R = this; + if (!IsObject(R)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R); + + // Step 3. + var result = ""; + + // Steps 4-6. + if (R.global) + result += "g"; + + // Steps 7-9. + if (R.ignoreCase) + result += "i"; + + // Steps 10-12. + if (R.multiline) + result += "m"; + + // Steps 13-15. + if (R.unicode) + result += "u"; + + // Steps 16-18. + if (R.sticky) + result += "y"; + + // Step 19. + return result; +} +_SetCanonicalName(RegExpFlagsGetter, "get flags"); + +// ES 2017 draft 40edb3a95a475c1b251141ac681b8793129d9a6d 21.2.5.14. +function RegExpToString() +{ + // Step 1. + var R = this; + + // Step 2. + if (!IsObject(R)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R); + + // Step 3. + var pattern = ToString(R.source); + + // Step 4. + var flags = ToString(R.flags); + + // Steps 5-6. + return '/' + pattern + '/' + flags; +} +_SetCanonicalName(RegExpToString, "toString"); + +// ES 2016 draft Mar 25, 2016 21.2.5.2.3. +function AdvanceStringIndex(S, index) { + // Step 1. + assert(typeof S === "string", "Expected string as 1st argument"); + + // Step 2. + assert(index >= 0 && index <= MAX_NUMERIC_INDEX, "Expected integer as 2nd argument"); + + // Step 3 (skipped). + + // Step 4 (skipped). + + // Step 5. + var length = S.length; + + // Step 6. + if (index + 1 >= length) + return index + 1; + + // Step 7. + var first = callFunction(std_String_charCodeAt, S, index); + + // Step 8. + if (first < 0xD800 || first > 0xDBFF) + return index + 1; + + // Step 9. + var second = callFunction(std_String_charCodeAt, S, index + 1); + + // Step 10. + if (second < 0xDC00 || second > 0xDFFF) + return index + 1; + + // Step 11. + return index + 2; +} + +// ES 2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.6. +function RegExpMatch(string) { + // Step 1. + var rx = this; + + // Step 2. + if (!IsObject(rx)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx); + + // Step 3. + var S = ToString(string); + + // Optimized paths for simple cases. + if (IsRegExpMethodOptimizable(rx)) { + // Step 4. + var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT); + var global = !!(flags & REGEXP_GLOBAL_FLAG); + + if (global) { + // Step 6.a. + var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG); + + // Steps 6.b-e. + return RegExpGlobalMatchOpt(rx, S, fullUnicode); + } + + // Step 5. + var sticky = !!(flags & REGEXP_STICKY_FLAG); + return RegExpLocalMatchOpt(rx, S, sticky); + } + + // Stes 4-6 + return RegExpMatchSlowPath(rx, S); +} + +// ES 2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.6 +// steps 4-6. +function RegExpMatchSlowPath(rx, S) { + // Steps 4-5. + if (!rx.global) + return RegExpExec(rx, S, false); + + // Step 6.a. + var fullUnicode = !!rx.unicode; + + // Step 6.b. + rx.lastIndex = 0; + + // Step 6.c. + var A = []; + + // Step 6.d. + var n = 0; + + // Step 6.e. + while (true) { + // Step 6.e.i. + var result = RegExpExec(rx, S, false); + + // Step 6.e.ii. + if (result === null) + return (n === 0) ? null : A; + + // Step 6.e.iii.1. + var matchStr = ToString(result[0]); + + // Step 6.e.iii.2. + _DefineDataProperty(A, n, matchStr); + + // Step 6.e.iii.4. + if (matchStr === "") { + var lastIndex = ToLength(rx.lastIndex); + rx.lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1; + } + + // Step 6.e.iii.5. + n++; + } +} + +// ES 2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.6. +// Steps 6.b-e. +// Optimized path for @@match with global flag. +function RegExpGlobalMatchOpt(rx, S, fullUnicode) { + // Step 6.b. + var lastIndex = 0; + rx.lastIndex = 0; + + // Step 6.c. + var A = []; + + // Step 6.d. + var n = 0; + + var lengthS = S.length; + + // Step 6.e. + while (true) { + // Step 6.e.i. + var result = RegExpMatcher(rx, S, lastIndex); + + // Step 6.e.ii. + if (result === null) + return (n === 0) ? null : A; + + lastIndex = result.index + result[0].length; + + // Step 6.e.iii.1. + var matchStr = result[0]; + + // Step 6.e.iii.2. + _DefineDataProperty(A, n, matchStr); + + // Step 6.e.iii.4. + if (matchStr === "") { + lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1; + if (lastIndex > lengthS) + return A; + } + + // Step 6.e.iii.5. + n++; + } +} + +// ES 2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.6 step 5. +// Optimized path for @@match without global flag. +function RegExpLocalMatchOpt(rx, S, sticky) { + // Step 4. + var lastIndex = ToLength(rx.lastIndex); + + // Step 8. + if (!sticky) { + lastIndex = 0; + } else { + if (lastIndex > S.length) { + // Steps 12.a.i-ii, 12.c.i.1-2. + rx.lastIndex = 0; + return null; + } + } + + // Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15. + var result = RegExpMatcher(rx, S, lastIndex); + if (result === null) { + // Steps 12.a.i-ii, 12.c.i.1-2. + rx.lastIndex = 0; + } else { + // Step 15. + if (sticky) + rx.lastIndex = result.index + result[0].length; + } + + return result; +} + +// Checks if following properties and getters are not modified, and accessing +// them not observed by content script: +// * flags +// * global +// * ignoreCase +// * multiline +// * sticky +// * unicode +// * exec +// * lastIndex +function IsRegExpMethodOptimizable(rx) { + if (!IsRegExpObject(rx)) + return false; + + var RegExpProto = GetBuiltinPrototype("RegExp"); + // If RegExpPrototypeOptimizable and RegExpInstanceOptimizable succeed, + // `RegExpProto.exec` is guaranteed to be data properties. + return RegExpPrototypeOptimizable(RegExpProto) && + RegExpInstanceOptimizable(rx, RegExpProto) && + RegExpProto.exec === RegExp_prototype_Exec; +} + +// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8. +function RegExpReplace(string, replaceValue) { + // Step 1. + var rx = this; + + // Step 2. + if (!IsObject(rx)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx); + + // Step 3. + var S = ToString(string); + + // Step 4. + var lengthS = S.length; + + // Step 5. + var functionalReplace = IsCallable(replaceValue); + + // Step 6. + var firstDollarIndex = -1; + if (!functionalReplace) { + // Step 6.a. + replaceValue = ToString(replaceValue); + + // Skip if replaceValue is an empty string or a single character. + // A single character string may contain "$", but that cannot be a + // substitution. + if (replaceValue.length > 1) + firstDollarIndex = GetFirstDollarIndex(replaceValue); + } + + // Optimized paths. + if (IsRegExpMethodOptimizable(rx)) { + var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT); + + // Step 7. + var global = !!(flags & REGEXP_GLOBAL_FLAG); + + // Steps 8-16. + if (global) { + // Step 8.a. + var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG); + + if (functionalReplace) { + var elemBase = GetElemBaseForLambda(replaceValue); + if (IsObject(elemBase)) + return RegExpGlobalReplaceOptElemBase(rx, S, lengthS, replaceValue, + fullUnicode, elemBase); + return RegExpGlobalReplaceOptFunc(rx, S, lengthS, replaceValue, + fullUnicode); + } + if (firstDollarIndex !== -1) { + return RegExpGlobalReplaceOptSubst(rx, S, lengthS, replaceValue, + fullUnicode, firstDollarIndex); + } + if (lengthS < 0x7fff) { + return RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue, + fullUnicode); + } + return RegExpGlobalReplaceOpt(rx, S, lengthS, replaceValue, + fullUnicode); + } + + var sticky = !!(flags & REGEXP_STICKY_FLAG); + + if (functionalReplace) { + return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue, + sticky); + } + if (firstDollarIndex !== -1) { + return RegExpLocalReplaceOptSubst(rx, S, lengthS, replaceValue, + sticky, firstDollarIndex); + } + return RegExpLocalReplaceOpt(rx, S, lengthS, replaceValue, + sticky); + } + + // Steps 8-16. + return RegExpReplaceSlowPath(rx, S, lengthS, replaceValue, + functionalReplace, firstDollarIndex); +} + +// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8 +// steps 7-16. +// Slow path for @@replace. +function RegExpReplaceSlowPath(rx, S, lengthS, replaceValue, + functionalReplace, firstDollarIndex) +{ + // Step 7. + var global = !!rx.global; + + // Step 8. + var fullUnicode = false; + if (global) { + // Step 8.a. + fullUnicode = !!rx.unicode; + + // Step 8.b. + rx.lastIndex = 0; + } + + // Step 9. + var results = []; + var nResults = 0; + + // Step 11. + while (true) { + // Step 11.a. + var result = RegExpExec(rx, S, false); + + // Step 11.b. + if (result === null) + break; + + // Step 11.c.i. + _DefineDataProperty(results, nResults++, result); + + // Step 11.c.ii. + if (!global) + break; + + // Step 11.c.iii.1. + var matchStr = ToString(result[0]); + + // Step 11.c.iii.2. + if (matchStr === "") { + var lastIndex = ToLength(rx.lastIndex); + rx.lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1; + } + } + + // Step 12. + var accumulatedResult = ""; + + // Step 13. + var nextSourcePosition = 0; + + // Step 14. + for (var i = 0; i < nResults; i++) { + result = results[i]; + + // Steps 14.a-b. + var nCaptures = std_Math_max(ToLength(result.length) - 1, 0); + + // Step 14.c. + var matched = ToString(result[0]); + + // Step 14.d. + var matchLength = matched.length; + + // Steps 14.e-f. + var position = std_Math_max(std_Math_min(ToInteger(result.index), lengthS), 0); + + var n, capN, replacement; + if (functionalReplace || firstDollarIndex !== -1) { + // Steps 14.g-j. + replacement = RegExpGetComplexReplacement(result, matched, S, position, + + nCaptures, replaceValue, + functionalReplace, firstDollarIndex); + } else { + // Step 14.g, 14.i, 14.i.iv. + // We don't need captures array, but ToString is visible to script. + for (n = 1; n <= nCaptures; n++) { + // Step 14.i.i-ii. + capN = result[n]; + + // Step 14.i.ii. + if (capN !== undefined) + ToString(capN); + } + replacement = replaceValue; + } + + // Step 14.l. + if (position >= nextSourcePosition) { + // Step 14.l.ii. + accumulatedResult += Substring(S, nextSourcePosition, + position - nextSourcePosition) + replacement; + + // Step 14.l.iii. + nextSourcePosition = position + matchLength; + } + } + + // Step 15. + if (nextSourcePosition >= lengthS) + return accumulatedResult; + + // Step 16. + return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition); +} + +// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8 +// steps 14.g-k. +// Calculates functional/substitution replaceement from match result. +// Used in the following functions: +// * RegExpGlobalReplaceOptFunc +// * RegExpGlobalReplaceOptElemBase +// * RegExpGlobalReplaceOptSubst +// * RegExpLocalReplaceOptFunc +// * RegExpLocalReplaceOptSubst +// * RegExpReplaceSlowPath +function RegExpGetComplexReplacement(result, matched, S, position, + nCaptures, replaceValue, + functionalReplace, firstDollarIndex) +{ + // Step 14.h. + var captures = []; + var capturesLength = 0; + + // Step 14.j.i (reordered). + // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise + // use `std_Function_apply` with all arguments stored in `captures`. + // In latter case, store `matched` as the first element here, to + // avoid unshift later. + if (functionalReplace && nCaptures > 4) + _DefineDataProperty(captures, capturesLength++, matched); + + // Step 14.g, 14.i, 14.i.iv. + for (var n = 1; n <= nCaptures; n++) { + // Step 14.i.i. + var capN = result[n]; + + // Step 14.i.ii. + if (capN !== undefined) + capN = ToString(capN); + + // Step 14.i.iii. + _DefineDataProperty(captures, capturesLength++, capN); + } + + // Step 14.j. + if (functionalReplace) { + switch (nCaptures) { + case 0: + return ToString(replaceValue(matched, position, S)); + case 1: + return ToString(replaceValue(matched, SPREAD(captures, 1), position, S)); + case 2: + return ToString(replaceValue(matched, SPREAD(captures, 2), position, S)); + case 3: + return ToString(replaceValue(matched, SPREAD(captures, 3), position, S)); + case 4: + return ToString(replaceValue(matched, SPREAD(captures, 4), position, S)); + default: + // Steps 14.j.ii-v. + _DefineDataProperty(captures, capturesLength++, position); + _DefineDataProperty(captures, capturesLength++, S); + return ToString(callFunction(std_Function_apply, replaceValue, null, captures)); + } + } + + // Steps 14.k.i. + return RegExpGetSubstitution(matched, S, position, captures, replaceValue, + firstDollarIndex); +} + +// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8 +// steps 8.b-16. +// Optimized path for @@replace with the following conditions: +// * global flag is true +// * S is a short string (lengthS < 0x7fff) +// * replaceValue is a string without "$" +function RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue, fullUnicode) +{ + // Step 8.b. + var lastIndex = 0; + rx.lastIndex = 0; + + // Step 12 (reordered). + var accumulatedResult = ""; + + // Step 13 (reordered). + var nextSourcePosition = 0; + + // Step 11. + while (true) { + // Step 11.a. + var result = RegExpSearcher(rx, S, lastIndex); + + // Step 11.b. + if (result === -1) + break; + + var position = result & 0x7fff; + lastIndex = (result >> 15) & 0x7fff; + + // Step 14.l.ii. + accumulatedResult += Substring(S, nextSourcePosition, + position - nextSourcePosition) + replaceValue; + + // Step 14.l.iii. + nextSourcePosition = lastIndex; + + // Step 11.c.iii.2. + if (lastIndex === position) { + lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1; + if (lastIndex > lengthS) + break; + } + } + + // Step 15. + if (nextSourcePosition >= lengthS) + return accumulatedResult; + + // Step 16. + return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition); +} + +// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8 +// steps 8-16. +// Optimized path for @@replace. + +// Conditions: +// * global flag is true +// * replaceValue is a string without "$" +#define FUNC_NAME RegExpGlobalReplaceOpt +#include "RegExpGlobalReplaceOpt.h.js" +#undef FUNC_NAME + +// Conditions: +// * global flag is true +// * replaceValue is a function +#define FUNC_NAME RegExpGlobalReplaceOptFunc +#define FUNCTIONAL +#include "RegExpGlobalReplaceOpt.h.js" +#undef FUNCTIONAL +#undef FUNC_NAME + +// Conditions: +// * global flag is true +// * replaceValue is a function that returns element of an object +#define FUNC_NAME RegExpGlobalReplaceOptElemBase +#define ELEMBASE +#include "RegExpGlobalReplaceOpt.h.js" +#undef ELEMBASE +#undef FUNC_NAME + +// Conditions: +// * global flag is true +// * replaceValue is a string with "$" +#define FUNC_NAME RegExpGlobalReplaceOptSubst +#define SUBSTITUTION +#include "RegExpGlobalReplaceOpt.h.js" +#undef SUBSTITUTION +#undef FUNC_NAME + +// Conditions: +// * global flag is false +// * replaceValue is a string without "$" +#define FUNC_NAME RegExpLocalReplaceOpt +#include "RegExpLocalReplaceOpt.h.js" +#undef FUNC_NAME + +// Conditions: +// * global flag is false +// * replaceValue is a function +#define FUNC_NAME RegExpLocalReplaceOptFunc +#define FUNCTIONAL +#include "RegExpLocalReplaceOpt.h.js" +#undef FUNCTIONAL +#undef FUNC_NAME + +// Conditions: +// * global flag is false +// * replaceValue is a string with "$" +#define FUNC_NAME RegExpLocalReplaceOptSubst +#define SUBSTITUTION +#include "RegExpLocalReplaceOpt.h.js" +#undef SUBSTITUTION +#undef FUNC_NAME + +// ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.9. +function RegExpSearch(string) { + // Step 1. + var rx = this; + + // Step 2. + if (!IsObject(rx)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx); + + // Step 3. + var S = ToString(string); + + if (IsRegExpMethodOptimizable(rx) && S.length < 0x7fff) { + // Step 6. + var result = RegExpSearcher(rx, S, 0); + + // Step 8. + if (result === -1) + return -1; + + // Step 9. + return result & 0x7fff; + } + + return RegExpSearchSlowPath(rx, S); +} + +// ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.9 +// steps 4-9. +function RegExpSearchSlowPath(rx, S) { + // Step 4. + var previousLastIndex = rx.lastIndex; + + // Step 5. + rx.lastIndex = 0; + + // Step 6. + var result = RegExpExec(rx, S, false); + + // Step 7. + rx.lastIndex = previousLastIndex; + + // Step 8. + if (result === null) + return -1; + + // Step 9. + return result.index; +} + +function IsRegExpSplitOptimizable(rx, C) { + if (!IsRegExpObject(rx)) + return false; + + var RegExpCtor = GetBuiltinConstructor("RegExp"); + if (C !== RegExpCtor) + return false; + + var RegExpProto = RegExpCtor.prototype; + // If RegExpPrototypeOptimizable succeeds, `RegExpProto.exec` is guaranteed + // to be a data property. + return RegExpPrototypeOptimizable(RegExpProto) && + RegExpInstanceOptimizable(rx, RegExpProto) && + RegExpProto.exec === RegExp_prototype_Exec; +} + +// ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.11. +function RegExpSplit(string, limit) { + // Step 1. + var rx = this; + + // Step 2. + if (!IsObject(rx)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx); + + // Step 3. + var S = ToString(string); + + // Step 4. + var C = SpeciesConstructor(rx, GetBuiltinConstructor("RegExp")); + + var optimizable = IsRegExpSplitOptimizable(rx, C) && + (limit === undefined || typeof limit == "number"); + + var flags, unicodeMatching, splitter; + if (optimizable) { + // Step 5. + flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT); + + // Steps 6-7. + unicodeMatching = !!(flags & (REGEXP_UNICODE_FLAG)); + + // Steps 8-10. + // If split operation is optimizable, perform non-sticky match. + splitter = regexp_construct_raw_flags(rx, flags & ~REGEXP_STICKY_FLAG); + } else { + // Step 5. + flags = ToString(rx.flags); + + // Steps 6-7. + unicodeMatching = callFunction(std_String_includes, flags, "u"); + + // Steps 8-9. + var newFlags; + if (callFunction(std_String_includes, flags, "y")) + newFlags = flags; + else + newFlags = flags + "y"; + + // Step 10. + splitter = new C(rx, newFlags); + } + + // Step 11. + var A = []; + + // Step 12. + var lengthA = 0; + + // Step 13. + var lim; + if (limit === undefined) + lim = MAX_NUMERIC_INDEX; + else + lim = limit >>> 0; + + // Step 15. + var p = 0; + + // Step 16. + if (lim === 0) + return A; + + // Step 14 (reordered). + var size = S.length; + + // Step 17. + if (size === 0) { + // Step 17.a. + var z; + if (optimizable) + z = RegExpMatcher(splitter, S, 0); + else + z = RegExpExec(splitter, S, false); + + // Step 17.b. + if (z !== null) + return A; + + // Step 17.d. + _DefineDataProperty(A, 0, S); + + // Step 17.e. + return A; + } + + // Step 18. + var q = p; + + // Step 19. + while (q < size) { + var e; + if (optimizable) { + // Step 19.a (skipped). + // splitter.lastIndex is not used. + + // Step 19.b. + z = RegExpMatcher(splitter, S, q); + + // Step 19.c. + if (z === null) + break; + + // splitter.lastIndex is not updated. + q = z.index; + if (q >= size) + break; + + // Step 19.d.i. + e = q + z[0].length; + } else { + // Step 19.a. + splitter.lastIndex = q; + + // Step 19.b. + z = RegExpExec(splitter, S, false); + + // Step 19.c. + if (z === null) { + q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1; + continue; + } + + // Step 19.d.i. + e = ToLength(splitter.lastIndex); + } + + // Step 19.d.iii. + if (e === p) { + q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1; + continue; + } + + // Steps 19.d.iv.1-3. + _DefineDataProperty(A, lengthA, Substring(S, p, q - p)); + + // Step 19.d.iv.4. + lengthA++; + + // Step 19.d.iv.5. + if (lengthA === lim) + return A; + + // Step 19.d.iv.6. + p = e; + + // Steps 19.d.iv.7-8. + var numberOfCaptures = std_Math_max(ToLength(z.length) - 1, 0); + + // Step 19.d.iv.9. + var i = 1; + + // Step 19.d.iv.10. + while (i <= numberOfCaptures) { + // Steps 19.d.iv.10.a-b. + _DefineDataProperty(A, lengthA, z[i]); + + // Step 19.d.iv.10.c. + i++; + + // Step 19.d.iv.10.d. + lengthA++; + + // Step 19.d.iv.10.e. + if (lengthA === lim) + return A; + } + + // Step 19.d.iv.11. + q = p; + } + + // Steps 20-22. + if (p >= size) + _DefineDataProperty(A, lengthA, ""); + else + _DefineDataProperty(A, lengthA, Substring(S, p, size - p)); + + // Step 23. + return A; +} + +// ES6 21.2.5.2. +// NOTE: This is not RegExpExec (21.2.5.2.1). +function RegExp_prototype_Exec(string) { + // Steps 1-3. + var R = this; + if (!IsObject(R) || !IsRegExpObject(R)) + return callFunction(CallRegExpMethodIfWrapped, R, string, "RegExp_prototype_Exec"); + + // Steps 4-5. + var S = ToString(string); + + // Step 6. + return RegExpBuiltinExec(R, S, false); +} + +// ES6 21.2.5.2.1. +function RegExpExec(R, S, forTest) { + // Steps 1-2 (skipped). + + // Steps 3-4. + var exec = R.exec; + + // Step 5. + // If exec is the original RegExp.prototype.exec, use the same, faster, + // path as for the case where exec isn't callable. + if (exec === RegExp_prototype_Exec || !IsCallable(exec)) { + // ES6 21.2.5.2 steps 1-2, 4-5 (skipped) for optimized case. + + // Steps 6-7 or ES6 21.2.5.2 steps 3, 6 for optimized case. + return RegExpBuiltinExec(R, S, forTest); + } + + // Steps 5.a-b. + var result = callContentFunction(exec, R, S); + + // Step 5.c. + if (typeof result !== "object") + ThrowTypeError(JSMSG_EXEC_NOT_OBJORNULL); + + // Step 5.d. + return forTest ? result !== null : result; +} + +// ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2. +function RegExpBuiltinExec(R, S, forTest) { + // ES6 21.2.5.2.1 step 6. + // This check is here for RegExpTest. RegExp_prototype_Exec does same + // thing already. + if (!IsRegExpObject(R)) + return UnwrapAndCallRegExpBuiltinExec(R, S, forTest); + + // Steps 1-2 (skipped). + + // Step 4. + var lastIndex = ToLength(R.lastIndex); + + // Step 5. + var flags = UnsafeGetInt32FromReservedSlot(R, REGEXP_FLAGS_SLOT); + + // Steps 6-7. + var globalOrSticky = !!(flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)); + + // Step 8. + if (!globalOrSticky) { + lastIndex = 0; + } else { + if (lastIndex > S.length) { + // Steps 12.a.i-ii, 12.c.i.1-2. + R.lastIndex = 0; + return forTest ? false : null; + } + } + + if (forTest) { + // Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15. + var endIndex = RegExpTester(R, S, lastIndex); + if (endIndex == -1) { + // Steps 12.a.i-ii, 12.c.i.1-2. + R.lastIndex = 0; + return false; + } + + // Step 15. + if (globalOrSticky) + R.lastIndex = endIndex; + + return true; + } + + // Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15. + var result = RegExpMatcher(R, S, lastIndex); + if (result === null) { + // Steps 12.a.i-ii, 12.c.i.1-2. + R.lastIndex = 0; + } else { + // Step 15. + if (globalOrSticky) + R.lastIndex = result.index + result[0].length; + } + + return result; +} + +function UnwrapAndCallRegExpBuiltinExec(R, S, forTest) { + return callFunction(CallRegExpMethodIfWrapped, R, S, forTest, "CallRegExpBuiltinExec"); +} + +function CallRegExpBuiltinExec(S, forTest) { + return RegExpBuiltinExec(this, S, forTest); +} + +// ES6 21.2.5.13. +function RegExpTest(string) { + // Steps 1-2. + var R = this; + if (!IsObject(R)) + ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R); + + // Steps 3-4. + var S = ToString(string); + + // Steps 5-6. + return RegExpExec(R, S, true); +} + +// ES 2016 draft Mar 25, 2016 21.2.4.2. +function RegExpSpecies() { + // Step 1. + return this; +} +_SetCanonicalName(RegExpSpecies, "get [Symbol.species]"); |