Skip to content

Commit

Permalink
Implementing operator overloading
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Aug 30, 2024
1 parent def570f commit 7241b17
Show file tree
Hide file tree
Showing 25 changed files with 409 additions and 86 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 @@ -39,6 +39,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.graph.unknownType
import de.fraunhofer.aisec.cpg.passes.SymbolResolver
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ interface ArgumentHolder : Holder<Expression> {
return false
}

override fun replace(old: Expression, new: Expression): Boolean {
return replaceArgument(old, new)
}

/**
* Replaces the existing argument specified in [old] with the one in [new]. Implementation how
* to do that might be specific to the argument holder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,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 @@ -504,6 +504,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 @@ -37,6 +37,15 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
* [CallExpression]).
*/
interface Holder<NodeTypeToHold : Node> {

/**
* Replaces the existing node specified in [old] with the one in [new]. Implementation how to do
* that might be specific to the holder.
*
* An indication whether this operation was successful needs to be returned.
*/
fun replace(old: NodeTypeToHold, new: NodeTypeToHold): Boolean

/** Adds a [Node] to the list of "held" nodes. */
operator fun plusAssign(node: NodeTypeToHold)
}
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 @@ -50,6 +50,10 @@ interface StatementHolder : Holder<Statement> {
*/
var statements: MutableList<Statement>

override fun replace(old: Statement, new: Statement): Boolean {
return statementEdges.replace(old, new)
}

override operator fun plusAssign(node: Statement) {
statementEdges += node
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ abstract class EdgeList<NodeType : Node, EdgeType : Edge<NodeType>>(
return ok
}

fun replace(old: NodeType, new: NodeType): Boolean {
val idx = this.indexOfFirst { it.end == old }
if (idx != -1) {
this[idx] = init(thisRef, new)
return true
}

return false
}

override fun clear() {
// Make a copy of our edges so we can pass a copy to our on-remove handler
val edges = this.toList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,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 @@ -263,7 +263,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 @@ -60,7 +60,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 @@ -57,7 +57,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 @@ -27,10 +27,15 @@ package de.fraunhofer.aisec.cpg.helpers

import de.fraunhofer.aisec.cpg.ScopeManager
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.ArgumentHolder
import de.fraunhofer.aisec.cpg.graph.ContextProvider
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.StatementHolder
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.edges.ast.AstEdge
import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeCollection
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.passes.Pass
import de.fraunhofer.aisec.cpg.processing.strategy.Strategy
import java.lang.annotation.AnnotationFormatError
import java.lang.reflect.Field
Expand Down Expand Up @@ -340,3 +345,38 @@ object SubgraphWalker {
}
}
}

context(ContextProvider)
fun SubgraphWalker.ScopedWalker.replace(parent: Node?, old: Expression, new: Expression): Boolean {
val success =
when (parent) {
is ArgumentHolder -> parent.replace(old, new)
is StatementHolder -> parent.replace(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
}
}

if (!success) {
Pass.log.error(
"Replacing expression $old was not successful. Further analysis might not be entirely accurate."
)
} else {
// Store any eventual EOG nodes and disconnect old node
val oldPrevEOG = old.prevEOG.toMutableList()
val oldNextEOG = old.nextEOG.toMutableList()
old.disconnectFromGraph()

// Put the stored EOG nodes to the new node
new.prevEOG = oldPrevEOG
new.nextEOG = oldNextEOG

// Make sure to inform the walker about our change
this.registerReplacement(old, new)
}

return success
}
Loading

0 comments on commit 7241b17

Please sign in to comment.