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 Oct 2, 2024
1 parent 3d07443 commit de11e5f
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 90 deletions.
51 changes: 51 additions & 0 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.scopes.*
import de.fraunhofer.aisec.cpg.graph.statements.*
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.graph.types.DeclaresType
import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType
import de.fraunhofer.aisec.cpg.graph.types.IncompleteType
import de.fraunhofer.aisec.cpg.graph.types.Type
Expand Down Expand Up @@ -1004,6 +1005,56 @@ class ScopeManager : ScopeProvider {

return list
}

/**
* This function tries to look up the symbol contained in [name] (using [findSymbols]) and
* returns a [DeclaresType] node, if this name resolved to something which declares a type.
*/
fun findTypeDeclaration(name: Name, startScope: Scope?): DeclaresType? {
var symbols =
findSymbols(name = name, startScope = startScope) { it is DeclaresType }
.filterIsInstance<DeclaresType>()

// We need to have a single match, otherwise we have an ambiguous type, and we cannot
// normalize it.
if (symbols.size > 1) {
LOGGER.warn(
"Lookup of type {} returned more than one symbol which declares a type, this is an ambiguity and the following analysis might not be correct.",
name
)
}

return symbols.singleOrNull()
}

/**
* This function checks, whether there exists a [Type] for the given combination of a [name] and
* [scope] and returns it. It returns null, if no such type exists.
*
* This is needed in passes that need to replace potential identifiers with [Type] nodes,
* because of ambiguities (e.g. [ResolveCallExpressionAmbiguityPass]).
*/
fun findTypeWithNameAndScope(
name: Name,
language: Language<*>?,
scope: Scope? = currentScope
): Type? {
// First, check if it is a simple type
var type = language?.getSimpleTypeOf(name)
if (type != null) {
return type
}

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

// Otherwise, try to find a type declaration with the given combination of symbol and name
var declares = findTypeDeclaration(name, scope)
return declares?.declaredType
}
}

/**
Expand Down
25 changes: 4 additions & 21 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ 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>()

/**
* @param recordDeclaration that is instantiated by a template containing parameterizedtypes
Expand Down Expand Up @@ -197,26 +197,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 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 @@ -88,23 +88,9 @@ class ResolveCallExpressionAmbiguityPass(ctx: TranslationContext) : TranslationU
// Check, if this is cast is really a construct expression (if the language supports
// functional-constructs)
if (language is HasFunctionStyleConstruction) {
// Make sure, we do not accidentally "construct" primitive types
if (language.builtInTypes.contains(callee.name.toString()) == true) {
return
}

val fqn =
if (callee.name.parent == null) {
scopeManager.currentNamespace.fqn(
callee.name.localName,
delimiter = callee.name.delimiter
)
} else {
callee.name
}

// Check for our type. We are only interested in object types
val type = typeManager.lookupResolvedType(fqn)
var type =
scopeManager.findTypeWithNameAndScope(callee.name, callee.language, callee.scope)
if (type is ObjectType) {
walker.replaceCallWithConstruct(type, parent, call)
}
Expand All @@ -121,27 +107,11 @@ class ResolveCallExpressionAmbiguityPass(ctx: TranslationContext) : TranslationU
callee = callee.input
}

// First, check if this is a built-in type
var builtInType = language.getSimpleTypeOf(callee.name)
if (builtInType != null) {
walker.replaceCallWithCast(builtInType, parent, call, false)
} else {
// If not, then this could still refer to an existing type. We need to make sure
// that we take the current namespace into account
val fqn =
if (callee.name.parent == null) {
scopeManager.currentNamespace.fqn(
callee.name.localName,
delimiter = callee.name.delimiter
)
} else {
callee.name
}

val type = typeManager.lookupResolvedType(fqn)
if (type != null) {
walker.replaceCallWithCast(type, parent, call, pointer)
}
// Check if it is type and replace the call
var type =
scopeManager.findTypeWithNameAndScope(callee.name, callee.language, callee.scope)
if (type != null) {
walker.replaceCallWithCast(type, parent, call, false)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,10 +989,14 @@ fun TranslationContext.tryNamespaceInference(
/**
* Tries to infer a [RecordDeclaration] from an unresolved [Type]. This will return `null`, if
* inference was not possible, or if it was turned off in the [InferenceConfiguration].
*
* If [updateType] is set to true, also the [ObjectType.recordDeclaration] is adjusted. This is only
* needed if we call this function in the [SymbolResolver] (and not in the [TypeResolver]).
*/
fun TranslationContext.tryRecordInference(
type: Type,
locationHint: Node? = null
locationHint: Node? = null,
updateType: Boolean = false,
): RecordDeclaration? {
val kind =
if (type.language is HasStructs) {
Expand Down Expand Up @@ -1032,7 +1036,7 @@ fun TranslationContext.tryRecordInference(

// update the type's record. Because types are only unique per scope, we potentially need to
// update multiple type nodes, i.e., all type nodes whose FQN match the inferred record
if (record != null) {
if (updateType && record != null) {
typeManager.firstOrderTypes
.filter { it.name == record.name }
.forEach { it.recordDeclaration = record }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,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 de11e5f

Please sign in to comment.