Python points-to: Add more comments about TObject and ObjectInternal.

This commit is contained in:
Mark Shannon
2019-05-22 12:58:06 +01:00
parent f057d5cb6b
commit 1018f27815
2 changed files with 45 additions and 9 deletions

View File

@@ -38,15 +38,14 @@ class ObjectInternal extends TObject {
/** Gets the class declaration for this object, if it is a class with a declaration. */
abstract ClassDecl getClassDeclaration();
/** True if this "object" is a class. */
/** True if this "object" is a class. That is, its class inherits from `type` */
abstract boolean isClass();
/** Holds if this object is a class. That is, it's class inherits from `type` */
/** Gets the class of this object. */
abstract ObjectInternal getClass();
/** 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.
/** True if this "object" can be meaningfully analysed to determine its boolean value in comparisons.
* For example, `None` or `int` can be, but `int()` or an unknown string cannot.
*/
abstract boolean isComparable();
@@ -57,7 +56,7 @@ class ObjectInternal extends TObject {
abstract Builtin getBuiltin();
/** Gets a control flow node that represents the source origin of this
* objects. Source code objects should attempt to return
* object. Source code objects should attempt to return
* exactly one result for this method.
*/
abstract ControlFlowNode getOrigin();
@@ -100,10 +99,10 @@ class ObjectInternal extends TObject {
pragma [nomagic]
abstract predicate attribute(string name, ObjectInternal value, CfgOrigin origin);
/** Holds if the attributes of this object are wholy or partly unknowable */
/** Holds if the attributes of this object are wholly or partly unknowable */
abstract predicate attributesUnknown();
/** Holds if the result of subscripting this object are wholy or partly unknowable */
/** Holds if the result of subscripting this object are wholly or partly unknowable */
abstract predicate subscriptUnknown();
/** For backwards compatibility shim -- Not all objects have a "source".
@@ -140,7 +139,7 @@ class ObjectInternal extends TObject {
abstract predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor);
/** Gets the length of the sequence that this "object" represents.
* Always returns a value for a sequence, will be -1 if object has no fixed length.
* Always returns a value for a sequence, will be -1 if the object has no fixed length.
*/
abstract int length();

View File

@@ -4,19 +4,27 @@ private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
/** Internal type backing `ObjectInternal` and `Value`
* See `ObjectInternal.qll` for an explanation of the API.
*/
newtype TObject =
/* Builtin class objects */
TBuiltinClassObject(Builtin bltn) {
bltn.isClass() and
not bltn = Builtin::unknownType() and
not bltn = Builtin::special("type")
}
or
/* Builtin function objects (module members) */
TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() }
or
/* Builtin method objects (class members) */
TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() }
or
/* Builtin module objects */
TBuiltinModuleObject(Builtin bltn) { bltn.isModule() }
or
/* Other builtin objects from the interpreter */
TBuiltinOpaqueObject(Builtin bltn) {
not bltn.isClass() and not bltn.isFunction() and
not bltn.isMethod() and not bltn.isModule() and
@@ -27,34 +35,45 @@ newtype TObject =
not py_special_objects(bltn, _)
}
or
/* Python function objects (including lambdas) */
TPythonFunctionObject(ControlFlowNode callable) {
callable.getNode() instanceof CallableExpr
}
or
/* Python class objects */
TPythonClassObject(ControlFlowNode classexpr) {
classexpr.getNode() instanceof ClassExpr
}
or
/* Package objects */
TPackageObject(Folder f) {
exists(moduleNameFromFile(f))
}
or
/* Python module objects */
TPythonModule(Module m) {
not m.isPackage() and not exists(SyntaxError se | se.getFile() = m.getFile())
}
or
/* `True` */
TTrue()
or
/* `False` */
TFalse()
or
/* `None` */
TNone()
or
/* Represents any value about which nothing useful is known */
TUnknown()
or
/* Represents any value known to be a class, but not known to be any specific class */
TUnknownClass()
or
/* Represents the absence of a value. Used by points-to for tracking undefined variables */
TUndefined()
or
/* The integer `n` */
TInt(int n) {
// Powers of 2 are used for flags
is_power_2(n) or
@@ -70,10 +89,12 @@ newtype TObject =
n = any(Builtin b).intValue()
}
or
/* The float `f` */
TFloat(float f) {
f = any(FloatLiteral num).getValue()
}
or
/* The unicode string `s` */
TUnicode(string s) {
// Any string explicitly mentioned in the source code.
exists(StrConst str |
@@ -90,6 +111,7 @@ newtype TObject =
s = "__main__"
}
or
/* The byte string `s` */
TBytes(string s) {
// Any string explicitly mentioned in the source code.
exists(StrConst str |
@@ -106,6 +128,7 @@ newtype TObject =
s = "__main__"
}
or
/* 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()
@@ -113,60 +136,74 @@ newtype TObject =
literal_instantiation(instantiation, cls, context)
}
or
/* A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */
TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) {
self_parameter(def, context, cls)
}
or
/* A bound method */
TBoundMethod(ObjectInternal self, CallableObjectInternal function) {
any(ObjectInternal obj).binds(self, _, function) and
function.isDescriptor() = true
}
or
/* Represents any value whose class is known, but nothing else */
TUnknownInstance(BuiltinClassObjectInternal cls) {
cls != ObjectInternal::superType() and
cls != ObjectInternal::builtin("bool") and
cls != ObjectInternal::noneType()
}
or
/* Represents an instance of `super` */
TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) {
super_instantiation(_, self, startclass, _)
}
or
/* Represents an instance of `classmethod` */
TClassMethod(CallNode instantiation, CallableObjectInternal function) {
class_method(instantiation, function, _)
}
or
/* Represents an instance of `staticmethod` */
TStaticMethod(CallNode instantiation, CallableObjectInternal function) {
static_method(instantiation, function, _)
}
or
/* Represents a builtin tuple */
TBuiltinTuple(Builtin bltn) {
bltn.getClass() = Builtin::special("tuple")
}
or
/* Represents a tuple in the Python source */
TPythonTuple(TupleNode origin, PointsToContext context) {
context.appliesTo(origin)
}
or
/* `type` */
TType()
or
/* Represents an instance of `property` */
TProperty(CallNode call, Context ctx, CallableObjectInternal getter) {
PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and
PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _)
}
or
/* Represents a dynamically created class */
TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) {
PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and
not count(instantiation.getAnArg()) = 1 and
Types::getMro(metacls).contains(TType())
}
or
/* Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */
TSysVersionInfo()
or
/* Represents a module that is inferred to perhaps exist, but is not present in the database. */
TAbsentModule(string name) {
missing_imported_module(_, _, name)
}
or
/* Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */
TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) {
(
PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _)