Bug 1885489 - Part 5: Add SnapshotIterator::readInt32(). r=iain
[gecko.git] / js / src / builtin / RegExpLocalReplaceOpt.h.js
blob869f03fe07d620d3c79bfd432373c4e23afd0af3
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 file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // Function template for the following functions:
6 //   * RegExpLocalReplaceOptSimple
7 //   * RegExpLocalReplaceOptFunc
8 //   * RegExpLocalReplaceOptSubst
9 // Define the following macro and include this file to declare function:
10 //   * FUNC_NAME     -- function name (required)
11 //       e.g.
12 //         #define FUNC_NAME RegExpLocalReplaceOpt
13 // Define one of the following macros (without value) to switch the code:
14 //   * SUBSTITUTION     -- replaceValue is a string with "$"
15 //   * FUNCTIONAL       -- replaceValue is a function
16 //   * SIMPLE           -- replaceValue is a string without "$"
18 // ES2023 draft rev 2c78e6f6b5bc6bfbf79dd8a12a9593e5b57afcd2
19 // 22.2.5.11 RegExp.prototype [ @@replace ] ( string, replaceValue )
20 // Steps 12.a-17.
21 // Optimized path for @@replace with the following conditions:
22 //   * global flag is false
23 function FUNC_NAME(
24   rx,
25   S,
26   lengthS,
27   replaceValue,
28 #ifdef SUBSTITUTION
29   firstDollarIndex
30 #endif
31 ) {
32   // 21.2.5.2.2 RegExpBuiltinExec, step 4.
33   var lastIndex = ToLength(rx.lastIndex);
35   // 21.2.5.2.2 RegExpBuiltinExec, step 5.
36   // Side-effects in step 4 can recompile the RegExp, so we need to read the
37   // flags again and handle the case when global was enabled even though this
38   // function is optimized for non-global RegExps.
39   var flags = UnsafeGetInt32FromReservedSlot(rx, REGEXP_FLAGS_SLOT);
41   // 21.2.5.2.2 RegExpBuiltinExec, steps 6-7.
42   var globalOrSticky = !!(flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG));
44   if (globalOrSticky) {
45     // 21.2.5.2.2 RegExpBuiltinExec, step 12.a.
46     if (lastIndex > lengthS) {
47       if (globalOrSticky) {
48         rx.lastIndex = 0;
49       }
51       // Steps 12-16.
52       return S;
53     }
54   } else {
55     // 21.2.5.2.2 RegExpBuiltinExec, step 8.
56     lastIndex = 0;
57   }
59 #if !defined(SIMPLE)
60   // Step 12.a.
61   var result = RegExpMatcher(rx, S, lastIndex);
63   // Step 12.b.
64   if (result === null) {
65     // 21.2.5.2.2 RegExpBuiltinExec, steps 12.a.i, 12.c.i.
66     if (globalOrSticky) {
67       rx.lastIndex = 0;
68     }
70     // Steps 13-17.
71     return S;
72   }
73 #else
74   // Step 12.a.
75   var position = RegExpSearcher(rx, S, lastIndex);
77   // Step 12.b.
78   if (position === -1) {
79     // 21.2.5.2.2 RegExpBuiltinExec, steps 12.a.i, 12.c.i.
80     if (globalOrSticky) {
81       rx.lastIndex = 0;
82     }
84     // Steps 13-17.
85     return S;
86   }
87 #endif
89   // Steps 12.c, 13-14.
91 #if !defined(SIMPLE)
92   // Steps 15.a-b.
93   assert(result.length >= 1, "RegExpMatcher doesn't return an empty array");
95   // Step 15.c.
96   var matched = result[0];
98   // Step 15.d.
99   var matchLength = matched.length;
101   // Step 15.e-f.
102   var position = result.index;
104   // Step 15.m.iii (reordered)
105   // To set rx.lastIndex before RegExpGetFunctionalReplacement.
106   var nextSourcePosition = position + matchLength;
107 #else
108   // Steps 15.a-f (skipped).
110   // Step 15.m.iii (reordered)
111   var nextSourcePosition = RegExpSearcherLastLimit(S);
112 #endif
114   // 21.2.5.2.2 RegExpBuiltinExec, step 15.
115   if (globalOrSticky) {
116     rx.lastIndex = nextSourcePosition;
117   }
119   var replacement;
120   // Steps 15.g-l.
121 #if defined(FUNCTIONAL)
122   replacement = RegExpGetFunctionalReplacement(
123     result,
124     S,
125     position,
126     replaceValue
127   );
128 #elif defined(SUBSTITUTION)
129   // Step 15.l.i
130   var namedCaptures = result.groups;
131   if (namedCaptures !== undefined) {
132     namedCaptures = ToObject(namedCaptures);
133   }
134   // Step 15.l.ii
135   replacement = RegExpGetSubstitution(
136     result,
137     S,
138     position,
139     replaceValue,
140     firstDollarIndex,
141     namedCaptures
142   );
143 #else
144   replacement = replaceValue;
145 #endif
147   // Step 15.m.ii.
148   var accumulatedResult = Substring(S, 0, position) + replacement;
150   // Step 16.
151   if (nextSourcePosition >= lengthS) {
152     return accumulatedResult;
153   }
155   // Step 17.
156   return (
157     accumulatedResult +
158     Substring(S, nextSourcePosition, lengthS - nextSourcePosition)
159   );