Skip to content

Commit

Permalink
Added OperatorDeclaration
Browse files Browse the repository at this point in the history
This PR adds support for an operator declaration. They are not resolved yet.
  • Loading branch information
oxisto committed Jul 22, 2024
1 parent 6b19d9b commit 2c74cd1
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,14 @@ 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.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.*

/**
* A language trait is a feature or trait that is common to a group of programming languages. Any
Expand Down Expand Up @@ -230,3 +228,13 @@ 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 the
* [HasOperatorCode.operatorCode] and the value is the name of the function.
*/
val overloadedOperatorNames: Map<String, Symbol>
}
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,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 @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.frontends.*
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.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression
import de.fraunhofer.aisec.cpg.graph.types.*
Expand All @@ -47,11 +48,55 @@ 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")

override val overloadedOperatorNames: Map<String, Symbol>
get() =
mapOf(
// Arithmetic operators. See
// https://en.cppreference.com/w/cpp/language/operator_arithmetic
"+" to "operator+",
"-" to "operator-",
"*" to "operator+",
"/" to "operator/",
"%" to "operator%",
"&" to "operator&",
"|" to "operator|",
"^" to "operator^",
"<<" to "operator<<",
">>" to "operator>>",

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

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

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

// Other operators. See https://en.cppreference.com/w/cpp/language/operator_other
"()" to "operator()",
"," 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
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
package de.fraunhofer.aisec.cpg.frontends.cxx

import de.fraunhofer.aisec.cpg.ResolveInFrontend
import de.fraunhofer.aisec.cpg.frontends.HasOperatorOverloading
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.scopes.NameScope
import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.helpers.Util
import java.util.*
import java.util.function.Supplier
import org.eclipse.cdt.core.dom.ast.*
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier
Expand Down Expand Up @@ -158,7 +158,7 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) :
* checks if the scope is a namespace or a record and if the name matches to the record (in case
* of a constructor).
*/
private fun createFunctionOrMethodOrConstructor(
private fun createAppropriateFunction(
name: Name,
scope: Scope?,
ctx: IASTNode,
Expand All @@ -168,9 +168,14 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) :

val func =
when {
// Check if it's an operator
name.isKnownOperatorName -> {
// retrieve the operator code
var operatorCode = name.localName.drop("operator".length)
newOperatorDeclaration(name, operatorCode, rawNode = ctx)
}
// Check, if it's a constructor. This is the case if the local names of the function
// and the
// record declaration match
// and the record declaration match
holder is RecordDeclaration && name.localName == holder.name.localName -> {
newConstructorDeclaration(name, holder, rawNode = ctx)
}
Expand Down Expand Up @@ -219,9 +224,11 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) :

/*
* As always, there are some special cases to consider and one of those are C++ operators.
* They are regarded as functions and eclipse CDT for some reason introduces a whitespace in the function name, which will complicate things later on
* They are regarded as functions and eclipse CDT for some reason introduces a whitespace
* in the function name, which will complicate things later on. But we only want to replace
* the whitespace for "standard" operators.
*/
if (name.startsWith("operator")) {
if (nameDecl.name is CPPASTOperatorName && name.replace(" ", "").isKnownOperatorName) {
name = name.replace(" ", "")
}
val declaration: FunctionDeclaration
Expand All @@ -242,15 +249,11 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) :
frontend.scopeManager.currentNamespace.fqn(parent.toString()).toString()
)

declaration = createFunctionOrMethodOrConstructor(name, parentScope, ctx.parent)
declaration = createAppropriateFunction(name, parentScope, ctx.parent)
} else if (frontend.scopeManager.isInRecord) {
// If the current scope is already a record, it's a method
declaration =
createFunctionOrMethodOrConstructor(
name,
frontend.scopeManager.currentScope,
ctx.parent
)
createAppropriateFunction(name, frontend.scopeManager.currentScope, ctx.parent)
} else {
// a plain old function, outside any named scope
declaration = newFunctionDeclaration(name, rawNode = ctx.parent)
Expand Down Expand Up @@ -506,6 +509,17 @@ class DeclaratorHandler(lang: CXXLanguageFrontend) :
frontend.declarationHandler.handle(member)
}
}

/** Checks whether the [Name] for a function is a known operator name. */
val Name.isKnownOperatorName: Boolean
get() {
var language = language
if (language !is HasOperatorOverloading) {
return false
}

return language.overloadedOperatorNames.containsValue(this.localName)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,18 @@ class CXXDeclarationTest {
assertEquals(assertResolvedType("ABC::A"), a.type)
}
}

@Test
fun testArithmeticOperator() {
val file = File("src/test/resources/cxx/operators/arithmetic.cpp")
val result =
analyze(listOf(file), file.parentFile.toPath(), true) {
it.registerLanguage<CPPLanguage>()
}
assertNotNull(result)

var plusplus = result.operators["operator++"]
assertNotNull(plusplus)
assertEquals("++", plusplus.operatorCode)
}
}
27 changes: 27 additions & 0 deletions cpg-language-cxx/src/test/resources/cxx/operators/arithmetic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Integer {
public:
Integer(int i) : i(i) {}
void operator++(int) {
i++;
}

Integer operator+(int j) {
return Integer(this->i+j);
}

Integer operator+(Integer &j) {
return Integer(this->i+j.i);
}

void test() {};

int i;
};

int main() {
Integer i(5);
i++;

Integer j = i + 2;
auto k = i + j;
}

0 comments on commit 2c74cd1

Please sign in to comment.