Python: Get rid of some get...Object methods

This frees `Class.qll`, `Exprs.qll`, and `Function.qll` from the
clutches of points-to. For the somewhat complicated setup with
`getLiteralObject` (an abstract method), I opted for a slightly ugly but
workable solution of just defining a predicate on `ImmutableLiteral`
that inlines each predicate body, special-cased to the specific instance
to which it applies.
This commit is contained in:
Taus
2025-10-30 16:44:25 +00:00
parent 78c33ab55a
commit f0465f441f
8 changed files with 72 additions and 51 deletions

View File

@@ -221,3 +221,66 @@ class ModuleWithPointsTo extends Module {
override string getAQlClass() { none() } override string getAQlClass() { none() }
} }
/**
* An extension of `Function` that provides points-to related methods.
*/
class FunctionWithPointsTo extends Function {
/** Gets the FunctionObject corresponding to this function */
FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() }
override string getAQlClass() { none() }
}
/**
* An extension of `Class` that provides points-to related methods.
*/
class ClassWithPointsTo extends Class {
/** Gets the ClassObject corresponding to this class */
ClassObject getClassObject() { result.getOrigin() = this.getParent() }
override string getAQlClass() { none() }
}
Object getLiteralObject(ImmutableLiteral l) {
l instanceof IntegerLiteral and
(
py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, l.(Num).getN())
or
py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, l.(Num).getN())
)
or
l instanceof FloatLiteral and
py_cobjecttypes(result, theFloatType()) and
py_cobjectnames(result, l.(Num).getN())
or
l instanceof ImaginaryLiteral and
py_cobjecttypes(result, theComplexType()) and
py_cobjectnames(result, l.(Num).getN())
or
l instanceof NegativeIntegerLiteral and
(
(py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and
py_cobjectnames(result, "-" + l.(UnaryExpr).getOperand().(IntegerLiteral).getN())
)
or
l instanceof Bytes and
py_cobjecttypes(result, theBytesType()) and
py_cobjectnames(result, l.(Bytes).quotedString())
or
l instanceof Unicode and
py_cobjecttypes(result, theUnicodeType()) and
py_cobjectnames(result, l.(Unicode).quotedString())
or
l instanceof True and
name_consts(l, "True") and
result = theTrueObject()
or
l instanceof False and
name_consts(l, "False") and
result = theFalseObject()
or
l instanceof None and
name_consts(l, "None") and
result = theNoneObject()
}

View File

@@ -141,9 +141,6 @@ class Class extends Class_, Scope, AstNode {
/** Gets the metaclass expression */ /** Gets the metaclass expression */
Expr getMetaClass() { result = this.getParent().getMetaClass() } Expr getMetaClass() { result = this.getParent().getMetaClass() }
/** Gets the ClassObject corresponding to this class */
ClassObject getClassObject() { result.getOrigin() = this.getParent() }
/** Gets the nth base of this class definition. */ /** Gets the nth base of this class definition. */
Expr getBase(int index) { result = this.getParent().getBase(index) } Expr getBase(int index) { result = this.getParent().getBase(index) }

View File

@@ -240,17 +240,12 @@ class Bytes extends StringLiteral {
/* syntax: b"hello" */ /* syntax: b"hello" */
Bytes() { not this.isUnicode() } Bytes() { not this.isUnicode() }
override Object getLiteralObject() {
py_cobjecttypes(result, theBytesType()) and
py_cobjectnames(result, this.quotedString())
}
/** /**
* The extractor puts quotes into the name of each string (to prevent "0" clashing with 0). * The extractor puts quotes into the name of each string (to prevent "0" clashing with 0).
* The following predicate help us match up a string/byte literals in the source * The following predicate help us match up a string/byte literals in the source
* which the equivalent object. * which the equivalent object.
*/ */
private string quotedString() { string quotedString() {
exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'") exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'")
} }
} }
@@ -266,8 +261,6 @@ class Ellipsis extends Ellipsis_ {
* Consists of string (both unicode and byte) literals and numeric literals. * Consists of string (both unicode and byte) literals and numeric literals.
*/ */
abstract class ImmutableLiteral extends Expr { abstract class ImmutableLiteral extends Expr {
abstract Object getLiteralObject();
abstract boolean booleanValue(); abstract boolean booleanValue();
} }
@@ -292,12 +285,6 @@ class IntegerLiteral extends Num {
override string toString() { result = "IntegerLiteral" } override string toString() { result = "IntegerLiteral" }
override Object getLiteralObject() {
py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN())
or
py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN())
}
override boolean booleanValue() { override boolean booleanValue() {
this.getValue() = 0 and result = false this.getValue() = 0 and result = false
or or
@@ -317,10 +304,6 @@ class FloatLiteral extends Num {
override string toString() { result = "FloatLiteral" } override string toString() { result = "FloatLiteral" }
override Object getLiteralObject() {
py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN())
}
override boolean booleanValue() { override boolean booleanValue() {
this.getValue() = 0.0 and result = false this.getValue() = 0.0 and result = false
or or
@@ -343,10 +326,6 @@ class ImaginaryLiteral extends Num {
override string toString() { result = "ImaginaryLiteral" } override string toString() { result = "ImaginaryLiteral" }
override Object getLiteralObject() {
py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN())
}
override boolean booleanValue() { override boolean booleanValue() {
this.getValue() = 0.0 and result = false this.getValue() = 0.0 and result = false
or or
@@ -365,11 +344,6 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr {
override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() } override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() }
override Object getLiteralObject() {
(py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and
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 * Gets the (integer) value of this constant. Will not return a result if the value does not fit into
* a 32 bit signed value * a 32 bit signed value
@@ -385,11 +359,6 @@ class Unicode extends StringLiteral {
/* syntax: "hello" */ /* syntax: "hello" */
Unicode() { this.isUnicode() } Unicode() { this.isUnicode() }
override Object getLiteralObject() {
py_cobjecttypes(result, theUnicodeType()) and
py_cobjectnames(result, this.quotedString())
}
/** /**
* Gets the quoted representation fo this string. * Gets the quoted representation fo this string.
* *
@@ -593,12 +562,10 @@ class StringLiteral extends Str_, ImmutableLiteral {
this.getText() != "" and result = true this.getText() != "" and result = true
} }
override Object getLiteralObject() { none() }
override string toString() { result = "StringLiteral" } override string toString() { result = "StringLiteral" }
} }
private predicate name_consts(Name_ n, string id) { predicate name_consts(Name_ n, string id) {
exists(Variable v | py_variables(v, n) and id = v.getId() | exists(Variable v | py_variables(v, n) and id = v.getId() |
id = "True" or id = "False" or id = "None" id = "True" or id = "False" or id = "None"
) )
@@ -627,8 +594,6 @@ class True extends BooleanLiteral {
/* syntax: True */ /* syntax: True */
True() { name_consts(this, "True") } True() { name_consts(this, "True") }
override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() }
override boolean booleanValue() { result = true } override boolean booleanValue() { result = true }
} }
@@ -637,8 +602,6 @@ class False extends BooleanLiteral {
/* syntax: False */ /* syntax: False */
False() { name_consts(this, "False") } False() { name_consts(this, "False") }
override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() }
override boolean booleanValue() { result = false } override boolean booleanValue() { result = false }
} }
@@ -647,8 +610,6 @@ class None extends NameConstant {
/* syntax: None */ /* syntax: None */
None() { name_consts(this, "None") } None() { name_consts(this, "None") }
override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() }
override boolean booleanValue() { result = false } override boolean booleanValue() { result = false }
} }

View File

@@ -87,9 +87,6 @@ class Function extends Function_, Scope, AstNode {
/** Gets the metrics for this function */ /** Gets the metrics for this function */
FunctionMetrics getMetrics() { result = this } FunctionMetrics getMetrics() { result = this }
/** Gets the FunctionObject corresponding to this function */
FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() }
/** /**
* Whether this function is a procedure, that is, it has no explicit return statement and always returns None. * Whether this function is a procedure, that is, it has no explicit return statement and always returns None.
* Note that generator and async functions are not procedures as they return generators and coroutines respectively. * Note that generator and async functions are not procedures as they return generators and coroutines respectively.

View File

@@ -89,7 +89,7 @@ class ClassObject extends Object {
} }
/** Gets the scope associated with this class, if it is not a builtin class */ /** Gets the scope associated with this class, if it is not a builtin class */
Class getPyClass() { result.getClassObject() = this } ClassWithPointsTo getPyClass() { result.getClassObject() = this }
/** Returns an attribute declared on this class (not on a super-class) */ /** Returns an attribute declared on this class (not on a super-class) */
Object declaredAttribute(string name) { Object declaredAttribute(string name) {

View File

@@ -7,10 +7,11 @@
*/ */
import python import python
private import LegacyPointsTo
from NumericObject n from NumericObject n
where where
exists(IntegerLiteral i | i.getLiteralObject() = n | exists(IntegerLiteral i | getLiteralObject(i) = n |
i.getEnclosingModule().getFile().getShortName() = "test.py" i.getEnclosingModule().getFile().getShortName() = "test.py"
) )
select n.toString(), n.repr() select n.toString(), n.repr()

View File

@@ -1,5 +1,6 @@
/* Test that there are no literals that do not have a corresponding object. */ /* Test that there are no literals that do not have a corresponding object. */
import python import python
private import LegacyPointsTo
string repr(Expr e) { string repr(Expr e) {
result = e.(Num).getN() or result = e.(Num).getN() or
@@ -8,5 +9,5 @@ string repr(Expr e) {
} }
from ImmutableLiteral l from ImmutableLiteral l
where not exists(l.getLiteralObject()) where not exists(getLiteralObject(l))
select l.getLocation().getStartLine(), repr(l) select l.getLocation().getStartLine(), repr(l)

View File

@@ -1,5 +1,6 @@
/* Test that there are no literals that do not have a corresponding object. */ /* Test that there are no literals that do not have a corresponding object. */
import python import python
private import LegacyPointsTo
string repr(Expr e) { string repr(Expr e) {
result = e.(Num).getN() or result = e.(Num).getN() or
@@ -8,5 +9,5 @@ string repr(Expr e) {
} }
from ImmutableLiteral l from ImmutableLiteral l
where not exists(l.getLiteralObject()) where not exists(getLiteralObject(l))
select l.getLocation().getStartLine(), repr(l) select l.getLocation().getStartLine(), repr(l)