Skip to content

Commit

Permalink
Improvements to C++ frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Apr 12, 2024
1 parent 1f752b7 commit 4d4202b
Show file tree
Hide file tree
Showing 36 changed files with 791 additions and 266 deletions.
60 changes: 46 additions & 14 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package de.fraunhofer.aisec.cpg

import de.fraunhofer.aisec.cpg.frontends.*
import de.fraunhofer.aisec.cpg.frontends.HasFirstClassFunctions
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.scopes.*
Expand Down Expand Up @@ -89,8 +90,7 @@ class ScopeManager : ScopeProvider {
* In reality, they can probably be defined at different scopes for other languages, but for now
* we only allow it for the current file.
*
* This can potentially be used to replace [addTypedef] at some point, which still relies on the
* existence of a [LanguageFrontend].
* This can potentially be merged with [addTypedef] at some point.
*/
private val aliases = mutableMapOf<PhysicalLocation.ArtifactLocation, MutableSet<Alias>>()

Expand Down Expand Up @@ -574,15 +574,13 @@ class ScopeManager : ScopeProvider {
}
}

/** Only used by the [TypeManager], adds typedefs to the current [ValueDeclarationScope]. */
fun addTypedef(typedef: TypedefDeclaration) {
val scope = this.firstScopeIsInstanceOrNull<ValueDeclarationScope>()
if (scope == null) {
LOGGER.error("Cannot add typedef. Not in declaration scope.")
return
}

scope.addTypedef(typedef)
/**
* Adds typedefs to a [ValueDeclarationScope]. The language frontend needs to decide on the
* scope of the typedef. Most likely, typedefs are global. Therefore, the [GlobalScope] is set
* as default.
*/
fun addTypedef(typedef: TypedefDeclaration, scope: ValueDeclarationScope? = globalScope) {
scope?.addTypedef(typedef)
}

private fun getCurrentTypedefs(searchScope: Scope?): Collection<TypedefDeclaration> {
Expand Down Expand Up @@ -968,9 +966,43 @@ class ScopeManager : ScopeProvider {
// a local definition overwrites / shadows one that was there on a higher scope.
while (current != null) {
if (current is ValueDeclarationScope) {
val decl = current.typedefs[alias]
if (decl != null) {
return decl.type
// This is a little bit of a hack to support partial FQN resolution at least with
// typedefs, but its not really ideal.
// And this also should be merged with the scope manager logic when resolving names.
//
// The better approach would be to harmonize the FQN of all types in one pass before
// all this happens.
//
// This process has several steps:
// First, do a quick local lookup, to see if we have a typedef our current scope
// (only do this if the name is not qualified)
if (!alias.name.isQualified() && current == currentScope) {
var decl = current.typedefs[alias]
if (decl != null) {
return decl.type
}
}

// Next, try to look up the name either by its FQN (if it is qualified) or make it
// qualified based on the current namespace
val key =
current.typedefs.keys.firstOrNull {
var lookupName = alias.name

// If the lookup name is already a FQN, we can use the name directly
lookupName =
if (lookupName.isQualified()) {
lookupName
} else {
// Otherwise, we want to make an FQN out of it using the current
// namespace
currentNamespace?.fqn(lookupName.localName) ?: lookupName
}

it.name.lastPartsMatch(lookupName)
}
if (key != null) {
return current.typedefs[key]?.type
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,9 @@ class TranslationContext(
* the [TranslationResult.finalCtx] this may either be null or the last component analyzed.
*/
var currentComponent: Component? = null,

// TODO: move somewhere better or maybe make translation context the statistics holder instead
// of the result?
var inferredFunctions: Int = 0,
var inferredRecords: Int = 0
)
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import de.fraunhofer.aisec.cpg.frontends.SupportsParallelParsing
import de.fraunhofer.aisec.cpg.frontends.TranslationException
import de.fraunhofer.aisec.cpg.graph.Component
import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.nodes
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.helpers.Benchmark
import de.fraunhofer.aisec.cpg.passes.*
import java.io.File
Expand Down Expand Up @@ -73,7 +76,7 @@ private constructor(
var executedFrontends = setOf<LanguageFrontend<*, *>>()

// Build a new global translation context
val ctx = TranslationContext(config, ScopeManager(), TypeManager())
val ctx = TranslationContext(config, ScopeManager(), TypeManager(), null)

// Build a new translation result
val result = TranslationResult(this, ctx)
Expand Down Expand Up @@ -115,6 +118,20 @@ private constructor(
}
}

// Print some graph statistics
val nodes = result.nodes
val functions = nodes.filterIsInstance<FunctionDeclaration>()
val records = nodes.filterIsInstance<CallExpression>()
val calls = nodes.filterIsInstance<CallExpression>()

log.info("Total graph nodes: ${nodes.size} ")
log.info("Function nodes: ${functions.size}")
log.info("Record nodes: ${records.size}")
log.info("Call nodes: ${calls.size}")

log.info("Inferred functions: ${ctx.inferredFunctions}")
log.info("Inferred records: ${ctx.inferredRecords}")

return result
}

Expand Down
26 changes: 1 addition & 25 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ package de.fraunhofer.aisec.cpg
import de.fraunhofer.aisec.cpg.frontends.CastNotPossible
import de.fraunhofer.aisec.cpg.frontends.CastResult
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TypedefDeclaration
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.graph.scopes.TemplateScope
import de.fraunhofer.aisec.cpg.graph.types.*
Expand Down Expand Up @@ -231,26 +228,6 @@ class TypeManager {
return firstOrderTypes.stream().anyMatch { type: Type -> type.root.name.toString() == name }
}

/**
* Creates a typedef / type alias in the form of a [TypedefDeclaration] to the scope manager and
* returns it.
*
* @param frontend the language frontend
* @param rawCode the raw code
* @param target the target type
* @param alias the alias type
* @return the typedef declaration
*/
fun createTypeAlias(
frontend: LanguageFrontend<*, *>,
target: Type,
alias: Type,
): Declaration {
val typedef = frontend.newTypedefDeclaration(target, alias)
frontend.scopeManager.addTypedef(typedef)
return typedef
}

fun resolvePossibleTypedef(alias: Type, scopeManager: ScopeManager): Type {
val finalToCheck = alias.root
val applicable = scopeManager.typedefFor(finalToCheck)
Expand Down Expand Up @@ -417,8 +394,7 @@ fun Type.wrap(wrapState: WrapState): Type {
}

if (wrapState.isReference) {
wrapState.referenceType?.elementType = type
return wrapState.referenceType!!
return ReferenceType(this)
}

return type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,11 +342,19 @@ abstract class Language<T : LanguageFrontend<*, *>> : Node() {

// TODO: Move this code somewhere else once we have a proper template expansion pass
// We need to check, whether this language has special handling of templates. In this
// case, we need to check, whether a template matches after we have no direct matches
// case, we need to check, whether a template matches directly after we have no direct
// matches
if (this is HasTemplates) {
result.call.templateParameterEdges = mutableListOf()
val (ok, candidates) =
this.handleTemplateFunctionCalls(null, result.call, false, result.call.ctx!!, null)
this.handleTemplateFunctionCalls(
null,
result.call,
false,
result.call.ctx!!,
null,
needsExactMatch = true
)
if (ok) {
return Pair(candidates.toSet(), CallResolutionResult.SuccessKind.SUCCESSFUL)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ interface HasTemplates : HasGenerics {
templateCall: CallExpression,
applyInference: Boolean,
ctx: TranslationContext,
currentTU: TranslationUnitDeclaration?
currentTU: TranslationUnitDeclaration?,
needsExactMatch: Boolean
): Pair<Boolean, List<FunctionDeclaration>>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@ fun <T : Node, AstNode> T.codeAndLocationFromChildren(parentNode: AstNode): T {
first,
current,
compareBy(
{ it.location?.region?.startLine },
{ it.location?.region?.startColumn }
{ it?.location?.region?.startLine },
{ it?.location?.region?.startColumn }
)
)
last =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,5 @@ class IncludeDeclaration : Declaration() {
filename == other.filename)
}

override fun hashCode() = Objects.hash(super.hashCode(), includes, problems, filename)
override fun hashCode() = Objects.hash(super.hashCode(), problems, filename)
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class ParameterDeclaration : ValueDeclaration(), HasDefault<Expression?> {
@AST
private var defaultValue: Expression? = null

var modifiers: List<String> = mutableListOf()

override var default: Expression?
get() = defaultValue
set(value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class CastExpression : Expression(), ArgumentHolder, HasType.TypeObserver {
set(value) {
field = value
type = value
name = value.name
}

fun setCastOperator(operatorCode: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,19 @@ class ConditionalExpression : Expression(), ArgumentHolder, BranchingNode, HasTy
}

override fun replaceArgument(old: Expression, new: Expression): Boolean {
// Do nothing
return false
return when (old) {
thenExpression -> {
thenExpression = new
true
}
elseExpression -> {
elseExpression = new
true
}
else -> {
false
}
}
}

override fun typeChanged(newType: Type, src: HasType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,6 @@ open class ObjectType : Type {
return unknownType()
}

override fun isSimilar(t: Type?): Boolean {
return t is ObjectType && generics == t.generics && super.isSimilar(t)
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ObjectType) return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
package de.fraunhofer.aisec.cpg.helpers

import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration
import de.fraunhofer.aisec.cpg.graph.edge.CallingContextIn
Expand Down
Loading

0 comments on commit 4d4202b

Please sign in to comment.