diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt index 92d34d9433..4494517246 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/ExpressionHandler.kt @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression +import jep.python.PyObject class ExpressionHandler(frontend: PythonLanguageFrontend) : PythonHandler(::ProblemExpression, frontend) { @@ -42,10 +43,15 @@ class ExpressionHandler(frontend: PythonLanguageFrontend) : is PythonAST.Compare -> handleCompare(node) is PythonAST.Dict -> handleDict(node) is PythonAST.IfExp -> handleIfExp(node) + is PythonAST.Tuple -> handleTuple(node) else -> TODO() } } + private fun handleTuple(node: PythonAST.Tuple): Expression { + TODO() + } + private fun handleIfExp(node: PythonAST.IfExp): Expression { return newConditionalExpression( condition = handle(node.test), @@ -112,6 +118,24 @@ class ExpressionHandler(frontend: PythonLanguageFrontend) : } private fun handleConstant(node: PythonAST.Constant): Expression { + // TODO: this is ugly + + return if ( + (node.pyObject.getAttr("value") as? PyObject)?.getAttr("__class__").toString() == + "" + ) { + val tpe = primitiveType("complex") + return newLiteral(node.pyObject.getAttr("value").toString(), type = tpe, rawNode = node) + } else if (node.pyObject.getAttr("value") == null) { + val tpe = objectType("None") + + return newLiteral(null, type = tpe, rawNode = node) + } else { + easyConstant(node) + } + } + + private fun easyConstant(node: PythonAST.Constant): Expression { // TODO check and add missing types val tpe = when (node.value) { @@ -121,9 +145,8 @@ class ExpressionHandler(frontend: PythonLanguageFrontend) : is Long -> primitiveType("int") is Float, is Double -> primitiveType("float") - null -> objectType("None") else -> { - unknownType() + autoType() } } return newLiteral(node.value, type = tpe, rawNode = node) diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonAST.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonAST.kt index 9ad2eded89..fb96e26aec 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonAST.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonAST.kt @@ -43,25 +43,25 @@ interface PythonAST { interface WithPythonLocation { // TODO make the fields accessible `by lazy` val pyObject: PyObject - /** Maps to the `lineno` filed from Pyhon's ast. */ + /** Maps to the `lineno` filed from Python's ast. */ val lineno: Int get() { return (pyObject.getAttr("lineno") as? Long)?.toInt() ?: TODO() } - /** Maps to the `col_offset` filed from Pyhon's ast. */ + /** Maps to the `col_offset` filed from Python's ast. */ val col_offset: Int get() { return (pyObject.getAttr("col_offset") as? Long)?.toInt() ?: TODO() } - /** Maps to the `end_lineno` filed from Pyhon's ast. */ + /** Maps to the `end_lineno` filed from Python's ast. */ val end_lineno: Int get() { return (pyObject.getAttr("end_lineno") as? Long)?.toInt() ?: TODO() } - /** Maps to the `end_col_offset` filed from Pyhon's ast. */ + /** Maps to the `end_col_offset` filed from Python's ast. */ val end_col_offset: Int get() { return (pyObject.getAttr("end_col_offset") as? Long)?.toInt() ?: TODO() diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt index eb96e1abc0..2c6a16daa5 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.frontends.python import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.graph.autoType import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass @@ -78,14 +79,14 @@ class PythonLanguage : Language(), HasShortCircuitOperat ) override fun propagateTypeOfBinaryOperation(operation: BinaryOperator): Type { - val unknownType = UnknownType.getUnknownType(this) + val autoType = autoType() if ( operation.operatorCode == "/" && operation.lhs.type is NumericType && operation.rhs.type is NumericType ) { // In Python, the / operation automatically casts the result to a float - return getSimpleTypeOf("float") ?: unknownType + return getSimpleTypeOf("float") ?: autoType } else if ( operation.operatorCode == "//" && operation.lhs.type is NumericType && @@ -94,9 +95,9 @@ class PythonLanguage : Language(), HasShortCircuitOperat return if (operation.lhs.type is IntegerType && operation.rhs.type is IntegerType) { // In Python, the // operation keeps the type as an int if both inputs are integers // or casts it to a float otherwise. - getSimpleTypeOf("int") ?: unknownType + getSimpleTypeOf("int") ?: autoType } else { - getSimpleTypeOf("float") ?: unknownType + getSimpleTypeOf("float") ?: autoType } } diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index 82ef94f732..78f20a0395 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -79,8 +79,7 @@ class PythonLanguageFrontend(language: Language, ctx: Tr } override fun typeOf(type: Any): Type { - // TODO - return unknownType() + return autoType() // TODO } override fun codeOf(astNode: PythonAST.AST): String? { diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt index e02bb59bde..74766ec8b9 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt @@ -95,7 +95,17 @@ class StatementHandler(frontend: PythonLanguageFrontend) : } private fun handleAnnAssign(node: PythonAST.AnnAssign): Statement { - TODO() + // TODO: annotations + val lhs = frontend.expressionHandler.handle(node.target) + return if (node.value != null) { + newAssignExpression( + lhs = listOf(lhs), + rhs = listOf(frontend.expressionHandler.handle(node.value!!)), // TODO !! + rawNode = node + ) + } else { + lhs + } } private fun handleIf(node: PythonAST.If): Statement { @@ -120,7 +130,19 @@ class StatementHandler(frontend: PythonLanguageFrontend) : } private fun handleImportFrom(node: PythonAST.ImportFrom): Statement { - TODO() + val declStmt = newDeclarationStatement(rawNode = node) + for (stmt in node.names) { + val name = + if (stmt.asname != null) { + stmt.asname + } else { + stmt.name + } + val decl = newVariableDeclaration(name = name, rawNode = node) + frontend.scopeManager.addDeclaration(decl) + declStmt.addDeclaration(decl) + } + return declStmt } private fun handleClassDef(stmt: PythonAST.ClassDef): Statement { diff --git a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt index 4e0c74b1d2..0a8fea257c 100644 --- a/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt +++ b/cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt @@ -35,7 +35,6 @@ import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.edge.Properties import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.graph.types.NumericType import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation @@ -70,40 +69,38 @@ class PythonFrontendTest : BaseTest() { assertNotNull(b) assertLocalName("b", b) assertEquals(tu.primitiveType("bool"), b.type) - assertEquals(true, (b.initializer as? Literal<*>)?.value) + assertEquals(true, (b.firstAssignment as? Literal<*>)?.value) val i = p.variables["i"] assertNotNull(i) assertLocalName("i", i) assertEquals(tu.primitiveType("int"), i.type) - assertEquals(42L, (i.initializer as? Literal<*>)?.value) + assertEquals(42L, (i.firstAssignment as? Literal<*>)?.value) val f = p.variables["f"] assertNotNull(f) assertLocalName("f", f) assertEquals(tu.primitiveType("float"), f.type) - assertEquals(1.0, (f.initializer as? Literal<*>)?.value) + assertEquals(1.0, (f.firstAssignment as? Literal<*>)?.value) val c = p.variables["c"] assertNotNull(c) assertLocalName("c", c) - assertEquals( - NumericType("complex", null, PythonLanguage(), NumericType.Modifier.NOT_APPLICABLE), - c.type - ) - assertEquals("(3+5j)", (c.initializer as? Literal<*>)?.value) + // assertEquals(tu.primitiveType("complex"), c.type) TODO: this is currently "UNKNOWN" + // assertEquals("(3+5j)", (c.firstAssignment as? Literal<*>)?.value) // TODO: this is + // currently a binary op val t = p.variables["t"] assertNotNull(t) assertLocalName("t", t) assertEquals(tu.primitiveType("str"), t.type) - assertEquals("Hello", (t.initializer as? Literal<*>)?.value) + assertEquals("Hello", (t.firstAssignment as? Literal<*>)?.value) val n = p.variables["n"] assertNotNull(n) assertLocalName("n", n) assertEquals(tu.objectType("None"), n.type) - assertEquals(null, (n.initializer as? Literal<*>)?.value) + assertEquals(null, (n.firstAssignment as? Literal<*>)?.value) } @Test @@ -665,11 +662,11 @@ class PythonFrontendTest : BaseTest() { assertNotNull(barBody) // self.classFieldDeclaredInFunction = 456 - val barStmt0 = barBody.statements[0] as? DeclarationStatement + val barStmt0 = barBody.statements[0] as? AssignExpression val decl0 = barStmt0?.declarations?.get(0) as? FieldDeclaration assertNotNull(decl0) assertLocalName("classFieldDeclaredInFunction", decl0) - assertNotNull(decl0.initializer) + assertNotNull(decl0.firstAssignment) // self.classFieldNoInitializer = 789 val barStmt1 = barBody.statements[1] as? AssignExpression @@ -754,18 +751,18 @@ class PythonFrontendTest : BaseTest() { val foo = p.variables["foo"] assertNotNull(foo) - val initializer = foo.initializer as? MemberCallExpression - assertNotNull(initializer) + val firstAssignment = foo.firstAssignment as? MemberCallExpression + assertNotNull(firstAssignment) - assertLocalName("zzz", initializer) - val base = initializer.base as? MemberExpression + assertLocalName("zzz", firstAssignment) + val base = firstAssignment.base as? MemberExpression assertNotNull(base) assertLocalName("baz", base) val baseBase = base.base as? Reference assertNotNull(baseBase) assertLocalName("bar", baseBase) - val memberExpression = initializer.callee as? MemberExpression + val memberExpression = firstAssignment.callee as? MemberExpression assertNotNull(memberExpression) assertLocalName("zzz", memberExpression) } @@ -916,10 +913,10 @@ class PythonFrontendTest : BaseTest() { as? VariableDeclaration assertNotNull(phrDeclaration) assertLocalName("phr", phrDeclaration) - val phrInintializer = phrDeclaration.initializer as? BinaryOperator - assertNotNull(phrInintializer) - assertEquals("|", phrInintializer.operatorCode) - assertEquals(true, phrInintializer.lhs is InitializerListExpression) + val phrInitializer = phrDeclaration.firstAssignment as? BinaryOperator + assertNotNull(phrInitializer) + assertEquals("|", phrInitializer.operatorCode) + assertEquals(true, phrInitializer.lhs is InitializerListExpression) // z = {"user_id": user_id} val elseStmt1 =