Skip to content

Commit

Permalink
Converting first set of graph nodes to Kotlin (#845)
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto authored Jun 29, 2022
1 parent ddc1f60 commit 459b357
Show file tree
Hide file tree
Showing 20 changed files with 566 additions and 674 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class NullPointerCheck {
fun handleHasBase(node: HasBase) {
try {
// check for all incoming DFG branches
node.base.prevDFG.forEach {
node.base?.prevDFG?.forEach {
var resolved: Any? = CouldNotResolve()
val resolver = ValueEvaluator()
if (it is Expression || it is Declaration) {
Expand All @@ -90,7 +90,7 @@ class NullPointerCheck {
sb.append("--- FINDING: Null pointer detected in ")
sb.append(node.javaClass.simpleName, DEFAULT.foreground(GREEN))
sb.append(" when accessing base ")
sb.append(node.base.name, DEFAULT.foreground(YELLOW))
sb.append(node.base?.name, DEFAULT.foreground(YELLOW))
sb.append(" ---")

val header = sb.toAnsi()
Expand All @@ -104,7 +104,7 @@ class NullPointerCheck {
)
println("")
println(
"The following path was discovered that leads to ${AttributedString(node.base.name, DEFAULT.foreground(YELLOW)).toAnsi()} being null:"
"The following path was discovered that leads to ${AttributedString(node.base?.name, DEFAULT.foreground(YELLOW)).toAnsi()} being null:"
)
for (p in resolver.path) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,22 +232,22 @@ class ExpressionHandler(lang: CXXLanguageFrontend) :
}

/**
* Gets all arguments a template was instantiated with. Note, that the arguments can either be
* Expressions referring to a value ot TypeExpressions referring to a type.
* Retrieves all arguments a template was instantiated with. Note, that the arguments can either
* be Expressions referring to a value ot TypeExpressions referring to a type.
*
* @param template
* @return List of Nodes containing the all the arguments the template was instantiated with.
*/
private fun getTemplateArguments(template: CPPASTTemplateId): List<Node?> {
val templateArguments: MutableList<Node?> = ArrayList()
private fun getTemplateArguments(template: CPPASTTemplateId): List<Node> {
val templateArguments: MutableList<Node> = ArrayList()
for (argument in template.templateArguments) {
if (argument is IASTTypeId) {
val type = TypeParser.createFrom(argument.declSpecifier.toString(), true)
templateArguments.add(NodeBuilder.newTypeExpression(type.name, type))
} else if (argument is IASTLiteralExpression) {
templateArguments.add(
lang.expressionHandler.handle(argument as IASTInitializerClause)
)
lang.expressionHandler.handle(argument as IASTInitializerClause)?.let {
templateArguments.add(it)
}
}
}
return templateArguments
Expand Down Expand Up @@ -443,12 +443,11 @@ class ExpressionHandler(lang: CXXLanguageFrontend) :
as CPPASTTemplateId)
.templateName.toString()
callExpression.name = name
callExpression.addExplicitTemplateParameters(
getTemplateArguments(
getTemplateArguments(
(ctx.functionNameExpression as IASTFieldReference).fieldName
as CPPASTTemplateId
)
)
.forEach { callExpression.addTemplateParameter(it) }
}
} else if (reference is BinaryOperator && reference.operatorCode == ".") {
// We have a dot operator that was not classified as a member expression. This happens
Expand All @@ -473,11 +472,10 @@ class ExpressionHandler(lang: CXXLanguageFrontend) :
((ctx.functionNameExpression as IASTIdExpression).name as CPPASTTemplateId)
.templateName.toString()
callExpression = NodeBuilder.newCallExpression(name, name, ctx.rawSignature, true)
callExpression.addExplicitTemplateParameters(
getTemplateArguments(
getTemplateArguments(
(ctx.functionNameExpression as IASTIdExpression).name as CPPASTTemplateId
)
)
.forEach { callExpression.addTemplateParameter(it) }
} else if (reference is CastExpression) {
// this really is a cast expression in disguise
return reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
/** Specifies that a certain node has a base on which it executes an operation. */
interface HasBase {

val base: Expression
val base: Expression?
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ object NodeBuilder {
node.name = name!!
node.setCodeAndRegion(lang, rawNode, code)
node.fqn = fqn
node.setTemplate(template)
node.template = template
log(node)
return node
}
Expand Down Expand Up @@ -385,7 +385,7 @@ object NodeBuilder {
): CallExpression {
val node = MemberCallExpression()
node.name = name!!
node.setBase(base)
node.base = base
node.member = member
node.operatorCode = operatorCode
node.setCodeAndRegion(lang, rawNode, code)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,17 @@
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.graph.declarations;
package de.fraunhofer.aisec.cpg.graph.declarations

import de.fraunhofer.aisec.cpg.graph.Node;
import de.fraunhofer.aisec.cpg.graph.Node

/**
* Represents a single declaration or definition, i.e. of a variable ({@link VariableDeclaration})
* or function ({@link FunctionDeclaration}).
* Represents a single declaration or definition, i.e. of a variable ([VariableDeclaration]) or
* function ([FunctionDeclaration]).
*
* <p>Note: We do NOT (currently) distinguish between the definition and the declaration of a
* function. This means, that if a function is first declared and later defined with a function
* body, we will currently have two {@link FunctionDeclaration} nodes. This is very similar to the
* behaviour of clang, however clang does establish a connection between those nodes, we currently
* do not.
* Note: We do NOT (currently) distinguish between the definition and the declaration of a function.
* This means, that if a function is first declared and later defined with a function body, we will
* currently have two [FunctionDeclaration] nodes. This is very similar to the behaviour of clang,
* however clang does establish a connection between those nodes, we currently do not.
*/
// TODO: expressionRefersToDeclaration definition and declaration nodes and introduce a field if its
// declaration only
public abstract class Declaration extends Node {}
abstract class Declaration : Node()

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, 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.graph.DeclarationHolder
import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge
import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate
import org.neo4j.ogm.annotation.Relationship

/**
* This represents a sequence of one or more declaration(s). The purpose of this node is primarily
* to bridge between a single declaration and a list of declarations in the front-end handlers. It
* will be converted into a list-structure and all its children will be added to the parent, i.e.
* the translation unit. It should NOT end up in the final graph.
*/
class DeclarationSequence : Declaration(), DeclarationHolder {
@Relationship(value = "CHILDREN", direction = Relationship.OUTGOING)
val childrenPropertyEdge: MutableList<PropertyEdge<Declaration>> = mutableListOf()

val children: List<Declaration> by
PropertyEdgeDelegate(DeclarationSequence::childrenPropertyEdge)

override fun addDeclaration(declaration: Declaration) {
if (declaration is DeclarationSequence) {
for (declarationChild in declaration.children) {
addIfNotContains(childrenPropertyEdge, declarationChild)
}
}
addIfNotContains(childrenPropertyEdge, declaration)
}

fun asList(): List<Declaration> {
return children
}

val isSingle: Boolean
get() = childrenPropertyEdge.size == 1

fun first(): Declaration {
return childrenPropertyEdge[0].end
}

override fun getDeclarations(): List<Declaration> {
return children
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,31 @@ package de.fraunhofer.aisec.cpg.graph.edge

import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.Persistable
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.ParameterizedType
import java.util.*
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
import org.neo4j.ogm.annotation.*
import org.neo4j.ogm.annotation.typeconversion.Convert
import org.slf4j.LoggerFactory

/**
* This class represents an edge between two [Node] objects in a Neo4J graph. It can be used to
* store additional information that relate to the relationship between the two nodes that belong to
* neither of the two nodes directly.
*
* An example would be the name (in this case `a`) of an argument between a [CallExpression] (`foo`)
* and its argument (a [Literal] of `2`) in languages that support keyword arguments, such as
* Python:
* ```python
* foo("bar", a = 2)
* ```
*/
@RelationshipEntity
open class PropertyEdge<T : Node> : Persistable {
/** Required field for object graph mapping. It contains the node id. */
Expand Down Expand Up @@ -296,3 +313,44 @@ open class PropertyEdge<T : Node> : Persistable {
}
}
}

/**
* This class can be used to implement
* [delegated properties](https://kotlinlang.org/docs/delegated-properties.html) in [Node] classes.
* The most common use case is to have a property that is a list of [PropertyEdge] objects (for
* persistence) and a second (delegated) property that allows easy access just to the connected
* nodes of the individual edges for in-memory access.
*
* For example:
* ```kotlin
*
* class MyNode {
* @Relationship(value = "EXPRESSIONS", direction = "OUTGOING")
* @field:SubGraph("AST")
* var expressionsEdges = mutableListOf<PropertyEdge<Expression>>()
* var expressions by PropertyEdgeDelegate(MyNode::expressionsEdges)
* }
* ```
*
* This class is intentionally marked with [Transient], so that the delegated properties are not
* transferred to the Neo4J OGM. Only the property that contains the property edges should be
* persisted in the graph database.
*/
@Transient
class PropertyEdgeDelegate<T : Node, S : Node>(
val edge: KProperty1<S, List<PropertyEdge<T>>>,
val outgoing: Boolean = true
) {
operator fun getValue(thisRef: S, property: KProperty<*>): List<T> {
return PropertyEdge.unwrap(edge.get(thisRef), outgoing)
}

operator fun setValue(thisRef: S, property: KProperty<*>, value: List<T>) {
if (edge is KMutableProperty1) {
edge.setter.call(
thisRef,
PropertyEdge.transformIntoOutgoingPropertyEdgeList(value, thisRef as Node)
)
}
}
}
Loading

0 comments on commit 459b357

Please sign in to comment.