Skip to content

Commit 8460ab4

Browse files
authored
Merge pull request #7549 from hvitved/python/points-to-perf
2 parents c81f266 + 92fa007 commit 8460ab4

File tree

2 files changed

+215
-86
lines changed

2 files changed

+215
-86
lines changed

python/ql/lib/semmle/python/pointsto/MRO.qll

Lines changed: 100 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
446498
module Mro {
447499
cached
448500
ClassList newStyleMro(ClassObjectInternal cls) {

0 commit comments

Comments
 (0)