@@ -92,14 +92,26 @@ abstract class FrameworkLibraryWithMarkerComment extends FrameworkLibrary {
92
92
93
93
/**
94
94
* Gets a regular expression that can be used to identify an instance of
95
- * this framework library.
95
+ * this framework library, with `<VERSION>` as a placeholder for version
96
+ * numbers.
96
97
*
97
98
* The first capture group of this regular expression should match
98
99
* the version number. Any occurrences of the string `<VERSION>` in
99
- * the regular expression are replaced by `versionRegex()` before
100
+ * the regular expression will be replaced by `versionRegex()` before
100
101
* matching.
101
102
*/
102
103
abstract string getAMarkerCommentRegex ( ) ;
104
+
105
+ /**
106
+ * Gets a regular expression that can be used to identify an instance of
107
+ * this framework library.
108
+ *
109
+ * The first capture group of this regular expression is intended to match
110
+ * the version number.
111
+ */
112
+ final string getAMarkerCommentRegexWithoutPlaceholders ( ) {
113
+ result = this .getAMarkerCommentRegex ( ) .replaceAll ( "<VERSION>" , versionRegex ( ) )
114
+ }
103
115
}
104
116
105
117
/**
@@ -182,18 +194,64 @@ class FrameworkLibraryInstanceWithMarkerComment extends FrameworkLibraryInstance
182
194
override predicate info ( FrameworkLibrary fl , string v ) { matchMarkerComment ( _, this , fl , v ) }
183
195
}
184
196
197
+ /** A marker comment that indicates a framework library. */
198
+ private class MarkerComment extends Comment {
199
+ MarkerComment ( ) {
200
+ /*
201
+ * PERFORMANCE OPTIMISATION:
202
+ *
203
+ * Each framework library has a regular expression describing its marker comments.
204
+ * We want to find the set of marker comments and the framework regexes they match.
205
+ * In order to perform such regex matching, CodeQL needs to compute the
206
+ * Cartesian product of possible receiver strings and regexes first,
207
+ * containing `num_receivers * num_regexes` tuples.
208
+ *
209
+ * A straightforward attempt to match marker comments with individual
210
+ * framework regexes will compute the Cartesian product between
211
+ * the set of comments and the set of framework regexes.
212
+ * Total: `num_comments * num_frameworks` tuples.
213
+ *
214
+ * Instead, create a single regex that matches *all* frameworks.
215
+ * This is the regex union of the individual framework regexes
216
+ * i.e. `(regex_1)|(regex_2)|...|(regex_n)`
217
+ * This approach will compute the Cartesian product between
218
+ * the set of comments and the singleton set of this union regex.
219
+ * Total: `num_comments * 1` tuples.
220
+ *
221
+ * To identify the individual frameworks and extract the version number from capture groups,
222
+ * use the member predicate `matchesFramework` *after* this predicate has been computed.
223
+ */
224
+
225
+ exists ( string unionRegex |
226
+ unionRegex =
227
+ concat ( FrameworkLibraryWithMarkerComment fl |
228
+ |
229
+ "(" + fl .getAMarkerCommentRegexWithoutPlaceholders ( ) + ")" , "|"
230
+ )
231
+ |
232
+ this .getText ( ) .regexpMatch ( unionRegex )
233
+ )
234
+ }
235
+
236
+ /**
237
+ * Holds if this marker comment indicates an instance of the framework `fl`
238
+ * with version number `version`.
239
+ */
240
+ predicate matchesFramework ( FrameworkLibraryWithMarkerComment fl , string version ) {
241
+ fl .getAMarkerCommentRegexWithoutPlaceholders ( ) .regexpCapture ( this .getText ( ) , 1 ) = version
242
+ }
243
+ }
244
+
185
245
/**
186
246
* Holds if comment `c` in toplevel `tl` matches the marker comment of library
187
247
* `fl` at `version`.
188
248
*/
189
249
cached
190
250
private predicate matchMarkerComment (
191
- Comment c , TopLevel tl , FrameworkLibraryWithMarkerComment fl , string version
251
+ MarkerComment c , TopLevel tl , FrameworkLibraryWithMarkerComment fl , string version
192
252
) {
193
253
c .getTopLevel ( ) = tl and
194
- exists ( string r | r = fl .getAMarkerCommentRegex ( ) .replaceAll ( "<VERSION>" , versionRegex ( ) ) |
195
- version = c .getText ( ) .regexpCapture ( r , 1 )
196
- )
254
+ c .matchesFramework ( fl , version )
197
255
}
198
256
199
257
/**
0 commit comments