Python: Enhance points-to to support type-hint analysis.

This commit is contained in:
Mark Shannon
2019-08-09 16:23:07 +01:00
parent 3fab5140a7
commit 6c6e35f541
18 changed files with 193 additions and 4 deletions

View File

@@ -365,3 +365,81 @@ class DynamicallyCreatedClass extends ClassObjectInternal, TDynamicClass {
}
class SubscriptedTypeInternal extends ObjectInternal, TSubscriptedType {
ObjectInternal getGeneric() {
this = TSubscriptedType(result, _)
}
ObjectInternal getSpecializer() {
this = TSubscriptedType(_, result)
}
override string getName() { result = this.getGeneric().getName() }
override string toString() { result = this.getGeneric().toString() + "[" + this.getSpecializer().toString() + "]" }
override predicate introducedAt(ControlFlowNode node, PointsToContext context) {
exists(ObjectInternal generic, ObjectInternal index |
this = TSubscriptedType(generic, index) and
Expressions::subscriptPartsPointsTo(node, context, generic, index)
)
}
/** Gets the class declaration for this object, if it is a class with a declaration. */
override ClassDecl getClassDeclaration() {
result = this.getGeneric().getClassDeclaration()
}
/** True if this "object" is a class. That is, its class inherits from `type` */
override boolean isClass() { result = true }
override ObjectInternal getClass() {
result = this.getGeneric().getClass()
}
override predicate notTestableForEquality() { none() }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { none() }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
override predicate calleeAndOffset(Function scope, int paramOffset){ none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
override boolean isDescriptor() { result = false }
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }
override predicate descriptorGetInstance(ObjectInternal instance, ObjectInternal value, CfgOrigin origin) { none() }
override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) { none() }
override int length() { none() }
override boolean booleanValue() { result = true }
override int intValue() { none()}
override string strValue() { none() }
override predicate subscriptUnknown() { none() }
override predicate contextSensitiveCallee() { none() }
override predicate useOriginAsLegacyObject() { none() }
/* Classes aren't usually iterable, but can e.g. Enums */
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
}

View File

@@ -91,7 +91,14 @@ class SpecificInstanceInternal extends TSpecificInstance, InstanceObject {
override predicate notTestableForEquality() { none() }
override ObjectInternal getClass() {
this = TSpecificInstance(_, result, _)
exists(ClassObjectInternal cls, ClassDecl decl |
this = TSpecificInstance(_, cls, _) and
decl = cls.getClassDeclaration() |
if decl.callReturnsInstance() then
result = cls
else
result = TUnknownClass()
)
}
/** Gets the `Builtin` for this object, if any.

View File

@@ -322,7 +322,11 @@ class ClassValue extends Value {
/** Gets an improper super type of this class. */
ClassValue getASuperType() {
result = Types::getMro(this).getAnItem()
result = this.getBaseType(_)
or
result = this.getASuperType().getBaseType(_)
or
result = this
}
/** Looks up the attribute `name` on this class.

View File

@@ -557,6 +557,8 @@ class DecoratedFunction extends ObjectInternal, TDecoratedFunction {
override string toString() {
result = "Decorated " + this.decoratedObject().toString()
or
not exists(this.decoratedObject()) and result = "Decorated function"
}
override boolean booleanValue() { result = true }

View File

@@ -49,7 +49,14 @@ abstract class TupleObjectInternal extends SequenceObjectInternal {
or
n = 3 and this.length() > 3 and result = (this.length()-3).toString() + " more..."
or
result = this.getItem(n).toString() + ", " + this.contents(n+1)
result = this.item(n) + ", " + this.contents(n+1)
}
private string item(int n) {
result = this.getItem(n).toString()
or
n in [0..this.length()-1] and
not exists(this.getItem(n)) and result = "?"
}
/** Gets the class declaration for this object, if it is a declared class. */

View File

@@ -132,7 +132,7 @@ cached newtype TObject =
/* An instance of `cls`, instantiated at `instantiation` given the `context`. */
TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) {
PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and
cls.isSpecial() = false and cls.getClassDeclaration().callReturnsInstance()
cls.isSpecial() = false
or
literal_instantiation(instantiation, cls, context)
}
@@ -236,6 +236,18 @@ cached newtype TObject =
TDecoratedFunction(CallNode call) {
call.isFunctionDecoratorCall()
}
or
/* Represents a subscript operation applied to a type. For type-hint analysis */
TSubscriptedType(ObjectInternal generic, ObjectInternal index) {
isType(generic) and
Expressions::subscriptPartsPointsTo(_, _, generic, index)
}
predicate isType(ObjectInternal t) {
t.isClass() = true
or
t.getOrigin().getEnclosingModule().getName().matches("%typing")
}
private predicate is_power_2(int n) {
n = 1 or

View File

@@ -1229,6 +1229,13 @@ module Expressions {
origin = subscr
}
predicate subscriptPartsPointsTo(SubscriptNode subscr, PointsToContext context, ObjectInternal objvalue, ObjectInternal indexvalue) {
exists(ControlFlowNode index |
subscriptObjectAndIndex(subscr, context, _, objvalue, index) and
PointsToInternal::pointsTo(index, context, indexvalue, _)
)
}
pragma [noinline]
private predicate subscriptObjectAndIndex(SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue, ControlFlowNode index) {
subscr.isLoad() and