Python points-to: Add support for tuples.

This commit is contained in:
Mark Shannon
2019-03-25 13:14:19 +00:00
parent dd83149cc3
commit 12853ccf30
3 changed files with 146 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ import semmle.python.objects.Classes
import semmle.python.objects.Instances
import semmle.python.objects.Callables
import semmle.python.objects.Constants
import semmle.python.objects.Sequences
class ObjectInternal extends TObject {

View File

@@ -0,0 +1,135 @@
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.PointsToContext
private import semmle.python.pointsto.MRO2
private import semmle.python.types.Builtins
abstract class SequenceObjectInternal extends ObjectInternal {
abstract ObjectInternal getItem(int n);
abstract int length();
/** The boolean value of this object, this may be both
* true and false if the "object" represents a set of possible objects. */
override boolean booleanValue() {
this.length() = 0 and result = false
or
this.length() != 0 and result = true
}
}
abstract class TupleObjectInternal extends SequenceObjectInternal {
override string toString() {
result = "tuple()"
}
/** Gets the class declaration for this object, if it is a declared class. */
override ClassDecl getClassDeclaration() { none() }
/** True if this "object" is a class. */
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") }
/** True if this "object" can be meaningfully analysed for
* truth or false in comparisons. For example, `None` or `int` can be, but `int()`
* or an unknown string cannot.
*/
override boolean isComparable() { result = true }
/** Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj`.
*/
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
/** Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj` with callee context `callee`.
*/
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
/** The integer value of things that have integer values.
* That is, ints and bools.
*/
override int intValue() { none() }
/** The integer value of things that have integer values.
* That is, strings.
*/
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
}
class BuiltinTupleObjectInternal extends TBuiltinTuple, TupleObjectInternal {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
none()
}
override Builtin getBuiltin() {
this = TBuiltinTuple(result)
}
override ControlFlowNode getOrigin() {
none()
}
override ObjectInternal getItem(int n) {
result.getBuiltin() = this.getBuiltin().getItem(n)
}
override int length() {
exists(Builtin b |
b = this.getBuiltin() and
result = count(int n | exists(b.getItem(n)))
)
}
}
class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal {
override predicate introduced(ControlFlowNode node, PointsToContext context) {
this = TPythonTuple(node, context)
}
override Builtin getBuiltin() {
none()
}
override ControlFlowNode getOrigin() {
this = TPythonTuple(result, _)
}
override ObjectInternal getItem(int n) {
exists(TupleNode t, PointsToContext context |
this = TPythonTuple(t, context) and
PointsTo2::points_to(t.getElement(n), context, result, _)
)
}
override int length() {
exists(TupleNode t |
this = TPythonTuple(t, _) and
result = count(int n | exists(t.getElement(n)))
)
}
}

View File

@@ -18,6 +18,7 @@ newtype TObject =
TBuiltinOpaqueObject(Builtin bltn) {
not bltn.isClass() and not bltn.isFunction() and
not bltn.isMethod() and not bltn.isModule() and
not bltn.getClass() = Builtin::special("tuple") and
not exists(bltn.intValue()) and
not exists(bltn.strValue()) and
not py_special_objects(bltn, _)
@@ -99,6 +100,14 @@ newtype TObject =
TStaticMethod(CallNode instantiation, CallableObjectInternal function) {
static_method(instantiation, function, _)
}
or
TBuiltinTuple(Builtin bltn) {
bltn.getClass() = Builtin::special("tuple")
}
or
TPythonTuple(TupleNode origin, PointsToContext context) {
context.appliesTo(origin)
}
private predicate is_power_2(int n) {
n = 1 or
@@ -254,6 +263,7 @@ library class ClassDecl extends @py_object {
exists(string name |
this = Builtin::special(name) |
not name = "object" and
not name = "list" and
not name = "set" and
not name.matches("%Exception") and
not name.matches("%Error")