Skip to content

Commit f0bb07f

Browse files
committed
Python: Assorted improvements to API.
1 parent 8443f68 commit f0bb07f

File tree

4 files changed

+171
-10
lines changed

4 files changed

+171
-10
lines changed

python/ql/src/semmle/python/Exprs.qll

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import python
22
private import semmle.python.pointsto.PointsTo
3+
private import semmle.python.objects.ObjectInternal
34

45
/** An expression */
56
class Expr extends Expr_, AstNode {
@@ -71,7 +72,8 @@ class Expr extends Expr_, AstNode {
7172
result = this.getASubExpression()
7273
}
7374

74-
/** Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to
75+
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
76+
* Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to
7577
* analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly
7678
* precise, but may not provide information for a significant number of flow-nodes.
7779
* If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead.
@@ -82,13 +84,15 @@ class Expr extends Expr_, AstNode {
8284
this.refersTo(_, obj, cls, origin)
8385
}
8486

85-
/** Gets what this expression might "refer-to" in the given `context`.
87+
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
88+
* Gets what this expression might "refer-to" in the given `context`.
8689
*/
8790
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
8891
this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode())
8992
}
9093

91-
/** Whether this expression might "refer-to" to `value` which is from `origin`
94+
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
95+
* Holds if this expression might "refer-to" to `value` which is from `origin`
9296
* Unlike `this.refersTo(value, _, origin)`, this predicate includes results
9397
* where the class cannot be inferred.
9498
*/
@@ -97,11 +101,31 @@ class Expr extends Expr_, AstNode {
97101
this.getAFlowNode().refersTo(obj, origin.getAFlowNode())
98102
}
99103

100-
/** Equivalent to `this.refersTo(value, _)` */
104+
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
105+
* Equivalent to `this.refersTo(value, _)` */
101106
predicate refersTo(Object obj) {
102107
this.refersTo(obj, _)
103108
}
104109

110+
/** Holds if this expression might "point-to" to `value` which is from `origin`
111+
* in the given `context`.
112+
*/
113+
predicate pointsTo(Context context, Value value, AstNode origin) {
114+
this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode())
115+
}
116+
117+
/** Holds if this expression might "point-to" to `value` which is from `origin`.
118+
*/
119+
predicate pointsTo(Value value, AstNode origin) {
120+
this.getAFlowNode().pointsTo(value, origin.getAFlowNode())
121+
}
122+
123+
/** Holds if this expression might "point-to" to `value`.
124+
*/
125+
predicate pointsTo(Value value) {
126+
this.pointsTo(value, _)
127+
}
128+
105129
}
106130

107131
/** An attribute expression, such as `value.attr` */
@@ -346,6 +370,10 @@ abstract class ImmutableLiteral extends Expr {
346370
abstract Object getLiteralObject();
347371

348372
abstract boolean booleanValue();
373+
final Value getLiteralValue() {
374+
result.(ConstantObjectInternal).getLiteral() = this
375+
}
376+
349377
}
350378

351379
/** A numerical constant expression, such as `7` or `4.2` */
@@ -472,8 +500,10 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr {
472500
py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN())
473501
}
474502

503+
/** Gets the (integer) value of this constant. Will not return a result if the value does not fit into
504+
a 32 bit signed value */
475505
int getValue() {
476-
result = -this.getOperand().(IntegerLiteral).getValue()
506+
result = -(this.getOperand().(IntegerLiteral).getValue())
477507
}
478508

479509
}

python/ql/src/semmle/python/Flow.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ class ControlFlowNode extends @py_flow_node {
222222
this.pointsTo(_, value, _)
223223
}
224224

225+
/** Gets the value that this ControlFlowNode points-to. */
226+
Value pointsTo() {
227+
this.pointsTo(_, result, _)
228+
}
229+
225230
/** Gets a value that this ControlFlowNode may points-to. */
226231
Value inferredValue() {
227232
this.pointsTo(_, result, _)

python/ql/src/semmle/python/objects/Constants.qll

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,15 @@ abstract class ConstantObjectInternal extends ObjectInternal {
7373

7474
override predicate useOriginAsLegacyObject() { none() }
7575

76+
/** Gets an AST literal with the same value as this object */
77+
abstract ImmutableLiteral getLiteral();
78+
7679
}
7780

7881
private abstract class BooleanObjectInternal extends ConstantObjectInternal {
7982

8083
override ObjectInternal getClass() {
81-
result = TBuiltinClassObject(Builtin::special("bool"))
84+
result = ClassValue::bool()
8285
}
8386

8487
override int length() { none() }
@@ -90,6 +93,10 @@ private abstract class BooleanObjectInternal extends ConstantObjectInternal {
9093
/* Booleans aren't iterable */
9194
override ObjectInternal getIterNext() { none() }
9295

96+
override ImmutableLiteral getLiteral() {
97+
result.(BooleanLiteral).booleanValue() = this.booleanValue()
98+
}
99+
93100
}
94101

95102
private class TrueObjectInternal extends BooleanObjectInternal, TTrue {
@@ -175,6 +182,10 @@ private class NoneObjectInternal extends ConstantObjectInternal, TNone {
175182
/* None isn't iterable */
176183
override ObjectInternal getIterNext() { none() }
177184

185+
override ImmutableLiteral getLiteral() {
186+
result instanceof None
187+
}
188+
178189
}
179190

180191

@@ -216,6 +227,12 @@ private class IntObjectInternal extends ConstantObjectInternal, TInt {
216227
/* ints aren't iterable */
217228
override ObjectInternal getIterNext() { none() }
218229

230+
override ImmutableLiteral getLiteral() {
231+
result.(IntegerLiteral).getValue() = this.intValue()
232+
or
233+
result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue()
234+
}
235+
219236
}
220237

221238
private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
@@ -264,6 +281,10 @@ private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
264281
/* floats aren't iterable */
265282
override ObjectInternal getIterNext() { none() }
266283

284+
override ImmutableLiteral getLiteral() {
285+
result.(FloatLiteral).getValue() = this.floatValue()
286+
}
287+
267288
}
268289

269290

@@ -310,6 +331,10 @@ private class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode {
310331
result = TUnknownInstance(this.getClass())
311332
}
312333

334+
override ImmutableLiteral getLiteral() {
335+
result.(Unicode).getText() = this.strValue()
336+
}
337+
313338
}
314339

315340
private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
@@ -355,6 +380,10 @@ private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
355380
result = TUnknownInstance(this.getClass())
356381
}
357382

383+
override ImmutableLiteral getLiteral() {
384+
result.(Bytes).getText() = this.strValue()
385+
}
386+
358387
}
359388

360389

python/ql/src/semmle/python/objects/ObjectAPI.qll

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private import semmle.python.objects.ObjectInternal
1212
private import semmle.python.pointsto.PointsTo
1313
private import semmle.python.pointsto.PointsToContext
1414
private import semmle.python.pointsto.MRO
15+
private import semmle.python.types.Builtins
1516

1617
/* Use the term `ObjectSource` to refer to DB entity. Either a CFG node
1718
* for Python objects, or `@py_cobject` entity for built-in objects.
@@ -85,7 +86,7 @@ class Value extends TObject {
8586
filepath = "" and bl = 0 and bc = 0 and el = 0 and ec = 0
8687
}
8788

88-
/** Gets the name of this value, if it has one.
89+
/** Gets the name of this value, if it has one.
8990
* Note this is the innate name of the
9091
* object, not necessarily all the names by which it can be called.
9192
*/
@@ -228,7 +229,7 @@ class CallableValue extends Value {
228229
cached ControlFlowNode getArgumentForCall(CallNode call, int n) {
229230
exists(ObjectInternal called, int offset |
230231
PointsToInternal::pointsTo(call.getFunction(), _, called, _) and
231-
called.functionAndOffset(this, offset)
232+
called.functionAndOffset(this, offset)
232233
|
233234
call.getArg(n-offset) = result
234235
or
@@ -316,13 +317,13 @@ class ClassValue extends Value {
316317
result = Types::getBase(this, n)
317318
}
318319

319-
/** Holds if this class is a new style class.
320+
/** Holds if this class is a new style class.
320321
A new style class is one that implicitly or explicitly inherits from `object`. */
321322
predicate isNewStyle() {
322323
Types::isNewStyle(this)
323324
}
324325

325-
/** Holds if this class is an old style class.
326+
/** Holds if this class is an old style class.
326327
An old style class is one that does not inherit from `object`. */
327328
predicate isOldStyle() {
328329
Types::isOldStyle(this)
@@ -333,6 +334,20 @@ class ClassValue extends Value {
333334
result = this.(PythonClassObjectInternal).getScope()
334335
}
335336

337+
/** Holds if this class has the attribute `name`, including
338+
* attributes declared by super classes.
339+
*/
340+
predicate hasAttribute(string name) {
341+
this.getMro().declares(name)
342+
}
343+
344+
/** Holds if this class declares the attribute `name`,
345+
* *not* including attributes declared by super classes.
346+
*/
347+
predicate declaresAttribute(string name) {
348+
this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name)
349+
}
350+
336351
}
337352

338353
/** A method-resolution-order sequence of classes */
@@ -347,5 +362,87 @@ class MRO extends TClassList {
347362
result = this.(ClassList).getItem(n)
348363
}
349364

365+
/** Holds if any class in this MRO declares the attribute `name` */
366+
predicate declares(string name) {
367+
this.(ClassList).declares(name)
368+
}
369+
370+
/** Gets the length of this MRO */
371+
int length() {
372+
result = this.(ClassList).length()
373+
}
374+
375+
/** Holds if this MRO contains `cls` */
376+
predicate contains(ClassValue cls) {
377+
this.(ClassList).contains(cls)
378+
}
379+
380+
/** Gets the value from scanning for the attribute `name` in this MRO. */
381+
Value lookup(string name) {
382+
this.(ClassList).lookup(name, result, _)
383+
}
384+
385+
/** Gets the MRO formed by removing all classes before `cls`
386+
* from this MRO.
387+
*/
388+
MRO startingAt(ClassValue cls) {
389+
result = this.(ClassList).startingAt(cls)
390+
}
391+
350392
}
351393

394+
395+
module ClassValue {
396+
397+
/** Get the `ClassValue` for the `bool` class. */
398+
ClassValue bool() {
399+
result = TBuiltinClassObject(Builtin::special("bool"))
400+
}
401+
402+
/** Get the `ClassValue` for the class of Python functions. */
403+
ClassValue function() {
404+
result = TBuiltinClassObject(Builtin::special("FunctionType"))
405+
}
406+
407+
/** Get the `ClassValue` for the class of builtin functions. */
408+
ClassValue builtinFunction() {
409+
result = Value::named("len").getClass()
410+
}
411+
412+
/** Get the `ClassValue` for the `int` class. */
413+
ClassValue int_() {
414+
result = TBuiltinClassObject(Builtin::special("int"))
415+
}
416+
417+
/** Get the `ClassValue` for the `float` class. */
418+
ClassValue float_() {
419+
result = TBuiltinClassObject(Builtin::builtin("float"))
420+
}
421+
422+
/** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */
423+
ClassValue bytes() {
424+
result = TBuiltinClassObject(Builtin::special("bytes"))
425+
}
426+
427+
/** Get the `ClassValue` for the class of unicode strings.
428+
* `str` in Python 3 and `unicode` in Python 2. */
429+
ClassValue unicode() {
430+
result = TBuiltinClassObject(Builtin::special("unicode"))
431+
}
432+
433+
/** Get the `ClassValue` for the `classmethod` class. */
434+
ClassValue classmethod() {
435+
result = TBuiltinClassObject(Builtin::special("ClassMethod"))
436+
}
437+
438+
/** Get the `ClassValue` for the `staticmethod` class. */
439+
ClassValue staticmethod() {
440+
result = TBuiltinClassObject(Builtin::special("StaticMethod"))
441+
}
442+
443+
/** Get the `ClassValue` for the class of modules. */
444+
ClassValue module_() {
445+
result = TBuiltinClassObject(Builtin::special("ModuleType"))
446+
}
447+
448+
}

0 commit comments

Comments
 (0)