@@ -133,49 +133,68 @@ List.prototype = objCreate(null);
133
133
*/
134
134
export function createRegExpRestore ( ) {
135
135
if ( internals . disableRegExpRestore ) {
136
- return { exp : / a / , input : 'a' } ;
136
+ return function ( ) { /* no-op */ } ;
137
137
}
138
138
139
- let esc = / [ . ? * + ^ $ [ \] \\ ( ) { } | - ] / g ,
140
- lm = RegExp . lastMatch || '' ,
141
- ml = RegExp . multiline ? 'm' : '' ,
142
- ret = { input : RegExp . input } ,
143
- reg = new List ( ) ,
144
- has = false ,
145
- cap = { } ;
139
+ let regExpCache = {
140
+ lastMatch : RegExp . lastMatch || '' ,
141
+ leftContext : RegExp . leftContext ,
142
+ multiline : RegExp . multiline ,
143
+ input : RegExp . input
144
+ } ,
145
+ has = false ;
146
146
147
147
// Create a snapshot of all the 'captured' properties
148
148
for ( let i = 1 ; i <= 9 ; i ++ )
149
- has = ( cap [ '$' + i ] = RegExp [ '$' + i ] ) || has ;
150
-
151
- // Now we've snapshotted some properties, escape the lastMatch string
152
- lm = lm . replace ( esc , '\\$&' ) ;
149
+ has = ( regExpCache [ '$' + i ] = RegExp [ '$' + i ] ) || has ;
150
+
151
+ return function ( ) {
152
+ // Now we've snapshotted some properties, escape the lastMatch string
153
+ let esc = / [ . ? * + ^ $ [ \] \\ ( ) { } | - ] / g,
154
+ lm = regExpCache . lastMatch . replace ( esc , '\\$&' ) ,
155
+ reg = new List ( ) ;
156
+
157
+ // If any of the captured strings were non-empty, iterate over them all
158
+ if ( has ) {
159
+ for ( let i = 1 ; i <= 9 ; i ++ ) {
160
+ let m = regExpCache [ '$' + i ] ;
161
+
162
+ // If it's empty, add an empty capturing group
163
+ if ( ! m )
164
+ lm = '()' + lm ;
165
+
166
+ // Else find the string in lm and escape & wrap it to capture it
167
+ else {
168
+ m = m . replace ( esc , '\\$&' ) ;
169
+ lm = lm . replace ( m , '(' + m + ')' ) ;
170
+ }
153
171
154
- // If any of the captured strings were non-empty, iterate over them all
155
- if ( has ) {
156
- for ( let i = 1 ; i <= 9 ; i ++ ) {
157
- let m = cap [ '$' + i ] ;
172
+ // Push it to the reg and chop lm to make sure further groups come after
173
+ arrPush . call ( reg , lm . slice ( 0 , lm . indexOf ( '(' ) + 1 ) ) ;
174
+ lm = lm . slice ( lm . indexOf ( '(' ) + 1 ) ;
175
+ }
176
+ }
158
177
159
- // If it's empty, add an empty capturing group
160
- if ( ! m )
161
- lm = '()' + lm ;
178
+ let exprStr = arrJoin . call ( reg , '' ) + lm ;
162
179
163
- // Else find the string in lm and escape & wrap it to capture it
164
- else {
165
- m = m . replace ( esc , '\\$&' ) ;
166
- lm = lm . replace ( m , '(' + m + ')' ) ;
167
- }
180
+ // Shorten the regex by replacing each part of the expression with a match
181
+ // for a string of that exact length. This is safe for the type of
182
+ // expressions generated above, because the expression matches the whole
183
+ // match string, so we know each group and each segment between capturing
184
+ // groups can be matched by its length alone.
185
+ exprStr = exprStr . replace ( / ( \\ \( | \\ \) | [ ^ ( ) ] ) + / g, ( match ) => {
186
+ return `[\\s\\S]{${ match . replace ( '\\' , '' ) . length } }` ;
187
+ } ) ;
168
188
169
- // Push it to the reg and chop lm to make sure further groups come after
170
- arrPush . call ( reg , lm . slice ( 0 , lm . indexOf ( '(' ) + 1 ) ) ;
171
- lm = lm . slice ( lm . indexOf ( '(' ) + 1 ) ;
172
- }
173
- }
189
+ // Create the regular expression that will reconstruct the RegExp properties
190
+ let expr = new RegExp ( exprStr , regExpCache . multiline ? 'gm' : 'g' ) ;
174
191
175
- // Create the regular expression that will reconstruct the RegExp properties
176
- ret . exp = new RegExp ( arrJoin . call ( reg , '' ) + lm , ml ) ;
192
+ // Set the lastIndex of the generated expression to ensure that the match
193
+ // is found in the correct index.
194
+ expr . lastIndex = regExpCache . leftContext . length ;
177
195
178
- return ret ;
196
+ expr . exec ( regExpCache . input ) ;
197
+ } ;
179
198
}
180
199
181
200
/**
0 commit comments