From f0465f441f3f782656a6efd71e3092fc7b95f711 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 30 Oct 2025 16:44:25 +0000 Subject: [PATCH] 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. --- python/ql/lib/LegacyPointsTo.qll | 63 +++++++++++++++++++ python/ql/lib/semmle/python/Class.qll | 3 - python/ql/lib/semmle/python/Exprs.qll | 43 +------------ python/ql/lib/semmle/python/Function.qll | 3 - .../lib/semmle/python/types/ClassObject.qll | 2 +- .../2/extractor-tests/normalise/Numbers.ql | 3 +- .../test/2/library-tests/objects/Literals.ql | 3 +- .../ql/test/library-tests/objects/Literals.ql | 3 +- 8 files changed, 72 insertions(+), 51 deletions(-) diff --git a/python/ql/lib/LegacyPointsTo.qll b/python/ql/lib/LegacyPointsTo.qll index bc2a6b7804a..cfa73bf47c4 100644 --- a/python/ql/lib/LegacyPointsTo.qll +++ b/python/ql/lib/LegacyPointsTo.qll @@ -221,3 +221,66 @@ class ModuleWithPointsTo extends Module { 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() +} diff --git a/python/ql/lib/semmle/python/Class.qll b/python/ql/lib/semmle/python/Class.qll index 58a6504b547..dd7da9ee041 100644 --- a/python/ql/lib/semmle/python/Class.qll +++ b/python/ql/lib/semmle/python/Class.qll @@ -141,9 +141,6 @@ class Class extends Class_, Scope, AstNode { /** Gets the metaclass expression */ 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. */ Expr getBase(int index) { result = this.getParent().getBase(index) } diff --git a/python/ql/lib/semmle/python/Exprs.qll b/python/ql/lib/semmle/python/Exprs.qll index e5ee2bdb28c..203e63f611f 100644 --- a/python/ql/lib/semmle/python/Exprs.qll +++ b/python/ql/lib/semmle/python/Exprs.qll @@ -240,17 +240,12 @@ class Bytes extends StringLiteral { /* syntax: b"hello" */ 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 following predicate help us match up a string/byte literals in the source * which the equivalent object. */ - private string quotedString() { + string quotedString() { 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. */ abstract class ImmutableLiteral extends Expr { - abstract Object getLiteralObject(); - abstract boolean booleanValue(); } @@ -292,12 +285,6 @@ class IntegerLiteral extends Num { 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() { this.getValue() = 0 and result = false or @@ -317,10 +304,6 @@ class FloatLiteral extends Num { override string toString() { result = "FloatLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN()) - } - override boolean booleanValue() { this.getValue() = 0.0 and result = false or @@ -343,10 +326,6 @@ class ImaginaryLiteral extends Num { override string toString() { result = "ImaginaryLiteral" } - override Object getLiteralObject() { - py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN()) - } - override boolean booleanValue() { this.getValue() = 0.0 and result = false or @@ -365,11 +344,6 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr { 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 * a 32 bit signed value @@ -385,11 +359,6 @@ class Unicode extends StringLiteral { /* syntax: "hello" */ Unicode() { this.isUnicode() } - override Object getLiteralObject() { - py_cobjecttypes(result, theUnicodeType()) and - py_cobjectnames(result, this.quotedString()) - } - /** * Gets the quoted representation fo this string. * @@ -593,12 +562,10 @@ class StringLiteral extends Str_, ImmutableLiteral { this.getText() != "" and result = true } - override Object getLiteralObject() { none() } - 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() | id = "True" or id = "False" or id = "None" ) @@ -627,8 +594,6 @@ class True extends BooleanLiteral { /* syntax: True */ True() { name_consts(this, "True") } - override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() } - override boolean booleanValue() { result = true } } @@ -637,8 +602,6 @@ class False extends BooleanLiteral { /* syntax: False */ False() { name_consts(this, "False") } - override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() } - override boolean booleanValue() { result = false } } @@ -647,8 +610,6 @@ class None extends NameConstant { /* syntax: None */ None() { name_consts(this, "None") } - override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() } - override boolean booleanValue() { result = false } } diff --git a/python/ql/lib/semmle/python/Function.qll b/python/ql/lib/semmle/python/Function.qll index 589d7f47161..a5d122c3de7 100644 --- a/python/ql/lib/semmle/python/Function.qll +++ b/python/ql/lib/semmle/python/Function.qll @@ -87,9 +87,6 @@ class Function extends Function_, Scope, AstNode { /** Gets the metrics for this function */ 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. * Note that generator and async functions are not procedures as they return generators and coroutines respectively. diff --git a/python/ql/lib/semmle/python/types/ClassObject.qll b/python/ql/lib/semmle/python/types/ClassObject.qll index fbea3acdb5b..8607141f16f 100644 --- a/python/ql/lib/semmle/python/types/ClassObject.qll +++ b/python/ql/lib/semmle/python/types/ClassObject.qll @@ -89,7 +89,7 @@ class ClassObject extends Object { } /** 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) */ Object declaredAttribute(string name) { diff --git a/python/ql/test/2/extractor-tests/normalise/Numbers.ql b/python/ql/test/2/extractor-tests/normalise/Numbers.ql index 2a770600279..9447141ab6a 100644 --- a/python/ql/test/2/extractor-tests/normalise/Numbers.ql +++ b/python/ql/test/2/extractor-tests/normalise/Numbers.ql @@ -7,10 +7,11 @@ */ import python +private import LegacyPointsTo from NumericObject n where - exists(IntegerLiteral i | i.getLiteralObject() = n | + exists(IntegerLiteral i | getLiteralObject(i) = n | i.getEnclosingModule().getFile().getShortName() = "test.py" ) select n.toString(), n.repr() diff --git a/python/ql/test/2/library-tests/objects/Literals.ql b/python/ql/test/2/library-tests/objects/Literals.ql index a7f10b358ff..d5aee013f25 100644 --- a/python/ql/test/2/library-tests/objects/Literals.ql +++ b/python/ql/test/2/library-tests/objects/Literals.ql @@ -1,5 +1,6 @@ /* Test that there are no literals that do not have a corresponding object. */ import python +private import LegacyPointsTo string repr(Expr e) { result = e.(Num).getN() or @@ -8,5 +9,5 @@ string repr(Expr e) { } from ImmutableLiteral l -where not exists(l.getLiteralObject()) +where not exists(getLiteralObject(l)) select l.getLocation().getStartLine(), repr(l) diff --git a/python/ql/test/library-tests/objects/Literals.ql b/python/ql/test/library-tests/objects/Literals.ql index a7f10b358ff..d5aee013f25 100644 --- a/python/ql/test/library-tests/objects/Literals.ql +++ b/python/ql/test/library-tests/objects/Literals.ql @@ -1,5 +1,6 @@ /* Test that there are no literals that do not have a corresponding object. */ import python +private import LegacyPointsTo string repr(Expr e) { result = e.(Num).getN() or @@ -8,5 +9,5 @@ string repr(Expr e) { } from ImmutableLiteral l -where not exists(l.getLiteralObject()) +where not exists(getLiteralObject(l)) select l.getLocation().getStartLine(), repr(l)