Python: Assorted improvements to API.

This commit is contained in:
Mark Shannon
2019-07-25 17:05:02 +01:00
parent 8443f68a33
commit f0bb07fc24
4 changed files with 171 additions and 10 deletions

View File

@@ -1,5 +1,6 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.ObjectInternal
/** An expression */
class Expr extends Expr_, AstNode {
@@ -71,7 +72,8 @@ class Expr extends Expr_, AstNode {
result = this.getASubExpression()
}
/** Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
* Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to
* analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly
* precise, but may not provide information for a significant number of flow-nodes.
* If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead.
@@ -82,13 +84,15 @@ class Expr extends Expr_, AstNode {
this.refersTo(_, obj, cls, origin)
}
/** Gets what this expression might "refer-to" in the given `context`.
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
* Gets what this expression might "refer-to" in the given `context`.
*/
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode())
}
/** Whether this expression might "refer-to" to `value` which is from `origin`
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
* Holds if this expression might "refer-to" to `value` which is from `origin`
* Unlike `this.refersTo(value, _, origin)`, this predicate includes results
* where the class cannot be inferred.
*/
@@ -97,11 +101,31 @@ class Expr extends Expr_, AstNode {
this.getAFlowNode().refersTo(obj, origin.getAFlowNode())
}
/** Equivalent to `this.refersTo(value, _)` */
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
* Equivalent to `this.refersTo(value, _)` */
predicate refersTo(Object obj) {
this.refersTo(obj, _)
}
/** Holds if this expression might "point-to" to `value` which is from `origin`
* in the given `context`.
*/
predicate pointsTo(Context context, Value value, AstNode origin) {
this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode())
}
/** Holds if this expression might "point-to" to `value` which is from `origin`.
*/
predicate pointsTo(Value value, AstNode origin) {
this.getAFlowNode().pointsTo(value, origin.getAFlowNode())
}
/** Holds if this expression might "point-to" to `value`.
*/
predicate pointsTo(Value value) {
this.pointsTo(value, _)
}
}
/** An attribute expression, such as `value.attr` */
@@ -346,6 +370,10 @@ abstract class ImmutableLiteral extends Expr {
abstract Object getLiteralObject();
abstract boolean booleanValue();
final Value getLiteralValue() {
result.(ConstantObjectInternal).getLiteral() = this
}
}
/** A numerical constant expression, such as `7` or `4.2` */
@@ -472,8 +500,10 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr {
py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN())
}
/** Gets the (integer) value of this constant. Will not return a result if the value does not fit into
a 32 bit signed value */
int getValue() {
result = -this.getOperand().(IntegerLiteral).getValue()
result = -(this.getOperand().(IntegerLiteral).getValue())
}
}

View File

@@ -222,6 +222,11 @@ class ControlFlowNode extends @py_flow_node {
this.pointsTo(_, value, _)
}
/** Gets the value that this ControlFlowNode points-to. */
Value pointsTo() {
this.pointsTo(_, result, _)
}
/** Gets a value that this ControlFlowNode may points-to. */
Value inferredValue() {
this.pointsTo(_, result, _)

View File

@@ -73,12 +73,15 @@ abstract class ConstantObjectInternal extends ObjectInternal {
override predicate useOriginAsLegacyObject() { none() }
/** Gets an AST literal with the same value as this object */
abstract ImmutableLiteral getLiteral();
}
private abstract class BooleanObjectInternal extends ConstantObjectInternal {
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("bool"))
result = ClassValue::bool()
}
override int length() { none() }
@@ -90,6 +93,10 @@ private abstract class BooleanObjectInternal extends ConstantObjectInternal {
/* Booleans aren't iterable */
override ObjectInternal getIterNext() { none() }
override ImmutableLiteral getLiteral() {
result.(BooleanLiteral).booleanValue() = this.booleanValue()
}
}
private class TrueObjectInternal extends BooleanObjectInternal, TTrue {
@@ -175,6 +182,10 @@ private class NoneObjectInternal extends ConstantObjectInternal, TNone {
/* None isn't iterable */
override ObjectInternal getIterNext() { none() }
override ImmutableLiteral getLiteral() {
result instanceof None
}
}
@@ -216,6 +227,12 @@ private class IntObjectInternal extends ConstantObjectInternal, TInt {
/* ints aren't iterable */
override ObjectInternal getIterNext() { none() }
override ImmutableLiteral getLiteral() {
result.(IntegerLiteral).getValue() = this.intValue()
or
result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue()
}
}
private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
@@ -264,6 +281,10 @@ private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
/* floats aren't iterable */
override ObjectInternal getIterNext() { none() }
override ImmutableLiteral getLiteral() {
result.(FloatLiteral).getValue() = this.floatValue()
}
}
@@ -310,6 +331,10 @@ private class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode {
result = TUnknownInstance(this.getClass())
}
override ImmutableLiteral getLiteral() {
result.(Unicode).getText() = this.strValue()
}
}
private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
@@ -355,6 +380,10 @@ private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
result = TUnknownInstance(this.getClass())
}
override ImmutableLiteral getLiteral() {
result.(Bytes).getText() = this.strValue()
}
}

View File

@@ -12,6 +12,7 @@ private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO
private import semmle.python.types.Builtins
/* Use the term `ObjectSource` to refer to DB entity. Either a CFG node
* for Python objects, or `@py_cobject` entity for built-in objects.
@@ -85,7 +86,7 @@ class Value extends TObject {
filepath = "" and bl = 0 and bc = 0 and el = 0 and ec = 0
}
/** Gets the name of this value, if it has one.
/** Gets the name of this value, if it has one.
* Note this is the innate name of the
* object, not necessarily all the names by which it can be called.
*/
@@ -228,7 +229,7 @@ class CallableValue extends Value {
cached ControlFlowNode getArgumentForCall(CallNode call, int n) {
exists(ObjectInternal called, int offset |
PointsToInternal::pointsTo(call.getFunction(), _, called, _) and
called.functionAndOffset(this, offset)
called.functionAndOffset(this, offset)
|
call.getArg(n-offset) = result
or
@@ -316,13 +317,13 @@ class ClassValue extends Value {
result = Types::getBase(this, n)
}
/** Holds if this class is a new style class.
/** Holds if this class is a new style class.
A new style class is one that implicitly or explicitly inherits from `object`. */
predicate isNewStyle() {
Types::isNewStyle(this)
}
/** Holds if this class is an old style class.
/** Holds if this class is an old style class.
An old style class is one that does not inherit from `object`. */
predicate isOldStyle() {
Types::isOldStyle(this)
@@ -333,6 +334,20 @@ class ClassValue extends Value {
result = this.(PythonClassObjectInternal).getScope()
}
/** Holds if this class has the attribute `name`, including
* attributes declared by super classes.
*/
predicate hasAttribute(string name) {
this.getMro().declares(name)
}
/** Holds if this class declares the attribute `name`,
* *not* including attributes declared by super classes.
*/
predicate declaresAttribute(string name) {
this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name)
}
}
/** A method-resolution-order sequence of classes */
@@ -347,5 +362,87 @@ class MRO extends TClassList {
result = this.(ClassList).getItem(n)
}
/** Holds if any class in this MRO declares the attribute `name` */
predicate declares(string name) {
this.(ClassList).declares(name)
}
/** Gets the length of this MRO */
int length() {
result = this.(ClassList).length()
}
/** Holds if this MRO contains `cls` */
predicate contains(ClassValue cls) {
this.(ClassList).contains(cls)
}
/** Gets the value from scanning for the attribute `name` in this MRO. */
Value lookup(string name) {
this.(ClassList).lookup(name, result, _)
}
/** Gets the MRO formed by removing all classes before `cls`
* from this MRO.
*/
MRO startingAt(ClassValue cls) {
result = this.(ClassList).startingAt(cls)
}
}
module ClassValue {
/** Get the `ClassValue` for the `bool` class. */
ClassValue bool() {
result = TBuiltinClassObject(Builtin::special("bool"))
}
/** Get the `ClassValue` for the class of Python functions. */
ClassValue function() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
}
/** Get the `ClassValue` for the class of builtin functions. */
ClassValue builtinFunction() {
result = Value::named("len").getClass()
}
/** Get the `ClassValue` for the `int` class. */
ClassValue int_() {
result = TBuiltinClassObject(Builtin::special("int"))
}
/** Get the `ClassValue` for the `float` class. */
ClassValue float_() {
result = TBuiltinClassObject(Builtin::builtin("float"))
}
/** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */
ClassValue bytes() {
result = TBuiltinClassObject(Builtin::special("bytes"))
}
/** Get the `ClassValue` for the class of unicode strings.
* `str` in Python 3 and `unicode` in Python 2. */
ClassValue unicode() {
result = TBuiltinClassObject(Builtin::special("unicode"))
}
/** Get the `ClassValue` for the `classmethod` class. */
ClassValue classmethod() {
result = TBuiltinClassObject(Builtin::special("ClassMethod"))
}
/** Get the `ClassValue` for the `staticmethod` class. */
ClassValue staticmethod() {
result = TBuiltinClassObject(Builtin::special("StaticMethod"))
}
/** Get the `ClassValue` for the class of modules. */
ClassValue module_() {
result = TBuiltinClassObject(Builtin::special("ModuleType"))
}
}