Skip to content

Commit

Permalink
Create one Type object per type reference
Browse files Browse the repository at this point in the history
Fixes #1768
Fixes #1770
  • Loading branch information
oxisto committed Nov 6, 2024
1 parent 4e7c0b8 commit f1fe26b
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 71 deletions.
50 changes: 15 additions & 35 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,14 +28,17 @@ 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.graph.Name
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration
import de.fraunhofer.aisec.cpg.graph.parseName
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.graph.scopes.TemplateScope
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.passes.Pass
import de.fraunhofer.aisec.cpg.passes.ResolveCallExpressionAmbiguityPass
import de.fraunhofer.aisec.cpg.passes.TypeResolver
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import org.slf4j.Logger
Expand All @@ -57,8 +60,14 @@ class TypeManager {
MutableMap<TemplateDeclaration, MutableList<ParameterizedType>> =
ConcurrentHashMap()

val firstOrderTypes: MutableSet<Type> = ConcurrentHashMap.newKeySet()
val secondOrderTypes: MutableSet<Type> = ConcurrentHashMap.newKeySet()
val firstOrderTypes = mutableListOf<Type>()
val secondOrderTypes = mutableListOf<Type>()

/**
* A map of declared types by their name. Useful to check for the existence of a declared type
* by its fully qualified name across all scopes.
*/
@PopulatedByPass(TypeResolver::class) val declaredTypes = mutableMapOf<Name, Type>()

/**
* @param recordDeclaration that is instantiated by a template containing parameterizedtypes
Expand Down Expand Up @@ -200,26 +209,9 @@ class TypeManager {
}

if (t.isFirstOrderType) {
// Make sure we only ever return one unique object per type
if (!firstOrderTypes.add(t)) {
return firstOrderTypes.first { it == t && it is T } as T
} else {
log.trace(
"Registering unique first order type {}{}",
t.name,
if ((t as? ObjectType)?.generics?.isNotEmpty() == true) {
" with generics ${t.generics.joinToString(",", "[", "]") { it.name.toString() }}"
} else {
""
}
)
}
synchronized(firstOrderTypes) { firstOrderTypes.add(t) }
} else if (t is SecondOrderType) {
if (!secondOrderTypes.add(t)) {
return secondOrderTypes.first { it == t && it is T } as T
} else {
log.trace("Registering unique second order type {}", t.name)
}
synchronized(secondOrderTypes) { secondOrderTypes.add(t) }
}

return t
Expand All @@ -240,25 +232,13 @@ class TypeManager {
* This function returns the first (there should be only one) [Type] with the given [fqn] that
* is [Type.Origin.RESOLVED].
*/
fun lookupResolvedType(
fqn: CharSequence,
generics: List<Type>? = null,
language: Language<*>? = null
): Type? {
fun lookupResolvedType(fqn: CharSequence, language: Language<*>? = null): Type? {
var primitiveType = language?.getSimpleTypeOf(fqn)
if (primitiveType != null) {
return primitiveType
}

return firstOrderTypes.firstOrNull {
(it.typeOrigin == Type.Origin.RESOLVED || it.typeOrigin == Type.Origin.GUESSED) &&
it.root.name == fqn &&
if (generics != null) {
(it as? ObjectType)?.generics == generics
} else {
true
}
}
return declaredTypes[language.parseName(fqn)]
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,34 +113,16 @@ fun LanguageProvider.objectType(
"Could not create type: translation context not available"
)

val scope = c.scopeManager.currentScope

synchronized(c.typeManager.firstOrderTypes) {
// We can try to look up the type by its name and return it, if it already exists.
var type =
c.typeManager.firstOrderTypes.firstOrNull {
it is ObjectType &&
it.name == name &&
it.scope == scope &&
it.generics == generics &&
it.language == language
}
if (type != null) {
return type
}

// Otherwise, we either need to create the type because of the generics or because we do not
// know the type yet.
type = ObjectType(name, generics, false, language)
// Apply our usual metadata, such as scope, code, location, if we have any. Make sure only
// to refer by the local name because we will treat types as sort of references when
// creating them and resolve them later.
type.applyMetadata(this, name, rawNode = rawNode, localNameOnly = true)

// Piping it through register type will ensure that in any case we return the one unique
// type object (per scope) for it.
return c.typeManager.registerType(type)
}
// Otherwise, we either need to create the type because of the generics or because we do not
// know the type yet.
var type = ObjectType(name, generics, false, language)
// Apply our usual metadata, such as scope, code, location, if we have any. Make sure only
// to refer by the local name because we will treat types as sort of references when
// creating them and resolve them later.
type.applyMetadata(this, name, rawNode = rawNode, localNameOnly = true)

// Piping it through register type will ensure that we know the type and can resolve it later
return c.typeManager.registerType(type)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,27 @@ class ResolveCallExpressionAmbiguityPass(ctx: TranslationContext) : TranslationU
}
}

/** This function checks whether our [Reference] refers to a [Type]. */
private fun lookupPotentialTypeFromReference(ref: Reference): Type? {
var name = ref.name
var scope = ref.scope

// First, check if it is a simple type
var type = ref.language?.getSimpleTypeOf(name)
if (type != null) {
return type
}

// This could also be a typedef
type = scopeManager.typedefFor(name, scope)
if (type != null) {
return type
}

// Lastly, check if the reference contains a symbol that points to type (declaration)
return scopeManager.lookupUniqueTypeSymbolByName(name, scope)?.declaredType
}

override fun cleanup() {
// Nothing to do
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ fun <T : Any?> assertLiteralValue(expected: T, expr: Expression?, message: Strin
assertEquals(expected, assertIs<Literal<T>>(expr).value, message)
}

fun ContextProvider.assertResolvedType(fqn: String, generics: List<Type>? = null): Type {
var type = ctx?.typeManager?.lookupResolvedType(fqn, generics)
fun ContextProvider.assertResolvedType(fqn: String): Type {
var type = ctx?.typeManager?.lookupResolvedType(fqn)
return assertNotNull(type)
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ internal class CXXLanguageFrontendTest : BaseTest() {
val decl = main
val ls = decl.variables["ls"]
assertNotNull(ls)
assertEquals(
assertResolvedType("std::vector", listOf(assertResolvedType("int"))),
ls.type
)
assertEquals(assertResolvedType("std::vector"), ls.type)
assertLocalName("ls", ls)

val forEachStatement = decl.forEachLoops.firstOrNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,8 @@ class Application : Callable<Int> {

if (!noDefaultPasses) {
translationConfiguration.defaultPasses()
translationConfiguration.registerPass<ControlDependenceGraphPass>()
translationConfiguration.registerPass<ProgramDependenceGraphPass>()
// translationConfiguration.registerPass<ControlDependenceGraphPass>()
// translationConfiguration.registerPass<ProgramDependenceGraphPass>()
}
if (customPasses != "DEFAULT") {
val pieces = customPasses.split(",")
Expand Down

0 comments on commit f1fe26b

Please sign in to comment.