Python: Add support for bound-methods.

This commit is contained in:
Mark Shannon
2019-03-20 16:45:05 +00:00
parent bf692f4aad
commit 39b9723054
14 changed files with 882 additions and 284 deletions

View File

@@ -0,0 +1,243 @@
import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.pointsto.MRO2
private import semmle.python.types.Builtins
abstract class CallableObjectInternal extends ObjectInternal {
override int intValue() {
none()
}
override string strValue() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
/** The boolean value of this object, if it has one */
override boolean booleanValue() {
result = true
}
/** Holds if this object may be true or false when evaluated as a bool */
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
abstract string getName();
}
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
Function getScope() {
exists(CallableExpr expr |
this = TPythonFunctionObject(expr.getAFlowNode()) and
result = expr.getInnerScope()
)
}
override string toString() {
result = this.getScope().toString()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
this = TPythonFunctionObject(node) and context.appliesTo(node)
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Builtin getBuiltin() {
none()
}
override ControlFlowNode getOrigin() {
this = TPythonFunctionObject(result)
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func, ControlFlowNode rval |
func = this.getScope() and
callee.appliesToScope(func) and
rval = func.getAReturnValueFlowNode() and
PointsTo2::points_to(rval, callee, obj, origin)
)
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
scope = this.getScope() and paramOffset = 0
}
override string getName() {
result = this.getScope().getName()
}
}
class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunctionObject {
override Builtin getBuiltin() {
this = TBuiltinFunctionObject(result)
}
override string toString() {
result = "builtin function " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should be be a unknown value of a known class if the return type is known or just an unknown.
none()
}
override ControlFlowNode getOrigin() {
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
none()
}
override string getName() {
result = this.getBuiltin().getName()
}
}
class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethodObject {
override Builtin getBuiltin() {
this = TBuiltinMethodObject(result)
}
override string toString() {
result = "builtin method " + this.getBuiltin().getName()
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should be be a unknown value of a known class if the return type is known or just an unknown.
none()
}
override ControlFlowNode getOrigin() {
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
none()
}
override string getName() {
result = this.getBuiltin().getName()
}
}
class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
override Builtin getBuiltin() {
none()
}
CallableObjectInternal getFunction() {
this = TBoundMethod(_, _, result, _)
}
ObjectInternal getSelf() {
this = TBoundMethod(_, result, _, _)
}
override string toString() {
result = "bound method '" + this.getFunction().getName() + "' of " + this.getSelf().toString()
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("MethodType"))
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
this = TBoundMethod(node, _, _, context)
}
override predicate isComparable() {
none()
}
override predicate notComparable() {
any()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
this.getFunction().callResult(callee, obj, origin)
}
override ControlFlowNode getOrigin() {
this = TBoundMethod(result, _, _, _)
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
this.getFunction().calleeAndOffset(scope, paramOffset-1)
}
override string getName() {
result = this.getFunction().getName()
}
}

View File

@@ -35,6 +35,9 @@ abstract class ClassObjectInternal extends ObjectInternal {
abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin);
boolean isSpecial() {
result = Types::getMro(this).isSpecial()
}
}
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
@@ -148,3 +151,55 @@ class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObjec
}
}
class UnknownClassInternal extends ClassObjectInternal, TUnknownClass {
override string toString() {
none()
}
override ClassDecl getClassDeclaration() {
result = Builtin::unknownType()
}
override ObjectInternal getClass() {
result = TUnknownClass()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
none()
}
override predicate notComparable() {
any()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
}
override ControlFlowNode getOrigin() {
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
none()
}
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) {
none()
}
}

View File

@@ -10,7 +10,7 @@ private import semmle.python.types.Builtins
class InstanceInternal extends TInstance, ObjectInternal {
override string toString() {
result = "instance of " + this.getClass().(ClassObjectInternal).getClassDeclaration().getName()
result = "instance of " + this.getClass().(ClassObjectInternal).getName()
}
/** The boolean value of this object, if it has one */

View File

@@ -6,6 +6,8 @@ private import semmle.python.pointsto.PointsToContext2
private import semmle.python.types.Builtins
import semmle.python.objects.Modules
import semmle.python.objects.Classes
import semmle.python.objects.Instances
import semmle.python.objects.Callables
import semmle.python.objects.Constants
class ObjectInternal extends TObject {
@@ -79,214 +81,6 @@ class ObjectInternal extends TObject {
}
class PythonFunctionObjectInternal extends ObjectInternal, TPythonFunctionObject {
Function getScope() {
exists(CallableExpr expr |
this = TPythonFunctionObject(expr.getAFlowNode()) and
result = expr.getInnerScope()
)
}
override string toString() {
result = this.getScope().toString()
}
/** The boolean value of this object, if it has one */
override boolean booleanValue() {
result = true
}
/** Holds if this object may be true or false when evaluated as a bool */
override predicate maybe() { none() }
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
this = TPythonFunctionObject(node) and context.appliesTo(node)
}
/** INTERNAL */
override ClassDecl getClassDeclaration() { none() }
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Builtin getBuiltin() {
none()
}
override ControlFlowNode getOrigin() {
this = TPythonFunctionObject(result)
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func, ControlFlowNode rval |
func = this.getScope() and
callee.appliesToScope(func) and
rval = func.getAReturnValueFlowNode() and
PointsTo2::points_to(rval, callee, obj, origin)
)
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
scope = this.getScope() and paramOffset = 0
}
override int intValue() {
none()
}
override string strValue() {
none()
}
}
/// BOUND METHODS
///
///
///
class BuiltinFunctionObjectInternal extends ObjectInternal, TBuiltinFunctionObject {
override Builtin getBuiltin() {
this = TBuiltinFunctionObject(result)
}
override string toString() {
result = "builtin function " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override boolean booleanValue() {
result = true
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should be be a unknown value of a known class if the return type is known or just an unknown.
none()
}
override ControlFlowNode getOrigin() {
none()
}
override int intValue() {
none()
}
override string strValue() {
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
none()
}
}
class BuiltinMethodObjectInternal extends ObjectInternal, TBuiltinMethodObject {
override Builtin getBuiltin() {
this = TBuiltinMethodObject(result)
}
override string toString() {
result = "builtin method " + this.getBuiltin().getName()
}
override boolean booleanValue() {
result = true
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should be be a unknown value of a known class if the return type is known or just an unknown.
none()
}
override ControlFlowNode getOrigin() {
none()
}
override int intValue() {
none()
}
override string strValue() {
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
none()
}
}
class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
override Builtin getBuiltin() {
@@ -354,66 +148,6 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
}
private predicate callee_for_object(PointsToContext2 callee, ObjectInternal obj) {
exists(CallNode call, PointsToContext2 caller |
callee.fromCall(call, caller) and
PointsTo2::points_to(call.getFunction(), caller, obj, _)
)
}
class UnknownClassInternal extends ClassObjectInternal, TUnknownClass {
override string toString() {
none()
}
override ClassDecl getClassDeclaration() {
result = Builtin::unknownType()
}
override predicate isClass() { any() }
override predicate notClass() { none() }
override ObjectInternal getClass() {
result = TUnknownClass()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
none()
}
override predicate notComparable() {
any()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
}
override ControlFlowNode getOrigin() {
none()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
none()
}
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) {
none()
}
}
class UnknownInternal extends ObjectInternal, TUnknown {

View File

@@ -73,7 +73,15 @@ newtype TObject =
or
TInstance(CallNode instantiation, ClassObjectInternal cls, PointsToContext2 context) {
PointsTo2::points_to(instantiation.getFunction(), context, cls, _) and
normal_class(cls)
cls.isSpecial() = false
}
or
TBoundMethod(AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext2 context) {
exists(ControlFlowNode objnode, string name |
objnode = instantiation.getObject(name) and
PointsTo2::points_to(objnode, context, self, _) and
self.getClass().(ClassObjectInternal).attribute(name, function, _)
)
}
private predicate is_power_2(int n) {
@@ -81,14 +89,6 @@ private predicate is_power_2(int n) {
exists(int half | is_power_2(half) and n = half*2)
}
private predicate normal_class(ClassObjectInternal cls) {
exists(Builtin bltn |
bltn = cls.getBuiltin() |
not bltn = Builtin::special(_)
)
//or
//cls.getMro().inheritsFromType(false)
}
library class ClassDecl extends @py_object {
@@ -118,4 +118,25 @@ library class ClassDecl extends @py_object {
result = this.getClass().getName()
}
/** Whether this is a class whose instances we treat specially, rather than as a generic instance.
*/
predicate isSpecial() {
exists(string name |
this = Builtin::special(name) |
not name = "object" and
not name = "set" and
not name.matches("%Exception") and
not name.matches("%Error")
)
}
}
predicate callee_for_object(PointsToContext2 callee, ObjectInternal obj) {
exists(CallNode call, PointsToContext2 caller |
callee.fromCall(call, caller) and
PointsTo2::points_to(call.getFunction(), caller, obj, _)
)
}

View File

@@ -0,0 +1,476 @@
/** Classes and predicates for computing the Method Resolution Order (MRO) of classes.
* Supports both old-style (diamond) inheritance and new-style (C3 linearization) inheritance.
*/
/*
* Implementation of the C3 linearization algorithm.
* See https://en.wikipedia.org/wiki/C3_linearization
*
* The key operation is merge, which takes a list of lists and produces a list.
* We implement it as the method `ClassListList.merge()`
*
* To support that we need to determine the best candidate to extract from a list of lists,
* implemented as `ClassListList.bestMergeCandidate()`
*
* The following code is designed to implement those operations
* without negation and as efficiently as possible.
*/
import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.types.Builtins
cached private newtype TClassList = Empty()
or
Cons(ClassObjectInternal head, TClassList tail) {
required_cons(head, tail)
}
/* Keep ClassList finite and as small as possible */
private predicate required_cons(ClassObjectInternal head, ClassList tail) {
tail = merge_of_linearization_of_bases(head)
or
exists(ClassObjectInternal cls, int n |
head = Types::getBase(cls, n) and tail = bases(cls, n+1)
)
or
head = ObjectInternal::builtin("object") and tail = Empty()
or
reverse_step(_, Cons(head, _), tail)
or
exists(ClassListList list |
merge_step(tail, list, _) and
head = list.bestMergeCandidate()
)
or
exists(ClassList list, int n |
n = list.firstIndex(head) and
tail = list.deduplicate(n+1)
)
or
exists(ClassListList list, int n |
head = list.getHead().getItem(n) and
tail = flatten_list(list, n+1)
)
or
tail = list_old_style_base_mros(head).flatten()
}
/** A list of classes, used to represent the MRO of a class */
class ClassList extends TClassList {
string toString() {
result = "[" + this.contents() + "]"
}
string contents() {
this = Empty() and result = ""
or
exists(ClassObjectInternal head |
head = this.getHead() |
this.getTail() = Empty() and result = head.getName()
or
this.getTail() != Empty() and result = head.getName() + ", " + this.getTail().contents()
)
}
int length() {
this = Empty() and result = 0
or
result = this.getTail().length() + 1
}
ClassObjectInternal getHead() {
this = Cons(result, _)
}
ClassList getTail() {
this = Cons(_, result)
}
ClassObjectInternal getItem(int n) {
n = 0 and result = this.getHead()
or
result = this.getTail().getItem(n-1)
}
pragma [inline]
ClassList removeHead(ClassObjectInternal cls) {
this.getHead() = cls and result = this.getTail()
or
this.getHead() != cls and result = this
or
this = Empty() and result = Empty()
}
predicate legalMergeHead(ClassObjectInternal cls) {
this.getTail().doesNotContain(cls)
or
this = Empty()
}
/** Use negative formulation for efficiency */
predicate contains(ClassObjectInternal cls) {
cls = this.getHead()
or
this.getTail().contains(cls)
}
/** Use negative formulation to avoid negative recursion */
predicate doesNotContain(ClassObjectInternal cls) {
this.relevantForContains(cls) and
cls != this.getHead() and
this.getTail().doesNotContain(cls)
or
this = Empty()
}
private predicate relevantForContains(ClassObjectInternal cls) {
exists(ClassListList list |
list.getItem(_).getHead() = cls and
list.getItem(_) = this
)
or
exists(ClassList l |
l.relevantForContains(cls) and
this = l.getTail()
)
}
ClassObjectInternal findDeclaringClass(string name) {
exists(ClassDecl head |
head = this.getHead().getClassDeclaration() |
if head.declaresAttribute(name) then
result = this.getHead()
else
result = this.getTail().findDeclaringClass(name)
)
}
predicate declares(string name) {
this.getHead().getClassDeclaration().declaresAttribute(name)
or
this.getTail().declares(name)
}
ClassList startingAt(ClassObjectInternal cls) {
exists(ClassObjectInternal head |
head = this.getHead() |
if head = cls then
result = this
else
result = this.getTail().startingAt(cls)
)
}
ClassList deduplicate() {
result = this.deduplicate(0)
}
/* Helpers for `deduplicate()` */
int firstIndex(ClassObjectInternal cls) {
result = this.firstIndex(cls, 0)
}
/* Helper for firstIndex(cls), getting the first index of `cls` where result >= n */
private int firstIndex(ClassObjectInternal cls, int n) {
this.getItem(n) = cls and result = n
or
this.getItem(n) != cls and result = this.firstIndex(cls, n+1)
}
/** Holds if the class at `n` is a duplicate of an earlier position. */
private predicate duplicate(int n) {
exists(ClassObjectInternal cls |
cls = this.getItem(n) and this.firstIndex(cls) < n
)
}
/** Gets a class list which is the de-duplicated form of the list containing elements of
* this list from `n` onwards.
*/
ClassList deduplicate(int n) {
n = this.length() and result = Empty()
or
this.duplicate(n) and result = this.deduplicate(n+1)
or
exists(ClassObjectInternal cls |
n = this.firstIndex(cls) and
result = Cons(cls, this.deduplicate(n+1))
)
}
predicate isEmpty() {
this = Empty()
}
ClassList reverse() {
reverse_step(this, Empty(), result)
}
boolean isSpecial() {
this = Empty() and result = false
or
exists(ClassDecl decl |
decl = this.getHead().getClassDeclaration() |
if decl.isSpecial() then
result = true
else
result = this.getTail().isSpecial()
)
}
}
private newtype TClassListList =
EmptyList() or
ConsList(TClassList head, TClassListList tail) {
required_list(head, tail)
}
/* Keep ClassListList finite and as small as possible */
private predicate required_list(ClassList head, ClassListList tail) {
any(ClassListList x).removedClassParts(_, head, tail, _)
or
head = bases(_) and tail = EmptyList()
or
exists(ClassObjectInternal cls, int n |
head = Mro::newStyleMro(Types::getBase(cls, n)) and
tail = list_of_linearization_of_bases_plus_bases(cls, n+1)
)
or
exists(ClassObjectInternal cls, int n |
head = Mro::oldStyleMro(Types::getBase(cls, n)) and
tail = list_old_style_base_mros(cls, n+1)
)
}
private class ClassListList extends TClassListList {
string toString() {
result = "[" + this.contents() + "]"
}
string contents() {
this = EmptyList() and result = ""
or
exists(ClassList head |
head = this.getHead() |
this.getTail() = EmptyList() and result = head.toString()
or
this.getTail() != EmptyList() and result = head.toString() + ", " + this.getTail().contents()
)
}
int length() {
this = EmptyList() and result = 0
or
result = this.getTail().length() + 1
}
ClassList getHead() {
this = ConsList(result, _)
}
ClassListList getTail() {
this = ConsList(_, result)
}
ClassList getItem(int n) {
n = 0 and result = this.getHead()
or
result = this.getTail().getItem(n-1)
}
private ClassObjectInternal getAHead() {
result = this.getHead().getHead()
or
result = this.getTail().getAHead()
}
pragma [nomagic]
ClassList merge() {
exists(ClassList reversed |
merge_step(reversed, EmptyList(), this) and
result = reversed.reverse()
)
or
this = EmptyList() and result = Empty()
}
/* Join ordering helper */
pragma [noinline]
predicate removedClassParts(ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n) {
cls = this.bestMergeCandidate() and n = this.length()-1 and
removed_head = this.getItem(n).removeHead(cls) and removed_tail = EmptyList()
or
exists(ClassList prev_head, ClassListList prev_tail |
this.removedClassParts(cls, prev_head, prev_tail, n+1) and
removed_head = this.getItem(n).removeHead(cls) and
removed_tail = ConsList(prev_head, prev_tail)
)
}
ClassListList remove(ClassObjectInternal cls) {
exists(ClassList removed_head, ClassListList removed_tail |
this.removedClassParts(cls, removed_head, removed_tail, 0) and
result = ConsList(removed_head, removed_tail)
)
or
this = EmptyList() and result = EmptyList()
}
predicate legalMergeCandidate(ClassObjectInternal cls, int n) {
cls = this.getAHead() and n = this.length()
or
this.getItem(n).legalMergeHead(cls) and
this.legalMergeCandidate(cls, n+1)
}
predicate legalMergeCandidate(ClassObjectInternal cls) {
this.legalMergeCandidate(cls, 0)
}
predicate illegalMergeCandidate(ClassObjectInternal cls) {
cls = this.getAHead() and
this.getItem(_).getTail().contains(cls)
}
ClassObjectInternal bestMergeCandidate(int n) {
exists(ClassObjectInternal head |
head = this.getItem(n).getHead()
|
legalMergeCandidate(head) and result = head
or
illegalMergeCandidate(head) and result = this.bestMergeCandidate(n+1)
)
}
ClassObjectInternal bestMergeCandidate() {
result = this.bestMergeCandidate(0)
}
/** Gets a ClassList representing the this list of list flattened into a single list.
* Used for old-style MRO computation.
*/
ClassList flatten() {
this = EmptyList() and result = Empty()
or
result = flatten_list(this, 0)
}
}
private ClassList flatten_list(ClassListList list, int n) {
need_flattening(list) and
exists(ClassList head, ClassListList tail |
list = ConsList(head, tail)
|
n = head.length() and result = tail.flatten()
or
result = Cons(head.getItem(n), flatten_list(list, n+1))
)
}
/* Restrict flattening to those lists that need to be flattened */
private predicate need_flattening(ClassListList list) {
list = list_old_style_base_mros(_)
or
exists(ClassListList toflatten |
need_flattening(toflatten) and
list = toflatten.getTail()
)
}
private ClassList bases(ClassObjectInternal cls) {
result = bases(cls, 0)
}
private ClassList bases(ClassObjectInternal cls, int n) {
result = Cons(Types::getBase(cls, n), bases(cls, n+1))
or
result = Empty() and n = Types::base_count(cls)
}
private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls) {
result = list_of_linearization_of_bases_plus_bases(cls, 0)
}
private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInternal cls, int n) {
result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls)
or
exists(ClassListList partial |
partial = list_of_linearization_of_bases_plus_bases(cls, n+1) and
result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial)
)
}
private ClassList merge_of_linearization_of_bases(ClassObjectInternal cls) {
result = list_of_linearization_of_bases_plus_bases(cls).merge()
}
private ClassListList list_old_style_base_mros(ClassObjectInternal cls) {
result = list_old_style_base_mros(cls, 0)
}
pragma [nomagic]
private ClassListList list_old_style_base_mros(ClassObjectInternal cls, int n) {
n = Types::base_count(cls) and result = EmptyList()
or
result = ConsList(Mro::oldStyleMro(Types::getBase(cls, n)), list_old_style_base_mros(cls, n+1))
}
/** Holds if the pair `reversed_mro`, `remaining_list` represents a step in the C3 merge operation
* of computing the C3 linearization of `original`.
*/
private predicate merge_step(ClassList reversed_mro, ClassListList remaining_list, ClassListList original) {
remaining_list = list_of_linearization_of_bases_plus_bases(_) and reversed_mro = Empty() and remaining_list = original
or
/* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */
exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list |
merge_step(prev_reverse_mro, prev_list, original) and
head = prev_list.bestMergeCandidate() and
reversed_mro = Cons(head, prev_reverse_mro) and
remaining_list = prev_list.remove(head)
)
or
merge_step(reversed_mro, ConsList(Empty(), remaining_list), original)
}
/* Helpers for `ClassList.reverse()` */
private predicate needs_reversing(ClassList lst) {
merge_step(lst, EmptyList(), _)
or
lst = Empty()
}
private predicate reverse_step(ClassList lst, ClassList remainder, ClassList reversed) {
needs_reversing(lst) and remainder = lst and reversed = Empty()
or
exists(ClassObjectInternal head, ClassList tail |
reversed = Cons(head, tail) and
reverse_step(lst, Cons(head, remainder), tail)
)
}
module Mro {
cached ClassList newStyleMro(ClassObjectInternal cls) {
cls = ObjectInternal::builtin("object") and result = Cons(cls, Empty())
or
result = Cons(cls, merge_of_linearization_of_bases(cls))
}
cached ClassList oldStyleMro(ClassObjectInternal cls) {
Types::isOldStyle(cls) and
result = Cons(cls, list_old_style_base_mros(cls).flatten()).(ClassList).deduplicate()
}
}

View File

@@ -174,12 +174,8 @@ class PointsToContext2 extends TPointsToContext {
this = TCallContext(call, outerContext, _) and
s = func.getScope()
)
// TO DO...
//or
//exists(FunctionObject func |
// PointsTo2::Flow::callsite_calls_function(_, _, func, this, _) and
// s = func.getFunction()
//)
or
InterProceduralPointsTo::callsite_calls_function(_, _, s, this, _)
}
/** Holds if this context can apply to the CFG node `n`. */