/* 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/. */ // ECMAScript 2020 draft (2020/03/12) 21.2.5.4 get RegExp.prototype.flags // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags // Uncloned functions with `$` prefix are allocated as extended function // to store the original name in `_SetCanonicalName`. function $RegExpFlagsGetter() { // Steps 1-2. var R = this; if (!IsObject(R)) ThrowTypeError(JSMSG_OBJECT_REQUIRED, R === null ? "null" : typeof R); // Step 3. var result = ""; // Steps 4-5. if (R.global) result += "g"; // Steps 6-7. if (R.ignoreCase) result += "i"; // Steps 8-9. if (R.multiline) result += "m"; // Steps 10-11. if (R.dotAll) result += "s"; // Steps 12-13. if (R.unicode) result += "u"; // Steps 14-15. if (R.sticky) result += "y"; // Step 16. 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_OBJECT_REQUIRED, 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_OBJECT_REQUIRED, 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. return RegExpBuiltinExec(rx, S, false); } // 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++; } } // Checks if following properties and getters are not modified, and accessing // them not observed by content script: // * flags // * global // * ignoreCase // * multiline // * dotAll // * 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_OBJECT_REQUIRED, 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) { if (functionalReplace) { // For large strings check if the replacer function is // applicable for the elem-base optimization. if (lengthS > 5000) { var elemBase = GetElemBaseForLambda(replaceValue); if (IsObject(elemBase)) { return RegExpGlobalReplaceOptElemBase(rx, S, lengthS, replaceValue, flags, elemBase); } } return RegExpGlobalReplaceOptFunc(rx, S, lengthS, replaceValue, flags); } if (firstDollarIndex !== -1) { return RegExpGlobalReplaceOptSubst(rx, S, lengthS, replaceValue, flags, firstDollarIndex); } if (lengthS < 0x7fff) return RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue, flags); return RegExpGlobalReplaceOpt(rx, S, lengthS, replaceValue, flags); } if (functionalReplace) return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue); if (firstDollarIndex !== -1) return RegExpLocalReplaceOptSubst(rx, S, lengthS, replaceValue, firstDollarIndex); if (lengthS < 0x7fff) return RegExpLocalReplaceOptShort(rx, S, lengthS, replaceValue); return RegExpLocalReplaceOpt(rx, S, lengthS, replaceValue); } // 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-k. 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); } // Step 14.j, 14.l., GetSubstitution Step 11. // We don't need namedCaptures, but ToObject is visible to script. var namedCaptures = result.groups; if (namedCaptures !== undefined) ToObject(namedCaptures); replacement = replaceValue; } // Step 14.m. if (position >= nextSourcePosition) { // Step 14.m.ii. accumulatedResult += Substring(S, nextSourcePosition, position - nextSourcePosition) + replacement; // Step 14.m.iii. nextSourcePosition = position + matchLength; } } // Step 15. if (nextSourcePosition >= lengthS) return accumulatedResult; // Step 16. return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition); } // ES 2021 draft 21.2.5.10 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace // steps 14.g-l. // Calculates functional/substitution replacement from match result. // Used in the following functions: // * RegExpReplaceSlowPath function RegExpGetComplexReplacement(result, matched, S, position, nCaptures, replaceValue, functionalReplace, firstDollarIndex) { // Step 14.h. var captures = []; var capturesLength = 0; // Step 14.k.i (reordered). _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. var namedCaptures = result.groups; // Step 14.k. if (functionalReplace) { // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise // use `std_Function_apply` with all arguments stored in `captures`. if (namedCaptures === undefined) { switch (nCaptures) { case 0: return ToString(replaceValue(SPREAD(captures, 1), position, S)); case 1: return ToString(replaceValue(SPREAD(captures, 2), position, S)); case 2: return ToString(replaceValue(SPREAD(captures, 3), position, S)); case 3: return ToString(replaceValue(SPREAD(captures, 4), position, S)); case 4: return ToString(replaceValue(SPREAD(captures, 5), position, S)); } } // Steps 14.k.ii-v. _DefineDataProperty(captures, capturesLength++, position); _DefineDataProperty(captures, capturesLength++, S); if (namedCaptures !== undefined) { _DefineDataProperty(captures, capturesLength++, namedCaptures); } return ToString(callFunction(std_Function_apply, replaceValue, undefined, captures)); } // Step 14.l. if (namedCaptures !== undefined) { namedCaptures = ToObject(namedCaptures); } return RegExpGetSubstitution(captures, S, position, replaceValue, firstDollarIndex, namedCaptures); } // ES 2021 draft 21.2.5.10 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace // steps 14.g-k. // Calculates functional replacement from match result. // Used in the following functions: // * RegExpGlobalReplaceOptFunc // * RegExpGlobalReplaceOptElemBase // * RegExpLocalReplaceOptFunc function RegExpGetFunctionalReplacement(result, S, position, replaceValue) { // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise // use `std_Function_apply` with all arguments stored in `captures`. assert(result.length >= 1, "RegExpMatcher doesn't return an empty array"); var nCaptures = result.length - 1; // Step 14.j (reordered) var namedCaptures = result.groups; if (namedCaptures === undefined) { switch (nCaptures) { case 0: return ToString(replaceValue(SPREAD(result, 1), position, S)); case 1: return ToString(replaceValue(SPREAD(result, 2), position, S)); case 2: return ToString(replaceValue(SPREAD(result, 3), position, S)); case 3: return ToString(replaceValue(SPREAD(result, 4), position, S)); case 4: return ToString(replaceValue(SPREAD(result, 5), position, S)); } } // Steps 14.g-i, 14.k.i-ii. var captures = []; for (var n = 0; n <= nCaptures; n++) { assert(typeof result[n] === "string" || result[n] === undefined, "RegExpMatcher returns only strings and undefined"); _DefineDataProperty(captures, n, result[n]); } // Step 14.k.iii. _DefineDataProperty(captures, nCaptures + 1, position); _DefineDataProperty(captures, nCaptures + 2, S); // Step 14.k.iv. if (namedCaptures !== undefined) { _DefineDataProperty(captures, nCaptures + 3, namedCaptures); } // Steps 14.k.v-vi. return ToString(callFunction(std_Function_apply, replaceValue, undefined, captures)); } // 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, flags) { // Step 8.a. var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG); // 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 // * S is a short string (lengthS < 0x7fff) // * replaceValue is a string without "$" #define FUNC_NAME RegExpLocalReplaceOptShort #define SHORT_STRING #include "RegExpLocalReplaceOpt.h.js" #undef SHORT_STRING #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 // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210 // 21.2.5.9 RegExp.prototype [ @@search ] ( string ) function RegExpSearch(string) { // Step 1. var rx = this; // Step 2. if (!IsObject(rx)) ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx); // Step 3. var S = ToString(string); // Step 4. var previousLastIndex = rx.lastIndex; // Step 5. var lastIndexIsZero = SameValue(previousLastIndex, 0); if (!lastIndexIsZero) rx.lastIndex = 0; if (IsRegExpMethodOptimizable(rx) && S.length < 0x7fff) { // Step 6. var result = RegExpSearcher(rx, S, 0); // We need to consider two cases: // // 1. Neither global nor sticky is set: // RegExpBuiltinExec doesn't modify lastIndex for local RegExps, that // means |SameValue(rx.lastIndex, 0)| is true after calling exec. The // comparison in steps 7-8 |SameValue(rx.lastIndex, previousLastIndex)| // is therefore equal to the already computed |lastIndexIsZero| value. // // 2. Global or sticky flag is set. // RegExpBuiltinExec will always update lastIndex and we need to // restore the property to its original value. // Steps 7-8. if (!lastIndexIsZero) { rx.lastIndex = previousLastIndex; } else { var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT); if (flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)) rx.lastIndex = previousLastIndex; } // Step 9. if (result === -1) return -1; // Step 10. return result & 0x7fff; } return RegExpSearchSlowPath(rx, S, previousLastIndex); } // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210 // 21.2.5.9 RegExp.prototype [ @@search ] ( string ) // Steps 6-10. function RegExpSearchSlowPath(rx, S, previousLastIndex) { // Step 6. var result = RegExpExec(rx, S, false); // Step 7. var currentLastIndex = rx.lastIndex; // Step 8. if (!SameValue(currentLastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex; // Step 9. if (result === null) return -1; // Step 10. 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_OBJECT_REQUIRED, 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. if (flags & REGEXP_STICKY_FLAG) { var source = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT); splitter = regexp_construct_raw_flags(source, flags & ~REGEXP_STICKY_FLAG); } else { splitter = rx; } } 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_UINT32; 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 (result !== null && !IsObject(result)) ThrowTypeError(JSMSG_EXEC_NOT_OBJORNULL); // Step 5.d. return forTest ? result !== null : result; } // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210 // 21.2.5.2.2 Runtime Semantics: RegExpBuiltinExec ( R, S ) function RegExpBuiltinExec(R, S, forTest) { // 21.2.5.2.1 Runtime Semantics: RegExpExec, step 5. // This check is here for RegExpTest. RegExp_prototype_Exec does same // thing already. if (!IsRegExpObject(R)) return UnwrapAndCallRegExpBuiltinExec(R, S, forTest); // Steps 1-3 (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 { // Step 12.a. if (lastIndex > S.length) { // Steps 12.a.i-ii. if (globalOrSticky) 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. if (globalOrSticky) 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, 12.c.i. if (globalOrSticky) 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_OBJECT_REQUIRED, 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]"); function IsRegExpMatchAllOptimizable(rx, C) { if (!IsRegExpObject(rx)) return false; var RegExpCtor = GetBuiltinConstructor("RegExp"); if (C !== RegExpCtor) return false; var RegExpProto = RegExpCtor.prototype; return RegExpPrototypeOptimizable(RegExpProto) && RegExpInstanceOptimizable(rx, RegExpProto); } // String.prototype.matchAll proposal. // // RegExp.prototype [ @@matchAll ] ( string ) function RegExpMatchAll(string) { // Step 1. var rx = this; // Step 2. if (!IsObject(rx)) ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx); // Step 3. var str = ToString(string); // Step 4. var C = SpeciesConstructor(rx, GetBuiltinConstructor("RegExp")); var source, flags, matcher, lastIndex; if (IsRegExpMatchAllOptimizable(rx, C)) { // Step 5, 9-12. source = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT); flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT); // Step 6. matcher = rx; // Step 7. lastIndex = ToLength(rx.lastIndex); // Step 8 (not applicable for the optimized path). } else { // Step 5. source = ""; flags = ToString(rx.flags); // Step 6. matcher = new C(rx, flags); // Steps 7-8. matcher.lastIndex = ToLength(rx.lastIndex); // Steps 9-12. flags = (callFunction(std_String_includes, flags, "g") ? REGEXP_GLOBAL_FLAG : 0) | (callFunction(std_String_includes, flags, "u") ? REGEXP_UNICODE_FLAG : 0); // Take the non-optimized path. lastIndex = REGEXP_STRING_ITERATOR_LASTINDEX_SLOW; } // Step 13. return CreateRegExpStringIterator(matcher, str, source, flags, lastIndex); } // String.prototype.matchAll proposal. // // CreateRegExpStringIterator ( R, S, global, fullUnicode ) function CreateRegExpStringIterator(regexp, string, source, flags, lastIndex) { // Step 1. assert(typeof string === "string", "|string| is a string value"); // Steps 2-3. assert(typeof flags === "number", "|flags| is a number value"); assert(typeof source === "string", "|source| is a string value"); assert(typeof lastIndex === "number", "|lastIndex| is a number value"); // Steps 4-9. var iterator = NewRegExpStringIterator(); UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_REGEXP_SLOT, regexp); UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_STRING_SLOT, string); UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_SOURCE_SLOT, source); UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_FLAGS_SLOT, flags | 0); UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT, lastIndex); // Step 10. return iterator; } function IsRegExpStringIteratorNextOptimizable() { var RegExpProto = GetBuiltinPrototype("RegExp"); // If RegExpPrototypeOptimizable succeeds, `RegExpProto.exec` is // guaranteed to be a data property. return RegExpPrototypeOptimizable(RegExpProto) && RegExpProto.exec === RegExp_prototype_Exec; } // String.prototype.matchAll proposal. // // %RegExpStringIteratorPrototype%.next ( ) function RegExpStringIteratorNext() { // Steps 1-3. var obj; if (!IsObject(this) || (obj = GuardToRegExpStringIterator(this)) === null) { return callFunction(CallRegExpStringIteratorMethodIfWrapped, this, "RegExpStringIteratorNext"); } var result = { value: undefined, done: false }; // Step 4. var lastIndex = UnsafeGetReservedSlot(obj, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT); if (lastIndex === REGEXP_STRING_ITERATOR_LASTINDEX_DONE) { result.done = true; return result; } // Step 5. var regexp = UnsafeGetObjectFromReservedSlot(obj, REGEXP_STRING_ITERATOR_REGEXP_SLOT); // Step 6. var string = UnsafeGetStringFromReservedSlot(obj, REGEXP_STRING_ITERATOR_STRING_SLOT); // Steps 7-8. var flags = UnsafeGetInt32FromReservedSlot(obj, REGEXP_STRING_ITERATOR_FLAGS_SLOT); var global = !!(flags & REGEXP_GLOBAL_FLAG); var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG); if (lastIndex >= 0) { assert(IsRegExpObject(regexp), "|regexp| is a RegExp object"); var source = UnsafeGetStringFromReservedSlot(obj, REGEXP_STRING_ITERATOR_SOURCE_SLOT); if (IsRegExpStringIteratorNextOptimizable() && UnsafeGetStringFromReservedSlot(regexp, REGEXP_SOURCE_SLOT) === source && UnsafeGetInt32FromReservedSlot(regexp, REGEXP_FLAGS_SLOT) === flags) { // Step 9 (Inlined RegExpBuiltinExec). var globalOrSticky = !!(flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)); if (!globalOrSticky) lastIndex = 0; var match = (lastIndex <= string.length) ? RegExpMatcher(regexp, string, lastIndex) : null; // Step 10. if (match === null) { // Step 10.a. UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT, REGEXP_STRING_ITERATOR_LASTINDEX_DONE); // Step 10.b. result.done = true; return result; } // Step 11.a. if (global) { // Step 11.a.i. var matchLength = match[0].length; lastIndex = match.index + matchLength; // Step 11.a.ii. if (matchLength === 0) { // Steps 11.a.ii.1-3. lastIndex = fullUnicode ? AdvanceStringIndex(string, lastIndex) : lastIndex + 1; } UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT, lastIndex); } else { // Step 11.b.i. UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT, REGEXP_STRING_ITERATOR_LASTINDEX_DONE); } // Steps 11.a.iii and 11.b.ii. result.value = match; return result; } // Reify the RegExp object. regexp = regexp_construct_raw_flags(source, flags); regexp.lastIndex = lastIndex; UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_REGEXP_SLOT, regexp); // Mark the iterator as no longer optimizable. UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT, REGEXP_STRING_ITERATOR_LASTINDEX_SLOW); } // Step 9. var match = RegExpExec(regexp, string, false); // Step 10. if (match === null) { // Step 10.a. UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT, REGEXP_STRING_ITERATOR_LASTINDEX_DONE); // Step 10.b. result.done = true; return result; } // Step 11.a. if (global) { // Step 11.a.i. var matchStr = ToString(match[0]); // Step 11.a.ii. if (matchStr.length === 0) { // Step 11.a.ii.1. var thisIndex = ToLength(regexp.lastIndex); // Step 11.a.ii.2. var nextIndex = fullUnicode ? AdvanceStringIndex(string, thisIndex) : thisIndex + 1; // Step 11.a.ii.3. regexp.lastIndex = nextIndex; } } else { // Step 11.b.i. UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_LASTINDEX_SLOT, REGEXP_STRING_ITERATOR_LASTINDEX_DONE); } // Steps 11.a.iii and 11.b.ii. result.value = match; return result; } // ES2020 draft rev e97c95d064750fb949b6778584702dd658cf5624 // 7.2.8 IsRegExp ( argument ) function IsRegExp(argument) { // Step 1. if (!IsObject(argument)) { return false; } // Step 2. var matcher = argument[std_match]; // Step 3. if (matcher !== undefined) { return !!matcher; } // Steps 4-5. return IsPossiblyWrappedRegExpObject(argument); }