From da8ba638cfdc0bccf85c3eb65789ca6e7f065380 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Tue, 23 Jul 2024 00:28:24 +0200 Subject: [PATCH] Ready to replace --- .../de/fraunhofer/aisec/cpg/ScopeManager.kt | 8 +- .../fraunhofer/aisec/cpg/graph/Interfaces.kt | 4 +- .../statements/expressions/BinaryOperator.kt | 2 +- .../statements/expressions/CallExpression.kt | 2 +- .../expressions/MemberExpression.kt | 2 +- .../statements/expressions/UnaryOperator.kt | 3 +- .../aisec/cpg/passes/SymbolResolver.kt | 94 ++++++++++++------- .../aisec/cpg/frontends/cxx/CPPLanguage.kt | 2 +- .../cpg/frontends/cxx/CXXDeclarationTest.kt | 2 +- 9 files changed, 71 insertions(+), 48 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt index 84a900b4b15..3fe140587aa 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt @@ -659,8 +659,8 @@ class ScopeManager : ScopeProvider { } /** -<<<<<<< HEAD -======= + * <<<<<<< HEAD + * ======= * This function tries to resolve a [CallExpression] into its matching [FunctionDeclaration] (or * multiple functions, if applicable). The result is returned in the form of a * [CallResolutionResult] which holds detail information about intermediate results as well as @@ -758,8 +758,8 @@ class ScopeManager : ScopeProvider { } /** ->>>>>>> 1cde93a3c6 (Preparing to use CallResolutionResult in member call resolution) - * This function extracts a scope for the [Name], e.g. if the name is fully qualified. `null` is + * >>>>>>> 1cde93a3c6 (Preparing to use CallResolutionResult in member call resolution) This + * function extracts a scope for the [Name], e.g. if the name is fully qualified. `null` is * returned, if no scope can be extracted. * * The pair returns the extracted scope and a name that is adjusted by possible import aliases. diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Interfaces.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Interfaces.kt index c15fc297dab..454f637bf1b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Interfaces.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Interfaces.kt @@ -135,7 +135,7 @@ interface HasScope { * Specifies that this node (e.g. a [BinaryOperator] contains an operation that can be overloaded by * an [OperatorDeclaration]. */ -interface HasOverloadedOperation : HasLanguage, HasOperatorCode { +interface HasOverloadedOperation : HasLanguage, HasOperatorCode, HasNameAndLocation { /** * Arguments forwarded to the operator. This might not necessarily be all of the regular @@ -147,5 +147,5 @@ interface HasOverloadedOperation : HasLanguage, HasOperatorCode { * The base expression this operator works on. The [Type] of this is also the source where the * [SymbolResolver] is looking for an overloaded [OperatorDeclaration]. */ - val operatorBase: HasType + val operatorBase: Expression } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt index f37f34be63a..e39312ca99a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt @@ -134,7 +134,7 @@ open class BinaryOperator : get() = listOf(rhs) /** The binary operator operators on the [lhs]. [rhs] is part of the [operatorArguments]. */ - override val operatorBase: HasType + override val operatorBase: Expression get() = lhs override fun equals(other: Any?): Boolean { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt index 717ed8a6fdc..9a212d4fd68 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -286,7 +286,7 @@ open class CallExpression : * is overloaded. In this case we want the [operatorBase] to point to [callee], so we can take * its type to lookup the necessary [OperatorDeclaration]. */ - override val operatorBase: HasType + override val operatorBase: Expression get() = callee override fun equals(other: Any?): Boolean { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt index 0ead65a84b2..3f153ed167c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberExpression.kt @@ -52,7 +52,7 @@ class MemberExpression : Reference(), HasOverloadedOperation, ArgumentHolder, Ha override val operatorArguments: List get() = listOf() - override val operatorBase: HasType + override val operatorBase: Expression get() = base override fun toString(): String { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt index 478393677df..a1132794b4c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt @@ -49,7 +49,8 @@ class UnaryOperator : Expression(), HasOverloadedOperation, ArgumentHolder, HasT get() = listOf() /** The unary operator operates on [input]. */ - override val operatorBase = input + override val operatorBase + get() = input /** The operator code. */ override var operatorCode: String? = null diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt index ad0d13f3395..c914d5d720d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt @@ -343,34 +343,25 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { var member: ValueDeclaration? = null var type = containingClass - // Check for a possible overloaded operator-> (C++ only?!) - /*if ( + // Check for a possible overloaded operator-> + if ( reference.language is HasOperatorOverloading && reference is MemberExpression && reference.operatorCode == "->" && reference.base.type !is PointerType ) { - var op = - resolveCalleeByName("operator->", reference) - .filterIsInstance() - .singleOrNull() - - if (op != null) { - type = op.returnTypes.singleOrNull()?.root ?: unknownType() - - // We need to insert a new operator call expression in between - val ref = - newMemberExpression(op.name, reference.base, operatorCode = ".") - .implicit(op.name.localName, location = reference.location) - ref.refersTo = op - var call = - newOperatorCallExpression(operatorCode = "->", ref).codeAndLocationFrom(ref) - call.invokes = listOf(op) - - // Make the call our new base - reference.base = call - } - }*/ + val result = resolveOperator(reference) + val op = result?.bestViable?.singleOrNull() + if (result?.success == SUCCESSFUL && op is OperatorDeclaration) { + type = op.returnTypes.singleOrNull()?.root ?: unknownType() + + // We need to insert a new operator call expression in between + val call = operatorCallFromDeclaration(op, reference) + + // Make the call our new base + reference.base = call + } + } val record = type.recordDeclaration if (record != null) { @@ -422,6 +413,22 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { } } + private fun operatorCallFromDeclaration( + decl: OperatorDeclaration, + op: HasOverloadedOperation + ): OperatorCallExpression { + val ref = + newMemberExpression(decl.name, op.operatorBase, operatorCode = ".") + .implicit(decl.name.localName, location = op.location) + ref.refersTo = decl + val call = + newOperatorCallExpression(operatorCode = op.operatorCode ?: "", ref) + .codeAndLocationFrom(ref) + call.invokes = listOf(decl) + + return call + } + // TODO(oxisto): Move to inference class protected fun handleUnknownField(base: Type, ref: Reference): FieldDeclaration? { val name = ref.name @@ -477,6 +484,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { is Reference -> handleReference(currClass, node) is ConstructExpression -> handleConstructExpression(node) is CallExpression -> handleCallExpression(node) + is HasOverloadedOperation -> handleOverloadedOperator(node) } } @@ -758,28 +766,42 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { } } - private fun handleOverloadedOperator(operator: HasOverloadedOperation) { - val language = operator.language - val base = operator.operatorBase + private fun handleOverloadedOperator(op: HasOverloadedOperation) { + val result = resolveOperator(op) + val decl = result?.bestViable?.singleOrNull() ?: return + + // If the result was successful, we can replace the node + if (result.success == SUCCESSFUL && decl is OperatorDeclaration) { + val call = operatorCallFromDeclaration(decl, op) + // TOOD: we do not have the parent :( + } + } + + private fun resolveOperator(op: HasOverloadedOperation): CallResolutionResult? { + val language = op.language + val base = op.operatorBase if (language !is HasOperatorOverloading || language.isPrimitive(base.type)) { - return + return null } - val symbol = language.overloadedOperatorNames[Pair(operator::class, operator.operatorCode)] + val symbol = language.overloadedOperatorNames[Pair(op::class, op.operatorCode)] if (symbol == null) { log.warn( - "Could not resolve operator overloading for unknown operatorCode ${operator.operatorCode}" + "Could not resolve operator overloading for unknown operatorCode ${op.operatorCode}" ) - return + return null } - // operator.invokes = - // resolveCalleeByName(symbol, operator).filterIsInstance() - // TODO: replace with call - /*val ops = - resolveCalleeByName(symbol, null, operator as Expression, base as Expression) + val possibleTypes = mutableSetOf() + possibleTypes.add(op.operatorBase.type) + possibleTypes.addAll(op.operatorBase.assignedTypes) + + val candidates = + resolveMemberByName(symbol, possibleTypes) .filterIsInstance() - println(ops)*/ + .toSet() + + return resolveWithArguments(candidates, op.operatorArguments, op as Expression) } /** diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt index b262566d8dd..512b7434b1e 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt @@ -206,7 +206,7 @@ open class CPPLanguage : // There is a sort of weird workaround in C++ to select a prefix vs. postfix operator for // increment and decrement operators. See // https://en.cppreference.com/w/cpp/language/operator_incdec - val expr = result.call + val expr = result.source if (expr is UnaryOperator && (expr.operatorCode == "++" || expr.operatorCode == "--")) { // If it is a postfix, we need to match for a function with a fake "int" parameter if (expr.isPostfix) { diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt index 64a4f7d8c93..afe8b7baa0a 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt @@ -271,7 +271,7 @@ class CXXDeclarationTest { } @Test - fun testMemberAccess() { + fun testMemberAccessOperator() { val file = File("src/test/resources/cxx/operators/member_access.cpp") val result = analyze(listOf(file), file.parentFile.toPath(), true) {