1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // https://github.com/tc39/ecma262/pull/2418 22.2.6.4 get RegExp.prototype.flags
6 // https://arai-a.github.io/ecma262-compare/?pr=2418&id=sec-get-regexp.prototype.flags
7 // Uncloned functions with `$` prefix are allocated as extended function
8 // to store the original name in `SetCanonicalName`.
9 function $RegExpFlagsGetter() {
13 ThrowTypeError(JSMSG_OBJECT_REQUIRED, R === null ? "null" : typeof R);
62 SetCanonicalName($RegExpFlagsGetter, "get flags");
64 // ES 2017 draft 40edb3a95a475c1b251141ac681b8793129d9a6d 21.2.5.14.
65 function $RegExpToString() {
71 ThrowTypeError(JSMSG_OBJECT_REQUIRED, R === null ? "null" : typeof R);
75 var pattern = ToString(R.source);
78 var flags = ToString(R.flags);
81 return "/" + pattern + "/" + flags;
83 SetCanonicalName($RegExpToString, "toString");
85 // ES 2016 draft Mar 25, 2016 21.2.5.2.3.
86 function AdvanceStringIndex(S, index) {
88 assert(typeof S === "string", "Expected string as 1st argument");
92 index >= 0 && index <= MAX_NUMERIC_INDEX,
93 "Expected integer as 2nd argument"
101 var supplementary = (
103 callFunction(std_String_codePointAt, S, index) > 0xffff
105 return index + 1 + supplementary;
108 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
109 // 22.2.5.8 RegExp.prototype [ @@match ] ( string )
110 function RegExpMatch(string) {
116 ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
120 var S = ToString(string);
122 // Optimized paths for simple cases.
123 if (IsRegExpMethodOptimizable(rx)) {
125 var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
126 var global = !!(flags & REGEXP_GLOBAL_FLAG);
130 var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG);
133 return RegExpGlobalMatchOpt(rx, S, fullUnicode);
137 return RegExpBuiltinExec(rx, S);
141 return RegExpMatchSlowPath(rx, S);
144 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
145 // 22.2.5.8 RegExp.prototype [ @@match ] ( string )
147 function RegExpMatchSlowPath(rx, S) {
149 var flags = ToString(rx.flags);
152 if (!callFunction(std_String_includes, flags, "g")) {
153 return RegExpExec(rx, S);
157 var fullUnicode = callFunction(std_String_includes, flags, "u");
171 var result = RegExpExec(rx, S);
174 if (result === null) {
175 return n === 0 ? null : A;
179 var matchStr = ToString(result[0]);
182 DefineDataProperty(A, n, matchStr);
185 if (matchStr === "") {
186 var lastIndex = ToLength(rx.lastIndex);
187 rx.lastIndex = fullUnicode
188 ? AdvanceStringIndex(S, lastIndex)
197 // ES 2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.6.
199 // Optimized path for @@match with global flag.
200 function RegExpGlobalMatchOpt(rx, S, fullUnicode) {
211 var lengthS = S.length;
216 var position = RegExpSearcher(rx, S, lastIndex);
219 if (position === -1) {
220 return n === 0 ? null : A;
223 lastIndex = RegExpSearcherLastLimit(S);
226 var matchStr = Substring(S, position, lastIndex - position);
229 DefineDataProperty(A, n, matchStr);
232 if (matchStr === "") {
233 lastIndex = fullUnicode
234 ? AdvanceStringIndex(S, lastIndex)
236 if (lastIndex > lengthS) {
246 // Checks if following properties and getters are not modified, and accessing
247 // them not observed by content script:
259 function IsRegExpMethodOptimizable(rx) {
260 if (!IsRegExpObject(rx)) {
264 var RegExpProto = GetBuiltinPrototype("RegExp");
265 // If RegExpPrototypeOptimizable and RegExpInstanceOptimizable succeed,
266 // `RegExpProto.exec` is guaranteed to be data properties.
268 RegExpPrototypeOptimizable(RegExpProto) &&
269 RegExpInstanceOptimizable(rx, RegExpProto) &&
270 RegExpProto.exec === RegExp_prototype_Exec
274 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
275 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
276 function RegExpReplace(string, replaceValue) {
282 ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
286 var S = ToString(string);
289 var lengthS = S.length;
292 var functionalReplace = IsCallable(replaceValue);
295 var firstDollarIndex = -1;
296 if (!functionalReplace) {
298 replaceValue = ToString(replaceValue);
300 // Skip if replaceValue is an empty string or a single character.
301 // A single character string may contain "$", but that cannot be a
303 if (replaceValue.length > 1) {
304 firstDollarIndex = GetFirstDollarIndex(replaceValue);
309 if (IsRegExpMethodOptimizable(rx)) {
311 var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
314 var global = !!(flags & REGEXP_GLOBAL_FLAG);
318 if (functionalReplace) {
319 // For large strings check if the replacer function is
320 // applicable for the elem-base optimization.
321 if (lengthS > 5000) {
322 var elemBase = GetElemBaseForLambda(replaceValue);
323 if (IsObject(elemBase)) {
324 return RegExpGlobalReplaceOptElemBase(
334 return RegExpGlobalReplaceOptFunc(rx, S, lengthS, replaceValue, flags);
336 if (firstDollarIndex !== -1) {
337 return RegExpGlobalReplaceOptSubst(
346 return RegExpGlobalReplaceOptSimple(rx, S, lengthS, replaceValue, flags);
349 if (functionalReplace) {
350 return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue);
352 if (firstDollarIndex !== -1) {
353 return RegExpLocalReplaceOptSubst(
361 return RegExpLocalReplaceOptSimple(rx, S, lengthS, replaceValue);
365 return RegExpReplaceSlowPath(
375 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
376 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
378 // Slow path for @@replace.
379 function RegExpReplaceSlowPath(
388 var flags = ToString(rx.flags);
391 var global = callFunction(std_String_includes, flags, "g");
394 var fullUnicode = false;
397 fullUnicode = callFunction(std_String_includes, flags, "u");
404 var results = new_List();
410 var result = RegExpExec(rx, S);
413 if (result === null) {
418 DefineDataProperty(results, nResults++, result);
426 var matchStr = ToString(result[0]);
429 if (matchStr === "") {
430 var lastIndex = ToLength(rx.lastIndex);
431 rx.lastIndex = fullUnicode
432 ? AdvanceStringIndex(S, lastIndex)
438 var accumulatedResult = "";
441 var nextSourcePosition = 0;
444 for (var i = 0; i < nResults; i++) {
448 var nCaptures = std_Math_max(ToLength(result.length) - 1, 0);
451 var matched = ToString(result[0]);
454 var matchLength = matched.length;
457 var position = std_Math_max(
458 std_Math_min(ToInteger(result.index), lengthS),
463 if (functionalReplace || firstDollarIndex !== -1) {
465 replacement = RegExpGetComplexReplacement(
476 // Steps 15.g, 15.i, 15.i.iv.
477 // We don't need captures array, but ToString is visible to script.
478 for (var n = 1; n <= nCaptures; n++) {
480 var capN = result[n];
483 if (capN !== undefined) {
488 // Steps 15.j, 15.l.i.
489 // We don't need namedCaptures, but ToObject is visible to script.
490 var namedCaptures = result.groups;
491 if (namedCaptures !== undefined) {
492 ToObject(namedCaptures);
496 replacement = replaceValue;
500 if (position >= nextSourcePosition) {
503 Substring(S, nextSourcePosition, position - nextSourcePosition) +
507 nextSourcePosition = position + matchLength;
512 if (nextSourcePosition >= lengthS) {
513 return accumulatedResult;
519 Substring(S, nextSourcePosition, lengthS - nextSourcePosition)
523 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
524 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
525 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
527 // Calculates functional/substitution replacement from match result.
528 // Used in the following functions:
529 // * RegExpReplaceSlowPath
530 function RegExpGetComplexReplacement(
541 var captures = new_List();
542 var capturesLength = 0;
544 // Step 15.k.i (reordered).
545 DefineDataProperty(captures, capturesLength++, matched);
547 // Steps 15.h, 15.i, 15.i.v.
548 for (var n = 1; n <= nCaptures; n++) {
550 var capN = result[n];
553 if (capN !== undefined) {
554 capN = ToString(capN);
558 DefineDataProperty(captures, capturesLength++, capN);
562 var namedCaptures = result.groups;
565 if (functionalReplace) {
566 // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
567 // use `std_Function_apply` with all arguments stored in `captures`.
568 if (namedCaptures === undefined) {
624 DefineDataProperty(captures, capturesLength++, position);
625 DefineDataProperty(captures, capturesLength++, S);
626 if (namedCaptures !== undefined) {
627 DefineDataProperty(captures, capturesLength++, namedCaptures);
630 callFunction(std_Function_apply, replaceValue, undefined, captures)
635 if (namedCaptures !== undefined) {
636 namedCaptures = ToObject(namedCaptures);
638 return RegExpGetSubstitution(
648 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
649 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
650 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
652 // Calculates functional replacement from match result.
653 // Used in the following functions:
654 // * RegExpGlobalReplaceOptFunc
655 // * RegExpGlobalReplaceOptElemBase
656 // * RegExpLocalReplaceOptFunc
657 function RegExpGetFunctionalReplacement(result, S, position, replaceValue) {
658 // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
659 // use `std_Function_apply` with all arguments stored in `captures`.
660 assert(result.length >= 1, "RegExpMatcher doesn't return an empty array");
661 var nCaptures = result.length - 1;
663 // Step 15.j (reordered)
664 var namedCaptures = result.groups;
666 if (namedCaptures === undefined) {
721 // Steps 15.g-i, 15.k.i-ii.
722 var captures = new_List();
723 for (var n = 0; n <= nCaptures; n++) {
725 typeof result[n] === "string" || result[n] === undefined,
726 "RegExpMatcher returns only strings and undefined"
728 DefineDataProperty(captures, n, result[n]);
732 DefineDataProperty(captures, nCaptures + 1, position);
733 DefineDataProperty(captures, nCaptures + 2, S);
736 if (namedCaptures !== undefined) {
737 DefineDataProperty(captures, nCaptures + 3, namedCaptures);
742 callFunction(std_Function_apply, replaceValue, undefined, captures)
746 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
747 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
749 // Optimized path for @@replace with the following conditions:
750 // * global flag is true
751 // * replaceValue is a string without "$"
752 function RegExpGlobalReplaceOptSimple(rx, S, lengthS, replaceValue, flags) {
754 var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG);
760 // Step 13 (reordered).
761 var accumulatedResult = "";
763 // Step 14 (reordered).
764 var nextSourcePosition = 0;
769 var position = RegExpSearcher(rx, S, lastIndex);
772 if (position === -1) {
776 lastIndex = RegExpSearcherLastLimit(S);
780 Substring(S, nextSourcePosition, position - nextSourcePosition) +
784 nextSourcePosition = lastIndex;
787 if (lastIndex === position) {
788 lastIndex = fullUnicode
789 ? AdvanceStringIndex(S, lastIndex)
791 if (lastIndex > lengthS) {
798 if (nextSourcePosition >= lengthS) {
799 return accumulatedResult;
805 Substring(S, nextSourcePosition, lengthS - nextSourcePosition)
809 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
810 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
812 // Optimized path for @@replace.
815 // * global flag is true
816 // * replaceValue is a function
817 #define FUNC_NAME RegExpGlobalReplaceOptFunc
819 #include "RegExpGlobalReplaceOpt.h.js"
822 /* global RegExpGlobalReplaceOptFunc */
825 // * global flag is true
826 // * replaceValue is a function that returns element of an object
827 #define FUNC_NAME RegExpGlobalReplaceOptElemBase
829 #include "RegExpGlobalReplaceOpt.h.js"
832 /* global RegExpGlobalReplaceOptElemBase */
835 // * global flag is true
836 // * replaceValue is a string with "$"
837 #define FUNC_NAME RegExpGlobalReplaceOptSubst
839 #include "RegExpGlobalReplaceOpt.h.js"
842 /* global RegExpGlobalReplaceOptSubst */
845 // * global flag is false
846 // * replaceValue is a string without "$"
847 #define FUNC_NAME RegExpLocalReplaceOptSimple
849 #include "RegExpLocalReplaceOpt.h.js"
852 /* global RegExpLocalReplaceOptSimple */
855 // * global flag is false
856 // * replaceValue is a function
857 #define FUNC_NAME RegExpLocalReplaceOptFunc
859 #include "RegExpLocalReplaceOpt.h.js"
862 /* global RegExpLocalReplaceOptFunc */
865 // * global flag is false
866 // * replaceValue is a string with "$"
867 #define FUNC_NAME RegExpLocalReplaceOptSubst
869 #include "RegExpLocalReplaceOpt.h.js"
872 /* global RegExpLocalReplaceOptSubst */
874 // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
875 // 21.2.5.9 RegExp.prototype [ @@search ] ( string )
876 function RegExpSearch(string) {
882 ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
886 var S = ToString(string);
889 var previousLastIndex = rx.lastIndex;
892 var lastIndexIsZero = SameValue(previousLastIndex, 0);
893 if (!lastIndexIsZero) {
897 if (IsRegExpMethodOptimizable(rx) && S.length < 0x7fff) {
899 var result = RegExpSearcher(rx, S, 0);
901 // We need to consider two cases:
903 // 1. Neither global nor sticky is set:
904 // RegExpBuiltinExec doesn't modify lastIndex for local RegExps, that
905 // means |SameValue(rx.lastIndex, 0)| is true after calling exec. The
906 // comparison in steps 7-8 |SameValue(rx.lastIndex, previousLastIndex)|
907 // is therefore equal to the already computed |lastIndexIsZero| value.
909 // 2. Global or sticky flag is set.
910 // RegExpBuiltinExec will always update lastIndex and we need to
911 // restore the property to its original value.
914 if (!lastIndexIsZero) {
915 rx.lastIndex = previousLastIndex;
917 var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
918 if (flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)) {
919 rx.lastIndex = previousLastIndex;
927 return RegExpSearchSlowPath(rx, S, previousLastIndex);
930 // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
931 // 21.2.5.9 RegExp.prototype [ @@search ] ( string )
933 function RegExpSearchSlowPath(rx, S, previousLastIndex) {
935 var result = RegExpExec(rx, S);
938 var currentLastIndex = rx.lastIndex;
941 if (!SameValue(currentLastIndex, previousLastIndex)) {
942 rx.lastIndex = previousLastIndex;
946 if (result === null) {
954 function IsRegExpSplitOptimizable(rx, C) {
955 if (!IsRegExpObject(rx)) {
959 var RegExpCtor = GetBuiltinConstructor("RegExp");
960 if (C !== RegExpCtor) {
964 var RegExpProto = RegExpCtor.prototype;
965 // If RegExpPrototypeOptimizable succeeds, `RegExpProto.exec` is guaranteed
966 // to be a data property.
968 RegExpPrototypeOptimizable(RegExpProto) &&
969 RegExpInstanceOptimizable(rx, RegExpProto) &&
970 RegExpProto.exec === RegExp_prototype_Exec
974 // ES 2017 draft 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e 21.2.5.11.
975 function RegExpSplit(string, limit) {
981 ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
985 var S = ToString(string);
988 var C = SpeciesConstructor(rx, GetBuiltinConstructor("RegExp"));
991 IsRegExpSplitOptimizable(rx, C) &&
992 (limit === undefined || typeof limit === "number");
994 var flags, unicodeMatching, splitter;
997 flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
1000 unicodeMatching = !!(flags & REGEXP_UNICODE_FLAG);
1003 // If split operation is optimizable, perform non-sticky match.
1004 if (flags & REGEXP_STICKY_FLAG) {
1005 var source = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT);
1006 splitter = RegExpConstructRaw(source, flags & ~REGEXP_STICKY_FLAG);
1012 flags = ToString(rx.flags);
1015 unicodeMatching = callFunction(std_String_includes, flags, "u");
1019 if (callFunction(std_String_includes, flags, "y")) {
1022 newFlags = flags + "y";
1026 splitter = constructContentFunction(C, C, rx, newFlags);
1037 if (limit === undefined) {
1051 // Step 14 (reordered).
1052 var size = S.length;
1058 if (RegExpSearcher(splitter, S, 0) !== -1) {
1062 if (RegExpExec(splitter, S) !== null) {
1068 DefineDataProperty(A, 0, S);
1077 var optimizableNoCaptures = optimizable && !RegExpHasCaptureGroups(splitter, S);
1082 if (optimizableNoCaptures) {
1083 // If there are no capturing groups, avoid allocating the match result
1084 // object |z| (we set it to null). This is the only difference between
1085 // this branch and the |if (optimizable)| case below.
1087 // Step 19.a (skipped).
1088 // splitter.lastIndex is not used.
1091 q = RegExpSearcher(splitter, S, q);
1092 if (q === -1 || q >= size) {
1097 e = RegExpSearcherLastLimit(S);
1099 } else if (optimizable) {
1100 // Step 19.a (skipped).
1101 // splitter.lastIndex is not used.
1104 z = RegExpMatcher(splitter, S, q);
1111 // splitter.lastIndex is not updated.
1118 e = q + z[0].length;
1121 splitter.lastIndex = q;
1124 z = RegExpExec(splitter, S);
1128 q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1;
1133 e = ToLength(splitter.lastIndex);
1138 q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1;
1142 // Steps 19.d.iv.1-3.
1143 DefineDataProperty(A, lengthA, Substring(S, p, q - p));
1149 if (lengthA === lim) {
1157 // Steps 19.d.iv.7-8.
1158 var numberOfCaptures = std_Math_max(ToLength(z.length) - 1, 0);
1164 while (i <= numberOfCaptures) {
1165 // Steps 19.d.iv.10.a-b.
1166 DefineDataProperty(A, lengthA, z[i]);
1168 // Step 19.d.iv.10.c.
1171 // Step 19.d.iv.10.d.
1174 // Step 19.d.iv.10.e.
1175 if (lengthA === lim) {
1187 DefineDataProperty(A, lengthA, "");
1189 DefineDataProperty(A, lengthA, Substring(S, p, size - p));
1197 // NOTE: This is not RegExpExec (21.2.5.2.1).
1198 function RegExp_prototype_Exec(string) {
1201 if (!IsObject(R) || !IsRegExpObject(R)) {
1202 return callFunction(
1203 CallRegExpMethodIfWrapped,
1206 "RegExp_prototype_Exec"
1211 var S = ToString(string);
1214 return RegExpBuiltinExec(R, S);
1218 function RegExpTest(string) {
1222 ThrowTypeError(JSMSG_OBJECT_REQUIRED, R === null ? "null" : typeof R);
1226 var S = ToString(string);
1229 return RegExpExecForTest(R, S);
1232 // ES 2016 draft Mar 25, 2016 21.2.4.2.
1233 function $RegExpSpecies() {
1237 SetCanonicalName($RegExpSpecies, "get [Symbol.species]");
1239 function IsRegExpMatchAllOptimizable(rx, C) {
1240 if (!IsRegExpObject(rx)) {
1244 var RegExpCtor = GetBuiltinConstructor("RegExp");
1245 if (C !== RegExpCtor) {
1249 var RegExpProto = RegExpCtor.prototype;
1251 RegExpPrototypeOptimizable(RegExpProto) &&
1252 RegExpInstanceOptimizable(rx, RegExpProto)
1256 // String.prototype.matchAll proposal.
1258 // RegExp.prototype [ @@matchAll ] ( string )
1259 function RegExpMatchAll(string) {
1264 if (!IsObject(rx)) {
1265 ThrowTypeError(JSMSG_OBJECT_REQUIRED, rx === null ? "null" : typeof rx);
1269 var str = ToString(string);
1272 var C = SpeciesConstructor(rx, GetBuiltinConstructor("RegExp"));
1274 var source, flags, matcher, lastIndex;
1275 if (IsRegExpMatchAllOptimizable(rx, C)) {
1277 source = UnsafeGetStringFromReservedSlot(rx, REGEXP_SOURCE_SLOT);
1278 flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
1284 lastIndex = ToLength(rx.lastIndex);
1286 // Step 8 (not applicable for the optimized path).
1290 flags = ToString(rx.flags);
1293 matcher = constructContentFunction(C, C, rx, flags);
1296 matcher.lastIndex = ToLength(rx.lastIndex);
1300 (callFunction(std_String_includes, flags, "g") ? REGEXP_GLOBAL_FLAG : 0) |
1301 (callFunction(std_String_includes, flags, "u") ? REGEXP_UNICODE_FLAG : 0);
1303 // Take the non-optimized path.
1304 lastIndex = REGEXP_STRING_ITERATOR_LASTINDEX_SLOW;
1308 return CreateRegExpStringIterator(matcher, str, source, flags, lastIndex);
1311 // String.prototype.matchAll proposal.
1313 // CreateRegExpStringIterator ( R, S, global, fullUnicode )
1314 function CreateRegExpStringIterator(regexp, string, source, flags, lastIndex) {
1316 assert(typeof string === "string", "|string| is a string value");
1319 assert(typeof flags === "number", "|flags| is a number value");
1321 assert(typeof source === "string", "|source| is a string value");
1322 assert(typeof lastIndex === "number", "|lastIndex| is a number value");
1325 var iterator = NewRegExpStringIterator();
1326 UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_REGEXP_SLOT, regexp);
1327 UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_STRING_SLOT, string);
1328 UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_SOURCE_SLOT, source);
1329 UnsafeSetReservedSlot(iterator, REGEXP_STRING_ITERATOR_FLAGS_SLOT, flags | 0);
1330 UnsafeSetReservedSlot(
1332 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1340 function IsRegExpStringIteratorNextOptimizable() {
1341 var RegExpProto = GetBuiltinPrototype("RegExp");
1342 // If RegExpPrototypeOptimizable succeeds, `RegExpProto.exec` is
1343 // guaranteed to be a data property.
1345 RegExpPrototypeOptimizable(RegExpProto) &&
1346 RegExpProto.exec === RegExp_prototype_Exec
1350 // String.prototype.matchAll proposal.
1352 // %RegExpStringIteratorPrototype%.next ( )
1353 function RegExpStringIteratorNext() {
1356 if (!IsObject(obj) || (obj = GuardToRegExpStringIterator(obj)) === null) {
1357 return callFunction(
1358 CallRegExpStringIteratorMethodIfWrapped,
1360 "RegExpStringIteratorNext"
1364 var result = { value: undefined, done: false };
1367 var lastIndex = UnsafeGetReservedSlot(
1369 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT
1371 if (lastIndex === REGEXP_STRING_ITERATOR_LASTINDEX_DONE) {
1377 var regexp = UnsafeGetObjectFromReservedSlot(
1379 REGEXP_STRING_ITERATOR_REGEXP_SLOT
1383 var string = UnsafeGetStringFromReservedSlot(
1385 REGEXP_STRING_ITERATOR_STRING_SLOT
1389 var flags = UnsafeGetInt32FromReservedSlot(
1391 REGEXP_STRING_ITERATOR_FLAGS_SLOT
1393 var global = !!(flags & REGEXP_GLOBAL_FLAG);
1394 var fullUnicode = !!(flags & REGEXP_UNICODE_FLAG);
1396 if (lastIndex >= 0) {
1397 assert(IsRegExpObject(regexp), "|regexp| is a RegExp object");
1399 var source = UnsafeGetStringFromReservedSlot(
1401 REGEXP_STRING_ITERATOR_SOURCE_SLOT
1404 IsRegExpStringIteratorNextOptimizable() &&
1405 UnsafeGetStringFromReservedSlot(regexp, REGEXP_SOURCE_SLOT) === source &&
1406 UnsafeGetInt32FromReservedSlot(regexp, REGEXP_FLAGS_SLOT) === flags
1408 // Step 9 (Inlined RegExpBuiltinExec).
1409 var globalOrSticky = !!(
1411 (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG)
1413 if (!globalOrSticky) {
1418 lastIndex <= string.length
1419 ? RegExpMatcher(regexp, string, lastIndex)
1423 if (match === null) {
1425 UnsafeSetReservedSlot(
1427 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1428 REGEXP_STRING_ITERATOR_LASTINDEX_DONE
1439 var matchLength = match[0].length;
1440 lastIndex = match.index + matchLength;
1443 if (matchLength === 0) {
1444 // Steps 11.a.ii.1-3.
1445 lastIndex = fullUnicode
1446 ? AdvanceStringIndex(string, lastIndex)
1450 UnsafeSetReservedSlot(
1452 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1457 UnsafeSetReservedSlot(
1459 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1460 REGEXP_STRING_ITERATOR_LASTINDEX_DONE
1464 // Steps 11.a.iii and 11.b.ii.
1465 result.value = match;
1469 // Reify the RegExp object.
1470 regexp = RegExpConstructRaw(source, flags);
1471 regexp.lastIndex = lastIndex;
1472 UnsafeSetReservedSlot(obj, REGEXP_STRING_ITERATOR_REGEXP_SLOT, regexp);
1474 // Mark the iterator as no longer optimizable.
1475 UnsafeSetReservedSlot(
1477 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1478 REGEXP_STRING_ITERATOR_LASTINDEX_SLOW
1483 var match = RegExpExec(regexp, string);
1486 if (match === null) {
1488 UnsafeSetReservedSlot(
1490 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1491 REGEXP_STRING_ITERATOR_LASTINDEX_DONE
1502 var matchStr = ToString(match[0]);
1505 if (matchStr.length === 0) {
1507 var thisIndex = ToLength(regexp.lastIndex);
1510 var nextIndex = fullUnicode
1511 ? AdvanceStringIndex(string, thisIndex)
1515 regexp.lastIndex = nextIndex;
1519 UnsafeSetReservedSlot(
1521 REGEXP_STRING_ITERATOR_LASTINDEX_SLOT,
1522 REGEXP_STRING_ITERATOR_LASTINDEX_DONE
1526 // Steps 11.a.iii and 11.b.ii.
1527 result.value = match;
1531 // ES2020 draft rev e97c95d064750fb949b6778584702dd658cf5624
1532 // 7.2.8 IsRegExp ( argument )
1533 function IsRegExp(argument) {
1535 if (!IsObject(argument)) {
1540 var matcher = argument[GetBuiltinSymbol("match")];
1543 if (matcher !== undefined) {
1548 return IsPossiblyWrappedRegExpObject(argument);