Skip to content

Commit

Permalink
Added OperatorDeclaration (#1605)
Browse files Browse the repository at this point in the history
* Added `OperatorDeclaration`

This PR adds support for an operator declaration. They are not resolved yet.

* Python operators (#1625)

---------

Co-authored-by: Maximilian Kaul <[email protected]>
  • Loading branch information
oxisto and maximiliankaul authored Jul 22, 2024
1 parent bf8b37c commit cf398db
Show file tree
Hide file tree
Showing 15 changed files with 299 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ package de.fraunhofer.aisec.cpg.frontends

import de.fraunhofer.aisec.cpg.ScopeManager
import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.graph.HasOperatorCode
import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CastExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression
import de.fraunhofer.aisec.cpg.graph.scopes.*
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.passes.ReplaceCallCastPass
import de.fraunhofer.aisec.cpg.passes.SymbolResolver
import de.fraunhofer.aisec.cpg.passes.*
import kotlin.reflect.KClass

/**
* A language trait is a feature or trait that is common to a group of programming languages. Any
Expand Down Expand Up @@ -230,3 +230,24 @@ interface HasFunctionalCasts : LanguageTrait
* multiple functions can share the same name with different parameters.
*/
interface HasFunctionOverloading : LanguageTrait

/** A language trait that specifies that this language allows overloading of operators. */
interface HasOperatorOverloading : LanguageTrait {

/**
* A map of operator codes and function names acting as overloaded operators. The key is a pair
* of the class and [HasOperatorCode.operatorCode] (ideally created by [of]) and the value is
* the name of the function.
*/
val overloadedOperatorNames: Map<Pair<KClass<out HasOverloadedOperation>, String>, Symbol>
}

/**
* Creates a [Pair] of class and operator code used in
* [HasOperatorOverloading.overloadedOperatorNames].
*/
inline infix fun <reified T : HasOverloadedOperation> KClass<T>.of(
operatorCode: String
): Pair<KClass<T>, String> {
return Pair(T::class, operatorCode)
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@ fun MetadataProvider.newMethodDeclaration(
return node
}

/**
* Creates a new [OperatorDeclaration]. 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.newOperatorDeclaration(
name: CharSequence,
operatorCode: String,
recordDeclaration: RecordDeclaration? = null,
rawNode: Any? = null
): MethodDeclaration {
val node = OperatorDeclaration()
node.applyMetadata(this, name, rawNode, defaultNamespace = recordDeclaration?.name)

node.operatorCode = operatorCode
node.recordDeclaration = recordDeclaration

log(node)
return node
}

/**
* Creates a new [ConstructorDeclaration]. 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 @@ -530,6 +530,10 @@ val Node?.casts: List<CastExpression>
val Node?.methods: List<MethodDeclaration>
get() = this.allChildren()

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

/** Returns all [FieldDeclaration] children in this graph, starting with this [Node]. */
val Node?.fields: List<FieldDeclaration>
get() = this.allChildren()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
*/
package de.fraunhofer.aisec.cpg.graph

import de.fraunhofer.aisec.cpg.graph.declarations.OperatorDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression

/** Specifies that a certain node has a base on which it executes an operation. */
Expand All @@ -39,3 +41,9 @@ interface HasBase : HasOperatorCode {
*/
override val operatorCode: String?
}

/**
* Specifies that this node (e.g. a [BinaryOperator] contains an operation that can be overloaded by
* an [OperatorDeclaration].
*/
interface HasOverloadedOperation : HasBase
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
*/
package de.fraunhofer.aisec.cpg.graph

import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.declarations

import de.fraunhofer.aisec.cpg.frontends.HasOperatorOverloading
import de.fraunhofer.aisec.cpg.graph.HasOperatorCode
import de.fraunhofer.aisec.cpg.graph.types.ObjectType

/**
* Some languages allow to either overload operators or to add custom operators to classes (see
* [HasOperatorOverloading]). In both cases, this special function class denotes that this handles
* this particular operator call.
*
* We need to derive from [MethodDeclaration] because all operators have a base class on which they
* operate on. Therefore, we need to associate them with an [ObjectType] and/or [RecordDeclaration].
* There are some very special cases for C++, where we can have a global operator for a particular
* class. In this case we just pretend like it is a method operator.
*/
class OperatorDeclaration : MethodDeclaration(), HasOperatorCode {
/** The operator code which this operator declares. */
override var operatorCode: String? = null

val isPrefix: Boolean = false
val isPostfix: Boolean = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder
* Note: For assignments, i.e., using an `=` or `+=`, etc. the [AssignExpression] MUST be used.
*/
open class BinaryOperator :
Expression(), HasBase, HasOperatorCode, ArgumentHolder, HasType.TypeObserver {
Expression(), HasOverloadedOperation, ArgumentHolder, HasType.TypeObserver {
/** The left-hand expression. */
@AST
var lhs: Expression = ProblemExpression("could not parse lhs")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import java.util.*
* While this node implements [HasBase], this is basically just a shortcut to access the base of the
* underlying [callee] property, if appropriate.
*/
class MemberCallExpression : CallExpression(), HasBase, HasOperatorCode {
class MemberCallExpression : CallExpression(), HasOverloadedOperation, HasBase, HasOperatorCode {
/**
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ 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.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.fqn
import de.fraunhofer.aisec.cpg.graph.types.HasType
Expand All @@ -40,7 +41,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder
* use-case is access of a member function (method) as part of the [MemberCallExpression.callee]
* property of a [MemberCallExpression].
*/
class MemberExpression : Reference(), ArgumentHolder, HasBase {
class MemberExpression : Reference(), HasOverloadedOperation, ArgumentHolder, HasBase {
@AST
override var base: Expression = ProblemExpression("could not parse base expression")
set(value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ 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.types.HasType
import de.fraunhofer.aisec.cpg.graph.types.Type
import org.apache.commons.lang3.builder.ToStringBuilder

/** A unary operator expression, involving one expression and an operator, such as `a++`. */
class UnaryOperator : Expression(), ArgumentHolder, HasType.TypeObserver {
class UnaryOperator : Expression(), HasOverloadedOperation, ArgumentHolder, HasType.TypeObserver {
/** The expression on which the operation is applied. */
@AST
var input: Expression = ProblemExpression("could not parse input")
Expand All @@ -45,8 +46,12 @@ class UnaryOperator : Expression(), ArgumentHolder, HasType.TypeObserver {
changeExpressionAccess()
}

/** The unary operator operates on itself. */
override val base: Expression?
get() = this

/** The operator code. */
var operatorCode: String? = null
override var operatorCode: String? = null
set(value) {
field = value
changeExpressionAccess()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,20 @@ package de.fraunhofer.aisec.cpg.frontends.cxx

import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.frontends.*
import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.edge.Properties
import de.fraunhofer.aisec.cpg.graph.scopes.Symbol
import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.passes.*
import de.fraunhofer.aisec.cpg.passes.inference.startInference
import kotlin.reflect.KClass
import org.neo4j.ogm.annotation.Transient

/** The C++ language. */
Expand All @@ -47,11 +53,59 @@ open class CPPLanguage :
HasClasses,
HasUnknownType,
HasFunctionalCasts,
HasFunctionOverloading {
HasFunctionOverloading,
HasOperatorOverloading {
override val fileExtensions = listOf("cpp", "cc", "cxx", "c++", "hpp", "hh")
override val elaboratedTypeSpecifier = listOf("class", "struct", "union", "enum")
override val unknownTypeString = listOf("auto")

@Transient
override val overloadedOperatorNames:
Map<Pair<KClass<out HasOverloadedOperation>, String>, Symbol> =
mapOf(
// Arithmetic operators. See
// https://en.cppreference.com/w/cpp/language/operator_arithmetic
UnaryOperator::class of "+" to "operator+",
UnaryOperator::class of "-" to "operator-",
BinaryOperator::class of "+" to "operator+",
BinaryOperator::class of "-" to "operator-",
BinaryOperator::class of "*" to "operator*",
BinaryOperator::class of "/" to "operator/",
BinaryOperator::class of "%" to "operator%",
UnaryOperator::class of "~" to "operator~",
BinaryOperator::class of "&" to "operator&",
BinaryOperator::class of "|" to "operator|",
BinaryOperator::class of "^" to "operator^",
BinaryOperator::class of "<<" to "operator<<",
BinaryOperator::class of ">>" to "operator>>",

// Increment/decrement operators. See
// https://en.cppreference.com/w/cpp/language/operator_incdec
UnaryOperator::class of "++" to "operator++",
UnaryOperator::class of "--" to "operator--",

// Comparison operators. See
// https://en.cppreference.com/w/cpp/language/operator_comparison
BinaryOperator::class of "==" to "operator==",
BinaryOperator::class of "!=" to "operator!=",
BinaryOperator::class of "<" to "operator<",
BinaryOperator::class of ">" to "operator>",
BinaryOperator::class of "<=" to "operator<=",
BinaryOperator::class of "=>" to "operator=>",

// Member access operators. See
// https://en.cppreference.com/w/cpp/language/operator_member_access
MemberExpression::class of "[]" to "operator[]",
UnaryOperator::class of "*" to "operator*",
UnaryOperator::class of "&" to "operator&",
MemberExpression::class of "->" to "operator->",
MemberExpression::class of "->*" to "operator->*",

// Other operators. See https://en.cppreference.com/w/cpp/language/operator_other
MemberCallExpression::class of "()" to "operator()",
BinaryOperator::class of "," to "operator,",
)

/**
* The list of built-in types. See https://en.cppreference.com/w/cpp/language/types for a
* reference. We only list equivalent types here and use the canonical form of integer values.
Expand Down
Loading

0 comments on commit cf398db

Please sign in to comment.