@@ -114,49 +114,33 @@ class ClassList extends TClassList {
114114 this = Empty ( ) and result = Empty ( )
115115 }
116116
117- predicate legalMergeHead ( ClassObjectInternal cls ) {
118- this .getTail ( ) .doesNotContain ( cls )
119- or
120- this = Empty ( )
121- }
122-
123117 predicate contains ( ClassObjectInternal cls ) {
124118 cls = this .getHead ( )
125119 or
126120 this .getTail ( ) .contains ( cls )
127121 }
128122
129- /** Use negative formulation to avoid negative recursion */
130- predicate doesNotContain ( ClassObjectInternal cls ) {
131- this .relevantForContains ( cls ) and
132- cls != this .getHead ( ) and
133- this .getTail ( ) .doesNotContain ( cls )
134- or
135- this = Empty ( )
136- }
137-
138- private predicate relevantForContains ( ClassObjectInternal cls ) {
139- exists ( ClassListList list |
140- list .getItem ( _) .getHead ( ) = cls and
141- list .getItem ( _) = this
142- )
143- or
144- exists ( ClassList l |
145- l .relevantForContains ( cls ) and
146- this = l .getTail ( )
123+ pragma [ nomagic]
124+ ClassObjectInternal findDeclaringClass ( string name ) {
125+ exists ( ClassObjectInternal head , ClassList tail , ClassDecl decl |
126+ this = Cons ( head , tail ) and decl = head .getClassDeclaration ( )
127+ |
128+ if decl .declaresAttribute ( name ) then result = head else result = tail .findDeclaringClass ( name )
147129 )
148130 }
149131
150- ClassObjectInternal findDeclaringClass ( string name ) {
151- exists ( ClassDecl head | head = this .getHead ( ) .getClassDeclaration ( ) |
152- if head .declaresAttribute ( name )
153- then result = this .getHead ( )
154- else result = this .getTail ( ) .findDeclaringClass ( name )
132+ pragma [ noinline]
133+ private ClassObjectInternal findDeclaringClassAttribute ( string name ) {
134+ result = this .findDeclaringClass ( name ) and
135+ (
136+ exists ( any ( Builtin b ) .getMember ( name ) )
137+ or
138+ declaredAttributeVar ( _, name , _)
155139 )
156140 }
157141
158142 predicate lookup ( string name , ObjectInternal value , CfgOrigin origin ) {
159- exists ( ClassObjectInternal decl | decl = this .findDeclaringClass ( name ) |
143+ exists ( ClassObjectInternal decl | decl = this .findDeclaringClassAttribute ( name ) |
160144 Types:: declaredAttribute ( decl , name , value , origin )
161145 )
162146 }
@@ -199,12 +183,18 @@ class ClassList extends TClassList {
199183 or
200184 this .duplicate ( n ) and result = this .deduplicate ( n + 1 )
201185 or
202- exists ( ClassObjectInternal cls |
203- n = this .firstIndex ( cls ) and
204- result = Cons ( cls , this . deduplicate ( n + 1 ) )
186+ exists ( ClassObjectInternal cls , ClassList tail |
187+ this .deduplicateCons ( n , cls , tail ) and
188+ result = Cons ( cls , tail )
205189 )
206190 }
207191
192+ pragma [ nomagic]
193+ private predicate deduplicateCons ( int n , ClassObjectInternal cls , ClassList tail ) {
194+ n = this .firstIndex ( cls ) and
195+ tail = this .deduplicate ( n + 1 )
196+ }
197+
208198 predicate isEmpty ( ) { this = Empty ( ) }
209199
210200 ClassList reverse ( ) { reverse_step ( this , Empty ( ) , result ) }
@@ -273,6 +263,24 @@ private class ClassListList extends TClassListList {
273263 result = this .getTail ( ) .getItem ( n - 1 )
274264 }
275265
266+ /**
267+ * Same as
268+ *
269+ * ```ql
270+ * result = this.getItem(n) and n = this.length() - 1
271+ * ```
272+ *
273+ * but avoids non-linear recursion.
274+ */
275+ ClassList getLastItem ( int n ) {
276+ n = 0 and this = ConsList ( result , EmptyList ( ) )
277+ or
278+ exists ( ClassListList tail |
279+ this = ConsList ( _, tail ) and
280+ result = tail .getLastItem ( n - 1 )
281+ )
282+ }
283+
276284 private ClassObjectInternal getAHead ( ) {
277285 result = this .getHead ( ) .getHead ( )
278286 or
@@ -295,17 +303,26 @@ private class ClassListList extends TClassListList {
295303 ClassObjectInternal cls , ClassList removed_head , ClassListList removed_tail , int n
296304 ) {
297305 cls = this .bestMergeCandidate ( ) and
298- n = this .length ( ) - 1 and
299- removed_head = this .getItem ( n ) .removeHead ( cls ) and
306+ removed_head = this .getLastItem ( n ) .removeHead ( cls ) and
300307 removed_tail = EmptyList ( )
301308 or
309+ removed_head = this .removedClassPartsCons1 ( cls , removed_tail , n ) .removeHead ( cls )
310+ }
311+
312+ pragma [ nomagic]
313+ predicate removedClassPartsCons0 ( ClassObjectInternal cls , ClassListList removed_tail , int n ) {
302314 exists ( ClassList prev_head , ClassListList prev_tail |
303315 this .removedClassParts ( cls , prev_head , prev_tail , n + 1 ) and
304- removed_head = this .getItem ( n ) .removeHead ( cls ) and
305316 removed_tail = ConsList ( prev_head , prev_tail )
306317 )
307318 }
308319
320+ pragma [ nomagic]
321+ ClassList removedClassPartsCons1 ( ClassObjectInternal cls , ClassListList removed_tail , int n ) {
322+ this .removedClassPartsCons0 ( cls , removed_tail , n ) and
323+ result = this .getItem ( n )
324+ }
325+
309326 ClassListList remove ( ClassObjectInternal cls ) {
310327 exists ( ClassList removed_head , ClassListList removed_tail |
311328 this .removedClassParts ( cls , removed_head , removed_tail , 0 ) and
@@ -315,18 +332,34 @@ private class ClassListList extends TClassListList {
315332 this = EmptyList ( ) and result = EmptyList ( )
316333 }
317334
318- predicate legalMergeCandidate ( ClassObjectInternal cls , int n ) {
319- cls = this .getAHead ( ) and n = this .length ( )
335+ pragma [ nomagic]
336+ private predicate legalMergeCandidateNonEmpty (
337+ ClassObjectInternal cls , ClassListList remainingList , ClassList remaining
338+ ) {
339+ this .legalMergeCandidate ( cls , ConsList ( Cons ( _, remaining ) , remainingList ) )
320340 or
321- this .getItem ( n ) .legalMergeHead ( cls ) and
322- this .legalMergeCandidate ( cls , n + 1 )
341+ exists ( ClassObjectInternal head |
342+ this .legalMergeCandidateNonEmpty ( cls , remainingList , Cons ( head , remaining ) ) and
343+ cls != head
344+ )
323345 }
324346
325- predicate legalMergeCandidate ( ClassObjectInternal cls ) { this .legalMergeCandidate ( cls , 0 ) }
347+ private predicate legalMergeCandidate ( ClassObjectInternal cls , ClassListList remaining ) {
348+ cls = this .getAHead ( ) and remaining = this
349+ or
350+ this .legalMergeCandidate ( cls , ConsList ( Empty ( ) , remaining ) )
351+ or
352+ this .legalMergeCandidateNonEmpty ( cls , remaining , Empty ( ) )
353+ }
326354
355+ pragma [ noinline]
356+ predicate legalMergeCandidate ( ClassObjectInternal cls ) {
357+ this .legalMergeCandidate ( cls , EmptyList ( ) )
358+ }
359+
360+ pragma [ noinline]
327361 predicate illegalMergeCandidate ( ClassObjectInternal cls ) {
328- cls = this .getAHead ( ) and
329- this .getItem ( _) .getTail ( ) .contains ( cls )
362+ this .legalMergeCandidateNonEmpty ( cls , _, Cons ( cls , _) )
330363 }
331364
332365 ClassObjectInternal bestMergeCandidate ( int n ) {
@@ -337,6 +370,7 @@ private class ClassListList extends TClassListList {
337370 )
338371 }
339372
373+ pragma [ noinline]
340374 ClassObjectInternal bestMergeCandidate ( ) { result = this .bestMergeCandidate ( 0 ) }
341375
342376 /**
@@ -417,14 +451,25 @@ private predicate merge_step(
417451 remaining_list = original
418452 or
419453 /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */
420- exists ( ClassObjectInternal head , ClassList prev_reverse_mro , ClassListList prev_list |
454+ exists ( ClassObjectInternal head , ClassList prev_reverse_mro |
455+ merge_stepCons ( head , prev_reverse_mro , remaining_list , original ) and
456+ reversed_mro = Cons ( head , prev_reverse_mro )
457+ )
458+ or
459+ merge_step ( reversed_mro , ConsList ( Empty ( ) , remaining_list ) , original )
460+ }
461+
462+ pragma [ nomagic]
463+ private predicate merge_stepCons (
464+ ClassObjectInternal head , ClassList prev_reverse_mro , ClassListList remaining_list ,
465+ ClassListList original
466+ ) {
467+ /* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */
468+ exists ( ClassListList prev_list |
421469 merge_step ( prev_reverse_mro , prev_list , original ) and
422470 head = prev_list .bestMergeCandidate ( ) and
423- reversed_mro = Cons ( head , prev_reverse_mro ) and
424471 remaining_list = prev_list .remove ( head )
425472 )
426- or
427- merge_step ( reversed_mro , ConsList ( Empty ( ) , remaining_list ) , original )
428473}
429474
430475/* Helpers for `ClassList.reverse()` */
@@ -439,10 +484,17 @@ private predicate reverse_step(ClassList lst, ClassList remainder, ClassList rev
439484 or
440485 exists ( ClassObjectInternal head , ClassList tail |
441486 reversed = Cons ( head , tail ) and
442- reverse_step ( lst , Cons ( head , remainder ) , tail )
487+ reverse_stepCons ( lst , remainder , head , tail )
443488 )
444489}
445490
491+ pragma [ nomagic]
492+ private predicate reverse_stepCons (
493+ ClassList lst , ClassList remainder , ClassObjectInternal head , ClassList tail
494+ ) {
495+ reverse_step ( lst , Cons ( head , remainder ) , tail )
496+ }
497+
446498module Mro {
447499 cached
448500 ClassList newStyleMro ( ClassObjectInternal cls ) {
0 commit comments