Skip to content

Commit

Permalink
Preparing to use CallResolutionResult in member call resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Jul 28, 2024
1 parent 1428326 commit 750138e
Show file tree
Hide file tree
Showing 20 changed files with 362 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType
import de.fraunhofer.aisec.cpg.graph.types.IncompleteType
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.helpers.Util
import de.fraunhofer.aisec.cpg.passes.SymbolResolver
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation
import java.util.*
import java.util.function.Predicate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,30 @@ fun MetadataProvider.newCallExpression(
return node
}

/**
* Creates a new [MemberCallExpression]. The [MetadataProvider] receiver will be used to fill
* different meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin
* requires an appropriate [MetadataProvider], such as a [LanguageFrontend] as an additional
* prepended argument.
*/
@JvmOverloads
fun MetadataProvider.newOperatorCallExpression(
operatorCode: String,
callee: Expression?,
rawNode: Any? = null
): OperatorCallExpression {
val node = OperatorCallExpression()
node.applyMetadata(this, operatorCode, rawNode)

node.operatorCode = operatorCode
if (callee != null) {
node.callee = callee
}

log(node)
return node
}

/**
* Creates a new [MemberCallExpression]. The [MetadataProvider] receiver will be used to fill
* different meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,10 @@ val Node?.nodes: List<Node>
val Node?.calls: List<CallExpression>
get() = this.allChildren()

/** Returns all [OperatorCallExpression] children in this graph, starting with this [Node]. */
val Node?.operatorCalls: List<OperatorCallExpression>
get() = this.allChildren()

/** Returns all [MemberCallExpression] children in this graph, starting with this [Node]. */
val Node?.mcalls: List<MemberCallExpression>
get() = this.allChildren()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,35 +34,6 @@ import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.passes.SymbolResolver
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation

/** A simple interface that a node has [language]. */
interface HasLanguage {

var language: Language<*>?
}

/** A simple interface that a node has [name] and [location]. */
interface HasNameAndLocation : HasLanguage {

val name: Name

/** Location of the finding in source code. */
val location: PhysicalLocation?
}

/** A simple interface that a node has [scope]. */
interface HasScope : HasNameAndLocation {

/** The scope this node lives in. */
val scope: Scope?
}

/** A simple interface to denote that the implementing class has some kind of [operatorCode]. */
interface HasOperatorCode : HasScope {

/** The operator code, identifying an operation executed on one or more [Expression]s */
val operatorCode: String?
}

/** Specifies that a certain node has a base on which it executes an operation. */
interface HasBase : HasOperatorCode {

Expand All @@ -77,20 +48,27 @@ interface HasBase : HasOperatorCode {
override val operatorCode: String?
}

/** A simple interface to denote that the implementing class has some kind of [operatorCode]. */
interface HasOperatorCode {

/** The operator code, identifying an operation executed on one or more [Expression]s */
val operatorCode: String?
}

/**
* Interface that allows us to mark nodes that contain a default value
*
* @param <T> type of the default node </T>
*/
interface HasDefault<T : Node?> : HasScope {
interface HasDefault<T : Node?> {
var default: T
}

/**
* Specifies that a certain node has an initializer. It is a special case of [ArgumentHolder], in
* which the initializer is treated as the first (and only) argument.
*/
interface HasInitializer : HasScope, HasType, ArgumentHolder, AssignmentHolder {
interface HasInitializer : HasType, ArgumentHolder, AssignmentHolder {

var initializer: Expression?

Expand Down Expand Up @@ -126,16 +104,38 @@ interface HasInitializer : HasScope, HasType, ArgumentHolder, AssignmentHolder {
* Some nodes have aliases, i.e., it potentially references another variable. This means that
* writing to this node, also writes to its [aliases] and vice-versa.
*/
interface HasAliases : HasScope {
interface HasAliases {
/** The aliases which this node has. */
var aliases: MutableSet<HasAliases>
}

/** A simple interface that a node has [language]. */
interface HasLanguage {

var language: Language<*>?
}

/** A simple interface that a node has [name] and [location]. */
interface HasNameAndLocation {

val name: Name

/** Location of the finding in source code. */
val location: PhysicalLocation?
}

/** A simple interface that a node has [scope]. */
interface HasScope {

/** The scope this node lives in. */
val scope: Scope?
}

/**
* Specifies that this node (e.g. a [BinaryOperator] contains an operation that can be overloaded by
* an [OperatorDeclaration].
*/
interface HasOverloadedOperation : HasOperatorCode {
interface HasOverloadedOperation : HasLanguage, HasOperatorCode, HasNameAndLocation {

/**
* Arguments forwarded to the operator. This might not necessarily be all of the regular
Expand All @@ -147,5 +147,5 @@ interface HasOverloadedOperation : 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
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ interface StatementHolder : Holder<Statement> {
statementEdges.add(propertyEdge)
}

fun replaceStatement(old: Statement, new: Statement): Boolean {
var found = statementEdges.firstOrNull { it.end == old }
if (found != null) {
found.end = new
return true
}

return false
}

/** Inserts the statement [s] before the statement specified in [before]. */
fun insertStatementBefore(s: Statement, beforeStmt: Statement) {
val statements = this.statements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ open class PropertyEdge<T : Node> : Persistable {
@Transient
class PropertyEdgeDelegate<T : Node, S : Node>(
val edge: KProperty1<S, List<PropertyEdge<T>>>,
val outgoing: Boolean = true
val outgoing: Boolean = true,
) {
operator fun getValue(thisRef: S, property: KProperty<*>): List<T> {
return PropertyEdge.unwrap(edge.get(thisRef), outgoing)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,8 @@
*/
package de.fraunhofer.aisec.cpg.graph.statements.expressions

import de.fraunhofer.aisec.cpg.graph.AST
import de.fraunhofer.aisec.cpg.graph.ArgumentHolder
import de.fraunhofer.aisec.cpg.graph.HasBase
import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.fqn
import de.fraunhofer.aisec.cpg.graph.types.HasType
import de.fraunhofer.aisec.cpg.graph.types.Type
import java.util.Objects
Expand All @@ -56,7 +52,7 @@ class MemberExpression : Reference(), HasOverloadedOperation, ArgumentHolder, Ha
override val operatorArguments: List<Expression>
get() = listOf()

override val operatorBase: HasType
override val operatorBase: Expression
get() = base

override fun toString(): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.graph.statements.expressions

import de.fraunhofer.aisec.cpg.graph.HasBase
import de.fraunhofer.aisec.cpg.graph.HasOperatorCode
import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.declarations.OperatorDeclaration

/**
* This special call expression is used when an operator (such as a [BinaryOperator]) is overloaded.
* In this case, we replace the original [BinaryOperator] with an [OperatorCallExpression], which
* points to its respective [OperatorDeclaration].
*/
class OperatorCallExpression : CallExpression(), HasOperatorCode, HasBase {

override var operatorCode: String? = null

override var name: Name
get() = Name(operatorCode ?: "")
set(_) {
// read-only
}

/**
* The base object. This is basically a shortcut to accessing the base of the [callee], if it
* has one (i.e., if it implements [HasBase]). This is the case for example, if it is a
* [MemberExpression].
*/
override val base: Expression?
get() {
return (callee as? HasBase)?.base
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@
*/
package de.fraunhofer.aisec.cpg.graph.statements.expressions

import de.fraunhofer.aisec.cpg.graph.AST
import de.fraunhofer.aisec.cpg.graph.AccessValues
import de.fraunhofer.aisec.cpg.graph.ArgumentHolder
import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation
import de.fraunhofer.aisec.cpg.graph.pointer
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.types.HasType
import de.fraunhofer.aisec.cpg.graph.types.Type
import org.apache.commons.lang3.builder.ToStringBuilder
Expand All @@ -53,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,23 @@ fun SubgraphWalker.ScopedWalker.replaceCallWithCast(
cast.expression = call.arguments.single()
cast.name = cast.castType.name

replaceArgument(parent, call, cast)
replace(parent, call, cast)
}

context(ContextProvider)
fun SubgraphWalker.ScopedWalker.replaceArgument(parent: Node?, old: Expression, new: Expression) {
if (parent !is ArgumentHolder) {
Pass.log.error(
"Parent AST node of call expression is not an argument holder. Cannot convert to cast expression. Further analysis might not be entirely accurate."
)
return
}
fun SubgraphWalker.ScopedWalker.replace(parent: Node?, old: Expression, new: Expression): Boolean {
val success =
if (parent is ArgumentHolder) {
parent.replaceArgument(old, new)
} else if (parent is StatementHolder) {
parent.replaceStatement(old, new)
} else {
Pass.log.error(
"Parent AST node is not an argument or statement holder. Cannot replace node. Further analysis might not be entirely accurate."
)
return false
}

val success = parent.replaceArgument(old, new)
if (!success) {
Pass.log.error(
"Replacing expression $old was not successful. Further analysis might not be entirely accurate."
Expand All @@ -153,4 +157,6 @@ fun SubgraphWalker.ScopedWalker.replaceArgument(parent: Node?, old: Expression,
// Make sure to inform the walker about our change
this.registerReplacement(old, new)
}

return success
}
Loading

0 comments on commit 750138e

Please sign in to comment.