mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Python: Add support for bound-methods.
This commit is contained in:
243
python/ql/src/semmle/python/objects/Callables.qll
Normal file
243
python/ql/src/semmle/python/objects/Callables.qll
Normal 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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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, _)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
476
python/ql/src/semmle/python/pointsto/MRO2.qll
Normal file
476
python/ql/src/semmle/python/pointsto/MRO2.qll
Normal 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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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`. */
|
||||
|
||||
Reference in New Issue
Block a user