From f1fe26b9bd1991f81736f44d335effc104a86a24 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 3 Oct 2024 01:08:30 +0200 Subject: [PATCH] Create one `Type` object per type reference Fixes #1768 Fixes #1770 --- .../de/fraunhofer/aisec/cpg/TypeManager.kt | 50 ++++++------------- .../fraunhofer/aisec/cpg/graph/TypeBuilder.kt | 38 ++++---------- .../ResolveCallExpressionAmbiguityPass.kt | 21 ++++++++ .../de/fraunhofer/aisec/cpg/test/TestUtils.kt | 4 +- .../frontends/cxx/CXXLanguageFrontendTest.kt | 5 +- .../aisec/cpg_vis_neo4j/Application.kt | 4 +- 6 files changed, 51 insertions(+), 71 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt index 3069f74195..9d85fb2e3a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt @@ -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 @@ -57,8 +60,14 @@ class TypeManager { MutableMap> = ConcurrentHashMap() - val firstOrderTypes: MutableSet = ConcurrentHashMap.newKeySet() - val secondOrderTypes: MutableSet = ConcurrentHashMap.newKeySet() + val firstOrderTypes = mutableListOf() + val secondOrderTypes = mutableListOf() + + /** + * 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() /** * @param recordDeclaration that is instantiated by a template containing parameterizedtypes @@ -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 @@ -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? = 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)] } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt index a0a0fde85a..10f318283f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt @@ -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) } /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ResolveCallExpressionAmbiguityPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ResolveCallExpressionAmbiguityPass.kt index ecd614701e..d47a73606a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ResolveCallExpressionAmbiguityPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ResolveCallExpressionAmbiguityPass.kt @@ -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 } diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt index 8be47cdcd3..fd0c47fa9c 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt @@ -318,7 +318,7 @@ fun assertLiteralValue(expected: T, expr: Expression?, message: Strin assertEquals(expected, assertIs>(expr).value, message) } -fun ContextProvider.assertResolvedType(fqn: String, generics: List? = null): Type { - var type = ctx?.typeManager?.lookupResolvedType(fqn, generics) +fun ContextProvider.assertResolvedType(fqn: String): Type { + var type = ctx?.typeManager?.lookupResolvedType(fqn) return assertNotNull(type) } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt index 3e1b661e95..085febc7a4 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt @@ -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() diff --git a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt index 1ae5207abd..8afdd6dbdd 100644 --- a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt +++ b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg_vis_neo4j/Application.kt @@ -520,8 +520,8 @@ class Application : Callable { if (!noDefaultPasses) { translationConfiguration.defaultPasses() - translationConfiguration.registerPass() - translationConfiguration.registerPass() + // translationConfiguration.registerPass() + // translationConfiguration.registerPass() } if (customPasses != "DEFAULT") { val pieces = customPasses.split(",")