diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/FlowQueriesTest.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/FlowQueriesTest.kt index ef99ae341e4..7332e1417ce 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/FlowQueriesTest.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/FlowQueriesTest.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.testcases import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.array import de.fraunhofer.aisec.cpg.graph.builder.assign import de.fraunhofer.aisec.cpg.graph.builder.body @@ -61,7 +62,7 @@ class FlowQueriesTest { .defaultPasses() .registerPass() .registerPass() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -107,7 +108,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -143,7 +144,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -184,7 +185,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -226,7 +227,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -260,7 +261,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -298,7 +299,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -337,7 +338,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -373,7 +374,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -414,7 +415,7 @@ class FlowQueriesTest { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt index a3c0e3ec02b..3e349250481 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/OrderingTests.kt @@ -25,12 +25,9 @@ */ package de.fraunhofer.aisec.cpg.testcases -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TestLanguage -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.array import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.passes.UnreachableEOGPass @@ -42,7 +39,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerPass() .build() ) = @@ -240,7 +237,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerPass() .build() ) = @@ -633,9 +630,3 @@ class GraphExamples { } } } - -fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) - val language = config.languages.filterIsInstance().first() - return TestLanguageFrontend(language.namespaceDelimiter, language, ctx) -} diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Passes.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Passes.kt index c639e3ec830..4edff74f26b 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Passes.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Passes.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.testcases import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.passes.UnreachableEOGPass @@ -37,7 +38,7 @@ class Passes { TranslationConfiguration.builder() .defaultPasses() .registerPass() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt index c8bf7a859ad..3965634a7a5 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/Query.kt @@ -28,6 +28,7 @@ package de.fraunhofer.aisec.cpg.testcases import de.fraunhofer.aisec.cpg.InferenceConfiguration import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.array import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.newNewArrayExpression @@ -39,7 +40,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -97,7 +98,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .inferenceConfiguration(InferenceConfiguration.builder().enabled(false).build()) .build() ) = @@ -173,7 +174,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder().inferFunctions(false).build() ) @@ -251,7 +252,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -328,7 +329,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -388,7 +389,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -436,7 +437,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -498,7 +499,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -546,7 +547,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -571,7 +572,7 @@ class Query { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt index 627dac89af2..2cd4db85142 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/ValueEvaluationTests.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.testcases import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.passes.UnreachableEOGPass @@ -37,7 +38,7 @@ class ValueEvaluationTests { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerPass() .build() ) = @@ -99,7 +100,7 @@ class ValueEvaluationTests { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerPass() .build() ) = @@ -141,7 +142,7 @@ class ValueEvaluationTests { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerPass() .build() ) = @@ -246,7 +247,7 @@ class ValueEvaluationTests { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerPass() .build() ) = diff --git a/cpg-console/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/GraphExamples.kt b/cpg-console/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/GraphExamples.kt index 729b0c9ea81..be44f6ec4a6 100644 --- a/cpg-console/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/GraphExamples.kt +++ b/cpg-console/src/test/kotlin/de/fraunhofer/aisec/cpg/testcases/GraphExamples.kt @@ -25,12 +25,10 @@ */ package de.fraunhofer.aisec.cpg.testcases -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TestLanguage -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.TestLanguageWithColon +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.array import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.newNewArrayExpression @@ -42,7 +40,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -103,7 +101,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage("::")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -152,11 +150,5 @@ class GraphExamples { } } } - - fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) - val language = config.languages.filterIsInstance().first() - return TestLanguageFrontend(language.namespaceDelimiter, language, ctx) - } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt index 43ff55b189e..264358a4902 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt @@ -28,6 +28,7 @@ package de.fraunhofer.aisec.cpg import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.annotation.JsonSerialize +import de.fraunhofer.aisec.cpg.TranslationContext.EmptyTranslationContext import de.fraunhofer.aisec.cpg.TranslationResult.Companion.DEFAULT_APPLICATION_NAME import de.fraunhofer.aisec.cpg.frontends.CompilationDatabase import de.fraunhofer.aisec.cpg.frontends.KClassSerializer @@ -43,9 +44,8 @@ import java.io.File import java.nio.file.Path import java.util.* import kotlin.reflect.KClass -import kotlin.reflect.full.createInstance import kotlin.reflect.full.findAnnotations -import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.full.isSubclassOf import org.apache.commons.lang3.builder.ToStringBuilder import org.apache.commons.lang3.builder.ToStringStyle import org.slf4j.LoggerFactory @@ -109,7 +109,7 @@ private constructor( Map>, KClass>>, KClass>>, /** This list contains the files with function summaries which should be considered. */ val functionSummaries: DFGFunctionSummaries, - languages: List>, + languages: List>>, codeInNodes: Boolean, processAnnotations: Boolean, disableCleanup: Boolean, @@ -127,7 +127,7 @@ private constructor( val exclusionPatternsByRegex: List, ) { /** This list contains all languages which we want to translate. */ - val languages: List> + @JsonIgnore val languages: List>> /** * Switch off cleaning up TypeManager memory after analysis. @@ -197,7 +197,7 @@ private constructor( val passConfigurations: Map>, PassConfiguration> init { - registeredPasses = passes + this.registeredPasses = passes this.languages = languages // Make sure to init this AFTER sourceLocations has been set this.codeInNodes = codeInNodes @@ -239,7 +239,7 @@ private constructor( */ class Builder { private var softwareComponents: MutableMap> = HashMap() - private val languages = mutableListOf>() + private val languages = mutableListOf>>() private var topLevels = mutableMapOf() private var debugParser = false private var failOnError = false @@ -447,16 +447,18 @@ private constructor( /** Registers an additional [Language]. */ fun registerLanguage(language: Language<*>): Builder { - languages.add(language) - log.info( - "Registered language frontend '${language::class.simpleName}' for following file types: ${language.fileExtensions}" - ) + throw UnsupportedOperationException("Use registerLanguage(className: String) instead") + } + + /** Registers an additional [Language] by its [KClass]. */ + fun > registerLanguage(clazz: KClass): Builder { + languages.add(clazz) return this } /** Registers an additional [Language]. */ inline fun > registerLanguage(): Builder { - T::class.primaryConstructor?.call()?.let { registerLanguage(it) } + registerLanguage(T::class) return this } @@ -501,26 +503,26 @@ private constructor( @Throws(ConfigurationException::class) fun registerLanguage(className: String): Builder { try { - val loadedClass = Class.forName(className).kotlin.createInstance() as? Language<*> + @Suppress("UNCHECKED_CAST") + val loadedClass = Class.forName(className).kotlin as? KClass> if (loadedClass != null) { registerLanguage(loadedClass) } else throw ConfigurationException( "Failed casting supposed language class '$className'. It does not seem to be an implementation of Language<*>." ) - } catch (e: Exception) { + } catch (_: Exception) { throw ConfigurationException( "Failed to load and instantiate class from FQN '$className'. Possible causes of this error:\n" + - "- the given class is unavailable in the class path\n" + - "- the given class does not have a single no-arg constructor\n" + "- the given class is unavailable in the class path\n" ) } return this } /** Unregisters a registered [de.fraunhofer.aisec.cpg.frontends.Language]. */ - fun unregisterLanguage(language: Class?>): Builder { - languages.removeIf { obj: Language<*>? -> language.isInstance(obj) } + fun unregisterLanguage(language: KClass>): Builder { + languages.removeIf { it.isSubclassOf(language) } return this } @@ -529,13 +531,16 @@ private constructor( * * This will register * - [TypeHierarchyResolver] - * - [JavaImportResolver] * - [SymbolResolver] + * - [ImportResolver] * - [DFGPass] * - [EvaluationOrderGraphPass] + * - [DynamicInvokeResolver] * - [TypeResolver] * - [ControlFlowSensitiveDFGPass] * - [FilenameMapper] + * - [ResolveCallExpressionAmbiguityPass] + * - [ResolveMemberExpressionAmbiguityPass] * * to be executed in the order specified by their annotations. */ @@ -567,7 +572,7 @@ private constructor( return } - for (frontend in languages.map(Language<*>::frontend)) { + for (frontend in languages.map { it.frontend }) { val extraPasses = frontend.findAnnotations() if (extraPasses.isNotEmpty()) { for (p in extraPasses) { @@ -582,7 +587,7 @@ private constructor( } private fun registerReplacedPasses() { - for (frontend in languages.map(Language<*>::frontend)) { + for (frontend in languages.map { it.frontend }) { val replacedPasses = frontend.findAnnotations() if (replacedPasses.isNotEmpty()) { for (p in replacedPasses) { @@ -721,3 +726,20 @@ private constructor( } } } + +/** + * Returns the frontend class of a language. Since the [Language.frontend] is a property of a + * language, we need to create a temporary object of the language class to access it (using + * [EmptyTranslationContext]). + */ +val KClass>.frontend: KClass> + get() { + // Instantiate a temporary object of the language class + val instance = + constructors.firstOrNull()?.call(EmptyTranslationContext) + ?: throw IllegalArgumentException( + "Could not instantiate temporary object of language class ${this.simpleName}" + ) + + return instance.frontend + } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt index e34493f6622..616ed09ee71 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt @@ -26,17 +26,20 @@ package de.fraunhofer.aisec.cpg import de.fraunhofer.aisec.cpg.TranslationManager.AdditionalSource +import de.fraunhofer.aisec.cpg.TranslationManager.Companion.log +import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.persistence.DoNotPersist +import java.io.File /** * The translation context holds all necessary managers and configurations needed during the * translation process. */ @DoNotPersist -class TranslationContext( +open class TranslationContext( /** The configuration for this translation. */ - val config: TranslationConfiguration, + val config: TranslationConfiguration = TranslationConfiguration.builder().build(), /** * The scope manager which comprises the complete translation result. In case of sequential @@ -44,13 +47,13 @@ class TranslationContext( * of sequential parsing, individual scope managers will be passed to each language frontend * (through individual contexts) and then finally merged into a final one. */ - val scopeManager: ScopeManager, + val scopeManager: ScopeManager = ScopeManager(), /** * The type manager is responsible for managing type information. Currently, we have one * instance of a [TypeManager] for the overall [TranslationResult]. */ - val typeManager: TypeManager, + val typeManager: TypeManager = TypeManager(), /** * Some frontends need access to the current [Component] we are currently processing. Note: for @@ -75,4 +78,62 @@ class TranslationContext( * import statements of the analyzed code and deciding which sources contain relevant symbols. */ var importedSources: MutableSet = mutableSetOf(), -) +) { + /** + * The set of languages available in this translation context. We store this information here + * because we want to ensure that we only have one instance of a language per + * [TranslationResult]. + */ + val availableLanguages: List> = createAvailableLanguages() + + /** + * Returns the single available language of the given type [T] or null if no such language is + * available. + */ + inline fun > availableLanguage(): T? { + return availableLanguages.filterIsInstance().singleOrNull() + } + + /** + * Creates a list of available [Language] nodes based on the registered languages in the + * [TranslationConfiguration.languages]. + */ + private fun createAvailableLanguages(): List> { + // We need to initialize the available languages out of the context + return config.languages.mapNotNull { + val language = it.constructors.firstOrNull()?.call(this) + if (language == null) { + log.error("Could not create language instance for {}", it.simpleName) + } + language + } + } + + /** + * This extension function returns an appropriate [Language] for this [File] based on the + * [availableLanguages], which in turn are based on the registered file extensions of + * [TranslationConfiguration.languages]. It will emit a warning if multiple languages are + * registered for the same extension (and the first one that was registered will be returned). + */ + internal val File.language: Language<*>? + get() { + val languages = availableLanguages.filter { it.handlesFile(this) } + if (languages.size > 1) { + TranslationManager.Companion.log.warn( + "Multiple languages match for file extension ${this.extension}, the first registered language will be used." + ) + } + return languages.firstOrNull() + } + + /** + * This is a special [TranslationContext] that is used when no translation context is available. + * This MUST only be used internally. It primarily serves one use-case: We need to gather + * certain metadata information about a [Language] inside the [TranslationConfiguration]. + * Therefore, we need to instantiate the language, but we do not have a translation context + * available at that point. This is why we use this special [EmptyTranslationContext] in the + * [TranslationConfiguration] to instantiate the language. This language instance is then not + * used after the config is built. + */ + internal object EmptyTranslationContext : TranslationContext() +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt index 7987aeae9c3..d15ccb4d2ad 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg -import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.SupportsParallelParsing import de.fraunhofer.aisec.cpg.frontends.TranslationException @@ -142,7 +141,6 @@ private constructor( result: TranslationResult, ): Set> { val usedFrontends = mutableSetOf>() - val usedLanguages = mutableSetOf>() // If loadIncludes is active, the files stored in the include paths are made available for // conditional analysis by providing them to the frontends over the @@ -184,13 +182,14 @@ private constructor( .toList() files } else { - val frontendClass = file.language?.frontend - + // Retrieve the file's language based on the available languages of the + // result + val language = with(ctx) { file.language } + val frontendClass = language?.frontend val supportsParallelParsing = - file.language - ?.frontend - ?.findAnnotation() - ?.supported ?: true + frontendClass?.findAnnotation()?.supported != + false + // By default, the frontends support parallel parsing. But the // SupportsParallelParsing annotation can be set to false and force // to disable it. @@ -264,13 +263,8 @@ private constructor( parseSequentially(component, result, ctx, sourceLocations) } ) - // Collects all used languages used in the main analysis code - usedLanguages.addAll(sourceLocations.mapNotNull { it.language }.toSet()) } - // Adds all languages provided as additional sources that may be relevant in the main code - usedLanguages.addAll(ctx.additionalSources.mapNotNull { it.relative.language }.toSet()) - // A set of processed files from [TranslationContext.additionalSources] that is used as // negative to the // worklist in ctx.importedSources it is used to filter out files that were already @@ -375,7 +369,7 @@ private constructor( val future = CompletableFuture.supplyAsync { try { - return@supplyAsync parse(component, ctx, sourceLocation) + return@supplyAsync parse(component, ctx, globalCtx, sourceLocation) } catch (e: TranslationException) { throw RuntimeException("Error parsing $sourceLocation", e) } @@ -439,7 +433,7 @@ private constructor( for (sourceLocation in sourceLocations) { ctx.currentComponent = component - val f = parse(component, ctx, sourceLocation) + val f = parse(component, ctx, ctx, sourceLocation) if (f != null) { handleCompletion(result, usedFrontends, sourceLocation, f) } @@ -468,13 +462,14 @@ private constructor( private fun parse( component: Component, ctx: TranslationContext, + globalCtx: TranslationContext, sourceLocation: File, ): LanguageFrontend<*, *>? { log.info("Parsing {}", sourceLocation.absolutePath) var frontend: LanguageFrontend<*, *>? = null try { - frontend = getFrontend(sourceLocation, ctx) + frontend = getFrontend(sourceLocation, ctx, globalCtx) if (frontend == null) { log.error("Found no parser frontend for ${sourceLocation.name}") @@ -496,8 +491,17 @@ private constructor( return frontend } - private fun getFrontend(file: File, ctx: TranslationContext): LanguageFrontend<*, *>? { - val language = file.language + private fun getFrontend( + file: File, + ctx: TranslationContext, + globalCtx: TranslationContext, + ): LanguageFrontend<*, *>? { + // Retrieve the languages based on the global ctx, so that all frontends share the same + // language instances. + // + // Once we address https://github.com/Fraunhofer-AISEC/cpg/issues/2109 we can remove the + // globalCtx parameter + val language = with(globalCtx) { file.language } return if (language != null) { try { @@ -525,23 +529,6 @@ private constructor( } else null } - /** - * This extension function returns an appropriate [Language] for this [File] based on the - * registered file extensions of [TranslationConfiguration.languages]. It will emit a warning if - * multiple languages are registered for the same extension (and the first one that was - * registered will be returned). - */ - private val File.language: Language<*>? - get() { - val languages = config.languages.filter { it.handlesFile(this) } - if (languages.size > 1) { - log.warn( - "Multiple languages match for file extension ${this.extension}, the first registered language will be used." - ) - } - return languages.firstOrNull() - } - /** * An additional source file that was originally part of [TranslationConfiguration.includePaths] * and that is potentially included in the analysis. @@ -573,7 +560,7 @@ private constructor( } companion object { - private val log = LoggerFactory.getLogger(TranslationManager::class.java) + internal val log = LoggerFactory.getLogger(TranslationManager::class.java) @JvmStatic fun builder(): Builder { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index 47530e626a0..50932d22036 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -89,7 +89,7 @@ data class ImplicitCast(override var depthDistance: Int) : CastResult(depthDista * persisted in the final graph (database) and each node links to its corresponding language using * the [Node.language] property. */ -abstract class Language> : Node() { +abstract class Language> : Node { /** The file extensions without the dot */ abstract val fileExtensions: List @@ -123,12 +123,16 @@ abstract class Language> : Node() { /** All operators which perform a simple assignment from the rhs to the lhs. */ open val simpleAssignmentOperators: Set = setOf("=") + constructor(ctx: TranslationContext? = null) : super() { + this.ctx = ctx + } + /** * Creates a new [LanguageFrontend] object to parse the language. It requires the * [TranslationContext], which holds the necessary managers. */ open fun newFrontend(ctx: TranslationContext): T { - return this.frontend.primaryConstructor?.call(this, ctx) + return this.frontend.primaryConstructor?.call(ctx, this) ?: throw TranslationException("could not instantiate language frontend") } @@ -485,7 +489,8 @@ object NoLanguage : Language() { * * @property languages A list of languages that are part of this composite language definition. */ -class MultipleLanguages(val languages: Set>) : Language() { +class MultipleLanguages(ctx: TranslationContext, val languages: Set>) : + Language(ctx) { override val fileExtensions = languages.flatMap { it.fileExtensions } override val frontend: KClass = Nothing::class override val builtInTypes: Map = mapOf() @@ -501,7 +506,7 @@ fun Node.multiLanguage(): Language<*> { return if (languages.size == 1) { languages.single() } else if (languages.size > 1) { - MultipleLanguages(languages = languages) + MultipleLanguages(ctx!!, languages = languages) } else { UnknownLanguage } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt index e6e97aa12db..09050a6d160 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt @@ -45,15 +45,18 @@ import org.slf4j.LoggerFactory * [GitHub wiki page](https://github.com/Fraunhofer-AISEC/cpg/wiki/Language-Frontends). */ abstract class LanguageFrontend( - /** The language this frontend works for. */ - override val language: Language>, - /** * The translation context, which contains all necessary managers used in this frontend parsing - * process. Note, that different contexts could passed to frontends, e.g., in parallel parsing - * to supply different managers to different frontends. + * process. Note, that different contexts could be passed to frontends, e.g., in parallel + * parsing to supply different managers to different frontends. + * + * TODO(oxisto): once we address https://github.com/Fraunhofer-AISEC/cpg/issues/2109 we can + * remove this parameter and use the context from the language */ final override var ctx: TranslationContext, + + /** The language this frontend works for. */ + override val language: Language>, ) : ProcessedListener(), CodeAndLocationProvider, diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt index 56e8bde64c2..7c34879b3a0 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt @@ -40,7 +40,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull -class TestFileLanguage : TestLanguage() { +class TestFileLanguage(ctx: TranslationContext) : TestLanguage(ctx) { override val fileExtensions: List get() = listOf("file") @@ -50,14 +50,14 @@ class TestFileLanguage : TestLanguage() { /** Just a test frontend that "reads" a file and returns an empty [TranslationUnitDeclaration]. */ class TestFileLanguageFrontend( - language: Language = TestFileLanguage(), ctx: TranslationContext = TranslationContext( TranslationConfiguration.builder().build(), ScopeManager(), TypeManager(), ), -) : TestLanguageFrontend("::", language, ctx) { + language: Language = TestFileLanguage(ctx), +) : TestLanguageFrontend(ctx, language) { override fun parse(file: File): TranslationUnitDeclaration { return newTranslationUnitDeclaration(file.name) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt index abdeae6e7bc..e66dd66a3c8 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/GraphExamples.kt @@ -25,9 +25,10 @@ */ package de.fraunhofer.aisec.cpg +import de.fraunhofer.aisec.cpg.frontends.ClassTestLanguage import de.fraunhofer.aisec.cpg.frontends.StructTestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguage -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.autoType import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.newInitializerListExpression @@ -43,7 +44,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -70,7 +71,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -113,7 +114,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -156,7 +157,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -207,7 +208,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -252,7 +253,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -282,17 +283,11 @@ class GraphExamples { } } - fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) - val language = config.languages.filterIsInstance().first() - return TestLanguageFrontend(language.namespaceDelimiter, language, ctx) - } - fun getInferenceRecordPtr( config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(StructTestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder().inferRecords(true).build() ) @@ -322,7 +317,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(StructTestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder().inferRecords(true).build() ) @@ -348,7 +343,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(StructTestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder() .inferRecords(true) @@ -376,7 +371,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(StructTestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder() .inferRecords(true) @@ -399,7 +394,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(StructTestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder() .inferRecords(true) @@ -420,7 +415,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(StructTestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder() .inferRecords(true) @@ -448,7 +443,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -499,7 +494,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -521,7 +516,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -543,7 +538,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -619,7 +614,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -705,7 +700,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -777,7 +772,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -851,7 +846,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -889,7 +884,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -970,7 +965,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1021,7 +1016,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1047,7 +1042,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1093,7 +1088,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1140,7 +1135,7 @@ class GraphExamples { TranslationConfiguration.builder() .defaultPasses() .useParallelPasses(true) - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1203,7 +1198,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1319,7 +1314,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1377,7 +1372,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1448,7 +1443,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { @@ -1486,7 +1481,7 @@ class GraphExamples { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt index c3ff0fe22a8..04b33d726d4 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt @@ -25,11 +25,11 @@ */ package de.fraunhofer.aisec.cpg.enhancements -import de.fraunhofer.aisec.cpg.GraphExamples import de.fraunhofer.aisec.cpg.InferenceConfiguration import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.edges.flows.CallingContextIn @@ -68,10 +68,10 @@ class DFGFunctionSummariesTest { @Test fun testMatching() { val code = - GraphExamples.testFrontend( + testFrontend( TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerFunctionSummaries(File("src/test/resources/function-dfg2.yml")) .inferenceConfiguration( InferenceConfiguration.builder() @@ -367,7 +367,7 @@ class DFGFunctionSummariesTest { ): TranslationResult { val config = TranslationConfiguration.builder() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .registerFunctionSummaries(File("src/test/resources/function-dfg.yml")) .inferenceConfiguration( InferenceConfiguration.builder() @@ -385,7 +385,7 @@ class DFGFunctionSummariesTest { return a; } */ - return GraphExamples.testFrontend(config).build { + return testFrontend(config).build { translationResult { translationUnit("DfgInferredCall.c") { function("main", t("int")) { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt index c48eb931751..ee090563a15 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt @@ -74,24 +74,26 @@ class LanguageTest { @Test fun testMultiLanguage() { - class OtherLanguage : TestLanguage() + class OtherLanguage(ctx: TranslationContext) : TestLanguage(ctx) - val otherLanguage = OtherLanguage() - val testLanguage = TestLanguage() + val ctx = + TranslationContext( + config = TranslationConfiguration.builder().build(), + scopeManager = ScopeManager(), + typeManager = TypeManager(), + ) + + val otherLanguage = OtherLanguage(ctx) + val testLanguage = TestLanguage(ctx) val result = TranslationResult( translationManager = TranslationManager.builder().build(), - finalCtx = - TranslationContext( - config = TranslationConfiguration.builder().build(), - scopeManager = ScopeManager(), - typeManager = TypeManager(), - ), + finalCtx = ctx, ) val comp1 = - with(TestLanguageFrontend("::", language = otherLanguage)) { + with(TestLanguageFrontend(language = otherLanguage)) { val tu = newTranslationUnitDeclaration("tu-language-other") val comp = Component() comp.ctx = this.ctx @@ -101,7 +103,7 @@ class LanguageTest { result.components += comp1 val comp2 = - with(TestLanguageFrontend("::", language = testLanguage)) { + with(TestLanguageFrontend(language = testLanguage)) { val tu = newTranslationUnitDeclaration("tu-language-test") val comp = Component() comp.ctx = this.ctx diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt index 3905ba85938..e4ea1671b9b 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/FluentTest.kt @@ -26,7 +26,7 @@ package de.fraunhofer.aisec.cpg.graph import de.fraunhofer.aisec.cpg.frontends.TestLanguage -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.TestLanguageWithColon import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration @@ -50,31 +50,32 @@ class FluentTest { @Test fun test() { val result = - TestLanguageFrontend().build { - translationResult { - translationUnit("file.cpp") { - function("main", t("int")) { - param("argc", t("int")) - body { - declare { variable("a", t("short")) { literal(1) } } - ifStmt { - condition { ref("argc") eq literal(1) } - thenStmt { call("printf") { literal("then") } } - elseIf { + testFrontend { it.registerLanguage() } + .build { + translationResult { + translationUnit("file.cpp") { + function("main", t("int")) { + param("argc", t("int")) + body { + declare { variable("a", t("short")) { literal(1) } } + ifStmt { condition { ref("argc") eq literal(1) } - thenStmt { call("printf") { literal("elseIf") } } - elseStmt { call("printf") { literal("else") } } + thenStmt { call("printf") { literal("then") } } + elseIf { + condition { ref("argc") eq literal(1) } + thenStmt { call("printf") { literal("elseIf") } } + elseStmt { call("printf") { literal("else") } } + } } - } - declare { variable("some", t("SomeClass")) } - call("do") { call("some.func") } + declare { variable("some", t("SomeClass")) } + call("do") { call("some.func") } - returnStmt { ref("a") + literal(2) } + returnStmt { ref("a") + literal(2) } + } } } } } - } // Let's assert that we did this correctly val main = result.functions["main"] @@ -189,7 +190,7 @@ class FluentTest { fun testCollectionComprehensions() { val result = testFrontend { - it.registerLanguage(TestLanguage(".")) + it.registerLanguage() it.defaultPasses() it.registerPass() it.registerPass() @@ -242,7 +243,7 @@ class FluentTest { fun testCollectionComprehensionsWithDeclaration() { val result = testFrontend { - it.registerLanguage(TestLanguage(".")) + it.registerLanguage() it.defaultPasses() it.registerPass() it.registerPass() @@ -296,7 +297,7 @@ class FluentTest { fun testCollectionComprehensionsWithTwoDeclarations() { val result = testFrontend { - it.registerLanguage(TestLanguage(".")) + it.registerLanguage() it.defaultPasses() it.registerPass() it.registerPass() diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/NameTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/NameTest.kt index d811ea9c097..708f0c6667a 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/NameTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/NameTest.kt @@ -25,7 +25,8 @@ */ package de.fraunhofer.aisec.cpg.graph -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.TestLanguageWithColon +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.test.* import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -72,7 +73,7 @@ internal class NameTest { @Test fun testParentNames() { - with(TestLanguageFrontend()) { + with(testFrontend { it.registerLanguage() }) { val tu = newTranslationUnitDeclaration("file.extension") this.scopeManager.resetToGlobal(tu) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt index 110fae5cab7..d1fa55bf458 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ShortcutsTest.kt @@ -276,7 +276,7 @@ class ShortcutsTest { GraphExamples.getShortcutClass( TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowExpressionTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowExpressionTest.kt index b87e146b877..90fc30c667c 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowExpressionTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ThrowExpressionTest.kt @@ -25,9 +25,9 @@ */ package de.fraunhofer.aisec.cpg.graph -import de.fraunhofer.aisec.cpg.GraphExamples.Companion.testFrontend import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.statements.ThrowExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block @@ -42,7 +42,7 @@ class ThrowExpressionTest { testFrontend( TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) .build { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt index f9eb9cbed4a..41ad33d50e0 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt @@ -25,9 +25,9 @@ */ package de.fraunhofer.aisec.cpg.helpers -import de.fraunhofer.aisec.cpg.GraphExamples.Companion.testFrontend import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.applyWithScope @@ -58,7 +58,7 @@ internal class ExtensionsTest : BaseTest() { config: TranslationConfiguration = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .build() ) = testFrontend(config).build { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalkerTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalkerTest.kt index cca2f0d13c4..2725a7b35ec 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalkerTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalkerTest.kt @@ -63,7 +63,7 @@ internal class SubgraphWalkerTest : BaseTest() { .debugParser(true) .failOnError(true) .useParallelFrontends(true) - .registerLanguage(TestLanguage(".")) + .registerLanguage() .defaultPasses() .build() ) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPassTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPassTest.kt index eac598e55be..d901a42f66d 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPassTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPassTest.kt @@ -25,12 +25,9 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager -import de.fraunhofer.aisec.cpg.frontends.TestLanguage -import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.TestLanguageWithColon +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block @@ -116,16 +113,10 @@ class ControlDependenceGraphPassTest { } companion object { - fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) - val language = config.languages.filterIsInstance().first() - return TestLanguageFrontend(language.namespaceDelimiter, language, ctx) - } - fun getIfTest() = testFrontend( TranslationConfiguration.builder() - .registerLanguage(TestLanguage("::")) + .registerLanguage() .defaultPasses() .registerPass() .build() @@ -161,7 +152,7 @@ class ControlDependenceGraphPassTest { fun getForEachTest() = testFrontend( TranslationConfiguration.builder() - .registerLanguage(TestLanguage("::")) + .registerLanguage() .defaultPasses() .registerPass() .build() diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt index 4d8531a3983..71845d72344 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPassTest.kt @@ -27,7 +27,8 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.GraphExamples import de.fraunhofer.aisec.cpg.TranslationConfiguration -import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.TestLanguageWithColon +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration @@ -281,9 +282,9 @@ class ControlFlowSensitiveDFGPassTest { } fun getForEachTest() = - ControlDependenceGraphPassTest.testFrontend( + testFrontend( TranslationConfiguration.builder() - .registerLanguage(TestLanguage("::")) + .registerLanguage() .defaultPasses() .registerPass() .configurePass( diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt index 5036c7460a1..8efb26ef240 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt @@ -47,13 +47,12 @@ class ImportResolverTest { fun testImportOrderResolve() { val frontend = TestLanguageFrontend( - namespaceDelimiter = ".", ctx = TranslationContext( TranslationConfiguration.builder().defaultPasses().build(), ScopeManager(), TypeManager(), - ), + ) ) var result = frontend.build { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt index d4cdb803f1e..f57cd90cf83 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ProgramDependenceGraphPassTest.kt @@ -108,7 +108,7 @@ class ProgramDependenceGraphPassTest { private fun getIfTest() = testFrontend { - it.registerLanguage(TestLanguage(".")) + it.registerLanguage() it.defaultPasses() it.registerPass() it.registerPass() @@ -144,7 +144,7 @@ class ProgramDependenceGraphPassTest { private fun getWhileLoopTest() = testFrontend { - it.registerLanguage(TestLanguage(".")) + it.registerLanguage() it.defaultPasses() it.registerPass() it.registerPass() diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt index 13c1987c33b..9d49cbcc8d9 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt @@ -25,8 +25,10 @@ */ package de.fraunhofer.aisec.cpg.passes +import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.StructTestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend @@ -39,7 +41,7 @@ class ReplaceTest { @ReplacePass(EvaluationOrderGraphPass::class, ReplaceTestLanguage::class, ReplacedPass::class) class ReplaceTestLanguageFrontend : TestLanguageFrontend() - class ReplaceTestLanguage : TestLanguage() { + class ReplaceTestLanguage(ctx: TranslationContext) : TestLanguage(ctx) { override val frontend: KClass get() = ReplaceTestLanguageFrontend::class @@ -54,6 +56,7 @@ class ReplaceTest { fun testReplaceAnnotation() { val config = TranslationConfiguration.builder().registerLanguage().build() + val ctx = TranslationContext(config, ScopeManager(), TypeManager()) assertContains(config.replacedPasses.values, ReplacedPass::class) assertContains( @@ -62,7 +65,7 @@ class ReplaceTest { ) val cls = - checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(), config) + checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(ctx), config) assertEquals(ReplacedPass::class, cls) } @@ -73,6 +76,7 @@ class ReplaceTest { .replacePass() .replacePass() .build() + val ctx = TranslationContext(config, ScopeManager(), TypeManager()) assertContains(config.replacedPasses.values, ReplacedPass::class) assertContains( @@ -80,13 +84,13 @@ class ReplaceTest { Pair(EvaluationOrderGraphPass::class, StructTestLanguage::class), ) - var cls = checkForReplacement(EvaluationOrderGraphPass::class, TestLanguage(), config) + var cls = checkForReplacement(EvaluationOrderGraphPass::class, TestLanguage(ctx), config) assertEquals(EvaluationOrderGraphPass::class, cls) - cls = checkForReplacement(EvaluationOrderGraphPass::class, StructTestLanguage(), config) + cls = checkForReplacement(EvaluationOrderGraphPass::class, StructTestLanguage(ctx), config) assertEquals(ReplacedPass::class, cls) - cls = checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(), config) + cls = checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(ctx), config) assertEquals(ReplacedPass::class, cls) } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt index b47f2961582..572906dba93 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnresolvedDFGPassTest.kt @@ -25,11 +25,11 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.GraphExamples.Companion.testFrontend import de.fraunhofer.aisec.cpg.InferenceConfiguration import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.* import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration @@ -142,7 +142,7 @@ class UnresolvedDFGPassTest { val config = TranslationConfiguration.builder() .defaultPasses() - .registerLanguage(TestLanguage(".")) + .registerLanguage() .inferenceConfiguration( InferenceConfiguration.builder() .inferDfgForUnresolvedCalls(inferUnresolved) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt index 05ffd5b1669..1913f8a775d 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt @@ -26,8 +26,8 @@ package de.fraunhofer.aisec.cpg.passes.scopes import de.fraunhofer.aisec.cpg.* -import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.frontends.TestLanguageWithColon import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.scopes.NameScope import de.fraunhofer.aisec.cpg.test.* @@ -45,8 +45,9 @@ internal class ScopeManagerTest : BaseTest() { fun testMerge() { val tm = TypeManager() val s1 = ScopeManager() - val frontend1 = - TestLanguageFrontend("::", TestLanguage(), TranslationContext(config, s1, tm)) + val ctx = TranslationContext(config, s1, tm) + val language = TestLanguageWithColon(ctx) + val frontend1 = TestLanguageFrontend(ctx, language) s1.resetToGlobal(frontend1.newTranslationUnitDeclaration("f1.cpp", null)) with(frontend1) { // build a namespace declaration in f1.cpp with the namespace A @@ -58,8 +59,7 @@ internal class ScopeManagerTest : BaseTest() { s1.addDeclaration(namespaceA1) val s2 = ScopeManager() - val frontend2 = - TestLanguageFrontend("::", TestLanguage(), TranslationContext(config, s2, tm)) + val frontend2 = TestLanguageFrontend(TranslationContext(config, s2, tm), language) s2.resetToGlobal(frontend2.newTranslationUnitDeclaration("f1.cpp", null)) // and do the same in the other file @@ -114,8 +114,8 @@ internal class ScopeManagerTest : BaseTest() { @Test fun testScopeFQN() { val s = ScopeManager() - val frontend = - TestLanguageFrontend("::", TestLanguage(), TranslationContext(config, s, TypeManager())) + val ctx = TranslationContext(config, s, TypeManager()) + val frontend = TestLanguageFrontend(ctx, TestLanguageWithColon(ctx)) s.resetToGlobal(frontend.newTranslationUnitDeclaration("file.cpp", null)) with(frontend) { assertNull(s.currentNamespace) @@ -146,8 +146,7 @@ internal class ScopeManagerTest : BaseTest() { @Test fun testMatchesSignature() { val s = ScopeManager() - val frontend = - TestLanguageFrontend("::", TestLanguage(), TranslationContext(config, s, TypeManager())) + val frontend = TestLanguageFrontend(TranslationContext(config, s, TypeManager())) with(frontend) { val method = newMethodDeclaration("testMethod").apply { diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt index 4c634c8c64c..97cbb0e1ee1 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt @@ -36,13 +36,23 @@ import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File import java.util.function.Supplier import kotlin.reflect.KClass +import kotlin.test.assertNotNull + +/** + * This is a variant of the test language with `::` as a [namespaceDelimiter] to simulate languages + * like C++. + */ +open class TestLanguageWithColon(ctx: TranslationContext) : TestLanguage(ctx) { + override val namespaceDelimiter: String + get() = "::" +} /** * This is a test language that can be used for unit test, where we need a language but do not have * a specific one. */ -open class TestLanguage(final override var namespaceDelimiter: String = "::") : - Language(), HasImplicitReceiver { +open class TestLanguage(ctx: TranslationContext) : + Language(ctx), HasImplicitReceiver { override val fileExtensions: List = listOf() override val frontend: KClass = TestLanguageFrontend::class override val compoundAssignmentOperators = @@ -64,26 +74,45 @@ open class TestLanguage(final override var namespaceDelimiter: String = "::") : get() = "this" } -class StructTestLanguage(namespaceDelimiter: String = "::") : - TestLanguage(namespaceDelimiter), HasStructs, HasClasses, HasDefaultArguments +class ClassTestLanguage(ctx: TranslationContext) : + TestLanguage(ctx), HasClasses, HasDefaultArguments + +class StructTestLanguage(ctx: TranslationContext) : + TestLanguageWithColon(ctx), HasStructs, HasClasses, HasDefaultArguments +/** + * Creates a new [TestLanguageFrontend] with the given configuration [builder]. + * + * Note: This requires that the user registers at least one [TestLanguage], but it gives him the + * chance to configure a specific subclass of it, e.g., [TestLanguageWithColon]. + */ fun testFrontend(builder: (TranslationConfiguration.Builder) -> Unit): TestLanguageFrontend { var config = TranslationConfiguration.builder().also(builder).build() + return testFrontend(config) +} - var ctx: TranslationContext = TranslationContext(config, ScopeManager(), TypeManager()) - return TestLanguageFrontend(ctx = ctx) +/** + * Creates a new [TestLanguageFrontend] with the given [config]. + * + * Note: This requires that the user registers at least one [TestLanguage], but it gives him the + * chance to configure a specific subclass of it, e.g., [TestLanguageWithColon]. + */ +fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { + val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val language = ctx.availableLanguage() + assertNotNull(language) + return TestLanguageFrontend(ctx, language) } open class TestLanguageFrontend( - namespaceDelimiter: String = "::", - language: Language = TestLanguage(namespaceDelimiter), ctx: TranslationContext = TranslationContext( TranslationConfiguration.builder().build(), ScopeManager(), TypeManager(), ), -) : LanguageFrontend(language, ctx) { + language: Language = TestLanguage(ctx), +) : LanguageFrontend(ctx, language) { override fun parse(file: File): TranslationUnitDeclaration { TODO("Not yet implemented") } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt index 4a1c1498611..1732f500ea8 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import com.fasterxml.jackson.annotation.JsonIgnore +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass @@ -34,8 +35,8 @@ import org.neo4j.ogm.annotation.Transient const val CONST = "const" /** The C language. */ -open class CLanguage : - Language(), +open class CLanguage(ctx: TranslationContext) : + Language(ctx), HasStructs, HasFunctionPointers, HasQualifier, diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt index 767885ce8e4..5c48b142a62 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt @@ -47,8 +47,8 @@ import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The C++ language. */ -open class CPPLanguage : - CLanguage(), +open class CPPLanguage(ctx: TranslationContext) : + CLanguage(ctx), HasDefaultArguments, HasTemplates, HasStructs, diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt index f10ac733d46..c82cc383af8 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt @@ -81,8 +81,8 @@ import org.slf4j.LoggerFactory */ @RegisterExtraPass(DynamicInvokeResolver::class) @RegisterExtraPass(CXXExtraPass::class) -open class CXXLanguageFrontend(language: Language, ctx: TranslationContext) : - LanguageFrontend(language, ctx) { +open class CXXLanguageFrontend(ctx: TranslationContext, language: Language) : + LanguageFrontend(ctx, language) { /** * The dialect used by this language frontend, either [GCCLanguage] for C or [GPPLanguage] for diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt index 37bd37734fd..f7edc2329ec 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/ScopeManagerCXXTest.kt @@ -50,11 +50,8 @@ internal class ScopeManagerTest : BaseTest() { @Throws(TranslationException::class) fun testReplaceNode() { val scopeManager = ScopeManager() - val frontend = - CXXLanguageFrontend( - CPPLanguage(), - TranslationContext(config, scopeManager, TypeManager()), - ) + val ctx = TranslationContext(config, scopeManager, TypeManager()) + val frontend = CXXLanguageFrontend(ctx, CPPLanguage(ctx)) val tu = frontend.parse(File("src/test/resources/cxx/recordstmt.cpp")) val methods = tu.allChildren().filter { it !is ConstructorDeclaration } assertFalse(methods.isEmpty()) diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt index cde0cb26e19..9987d0431c6 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt @@ -93,6 +93,9 @@ internal class FunctionTemplateTest : BaseTest() { ) { it.registerLanguage() } + val language = result.finalCtx.availableLanguage() + assertNotNull(language) + // This test checks the structure of FunctionTemplates without the TemplateExpansionPass val functionTemplateDecl = result.allChildren()[0] @@ -103,9 +106,9 @@ internal class FunctionTemplateTest : BaseTest() { val typeParamDeclaration = typeParamDecls[0] assertEquals(typeParamDeclaration, functionTemplateDecl.parameters[0]) - val typeT = ParameterizedType("T", CPPLanguage()) - val intType = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) - val floatType = FloatingPointType("float", 32, CPPLanguage(), NumericType.Modifier.SIGNED) + val typeT = ParameterizedType("T", language) + val intType = IntegerType("int", 32, language, NumericType.Modifier.SIGNED) + val floatType = FloatingPointType("float", 32, language, NumericType.Modifier.SIGNED) assertEquals(typeT, typeParamDeclaration.type) assertEquals(intType, typeParamDeclaration.default) @@ -193,6 +196,9 @@ internal class FunctionTemplateTest : BaseTest() { ) { it.registerLanguage() } + val language = result.finalCtx.availableLanguage() + assertNotNull(language) + val templateDeclaration = findByUniquePredicate(result.allChildren()) { t: FunctionTemplateDeclaration -> @@ -217,7 +223,7 @@ internal class FunctionTemplateTest : BaseTest() { assertEquals(fixedMultiply, call.invokes[0]) // Check template parameters - val doubleType = FloatingPointType("double", 64, CPPLanguage(), NumericType.Modifier.SIGNED) + val doubleType = FloatingPointType("double", 64, language, NumericType.Modifier.SIGNED) val literal5 = findByUniquePredicate(result.literals) { l: Literal<*> -> l.value == 5 } assertEquals(2, call.templateArguments.size) assertEquals(doubleType, (call.templateArguments[0] as TypeExpression).type) @@ -330,6 +336,9 @@ internal class FunctionTemplateTest : BaseTest() { ) { it.registerLanguage() } + val language = result.finalCtx.availableLanguage() + assertNotNull(language) + val templateDeclaration = findByUniquePredicate(result.allChildren()) { t: FunctionTemplateDeclaration -> @@ -354,7 +363,7 @@ internal class FunctionTemplateTest : BaseTest() { assertEquals(fixedMultiply, call.invokes[0]) // Check template parameters - val doubleType = FloatingPointType("double", 64, CPPLanguage(), NumericType.Modifier.SIGNED) + val doubleType = FloatingPointType("double", 64, language, NumericType.Modifier.SIGNED) val literal5 = findByUniquePredicate(result.literals) { l: Literal<*> -> l.value == 5 } assertEquals(2, call.templateArguments.size) assertEquals(doubleType, (call.templateArguments[0] as TypeExpression).type) @@ -540,6 +549,8 @@ internal class FunctionTemplateTest : BaseTest() { ) { it.registerLanguage() } + val language = result.finalCtx.availableLanguage() + assertNotNull(language) // Check inferred for first fixed_division call var templateDeclaration = @@ -594,7 +605,7 @@ internal class FunctionTemplateTest : BaseTest() { ) // Check return values - assertEquals(UnknownType.getUnknownType(CPPLanguage()), callInt2.type) - assertEquals(UnknownType.getUnknownType(CPPLanguage()), callDouble3.type) + assertEquals(UnknownType.getUnknownType(language), callInt2.type) + assertEquals(UnknownType.getUnknownType(language), callDouble3.type) } } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt index 042bd5e468d..dd40766c102 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt @@ -37,14 +37,23 @@ import kotlin.test.* internal class TypeTests : BaseTest() { @Test fun reference() { - val objectType: Type = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) + val language = + CPPLanguage( + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager(), + ) + ) + + val objectType: Type = IntegerType("int", 32, language, NumericType.Modifier.SIGNED) val pointerType: Type = PointerType(objectType, PointerType.PointerOrigin.POINTER) - val unknownType: Type = UnknownType.getUnknownType(CPPLanguage()) - val incompleteType: Type = IncompleteType(CPPLanguage()) + val unknownType: Type = UnknownType.getUnknownType(language) + val incompleteType: Type = IncompleteType(language) val parameterList = - listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) + listOf(IntegerType("int", 32, language, NumericType.Modifier.SIGNED)) val functionPointerType: Type = - FunctionPointerType(parameterList, CPPLanguage(), IncompleteType(CPPLanguage())) + FunctionPointerType(parameterList, language, IncompleteType(language)) // Test 1: ObjectType becomes PointerType containing the original ObjectType as ElementType assertEquals( @@ -76,19 +85,28 @@ internal class TypeTests : BaseTest() { @Test fun dereference() { - val objectType: Type = IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED) + val language = + CPPLanguage( + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager(), + ) + ) + + val objectType: Type = IntegerType("int", 32, language, NumericType.Modifier.SIGNED) val pointerType: Type = PointerType(objectType, PointerType.PointerOrigin.POINTER) - val unknownType: Type = UnknownType.getUnknownType(CPPLanguage()) - val incompleteType: Type = IncompleteType(CPPLanguage()) + val unknownType: Type = UnknownType.getUnknownType(language) + val incompleteType: Type = IncompleteType(language) val parameterList = - listOf(IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED)) + listOf(IntegerType("int", 32, language, NumericType.Modifier.SIGNED)) val functionPointerType: Type = - FunctionPointerType(parameterList, CPPLanguage(), IncompleteType(CPPLanguage())) + FunctionPointerType(parameterList, language, IncompleteType(language)) // Test 1: Dereferencing an ObjectType results in an UnknownType, since we cannot track the // type // of the corresponding memory - assertEquals(UnknownType.getUnknownType(CPPLanguage()), objectType.dereference()) + assertEquals(UnknownType.getUnknownType(language), objectType.dereference()) // Test 2: Dereferencing a PointerType results in the corresponding elementType of the // PointerType (can also be another PointerType) @@ -120,19 +138,21 @@ internal class TypeTests : BaseTest() { ) { it.registerLanguage() } - val noParamType = - FunctionPointerType(emptyList(), CPPLanguage(), IncompleteType(CPPLanguage())) + val language = tu.ctx?.availableLanguage() + assertNotNull(language) + + val noParamType = FunctionPointerType(emptyList(), language, IncompleteType(language)) val oneParamType = FunctionPointerType( listOf(tu.primitiveType("int")), - CPPLanguage(), - IncompleteType(CPPLanguage()), + language, + IncompleteType(language), ) val twoParamType = FunctionPointerType( listOf(tu.primitiveType("int"), tu.primitiveType("unsigned long int")), - CPPLanguage(), - IntegerType("int", 32, CPPLanguage(), NumericType.Modifier.SIGNED), + language, + IntegerType("int", 32, language, NumericType.Modifier.SIGNED), ) val variables = tu.variables val localTwoParam = findByUniqueName(variables, "local_two_param") @@ -163,16 +183,14 @@ internal class TypeTests : BaseTest() { @Throws(Exception::class) @Test fun testCommonTypeTestCpp() { - with( - CXXLanguageFrontend( - CPPLanguage(), - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ), + val ctx = + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager(), ) - ) { + + with(CXXLanguageFrontend(ctx, CPPLanguage(ctx))) { val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") val result = 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 de7f71aab3f..f77549cde84 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 @@ -660,6 +660,9 @@ internal class CXXLanguageFrontendTest : BaseTest() { analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } + val language = tu.ctx?.availableLanguage() + assertNotNull(language) + assertEquals(language, tu.language) val recordDeclaration = tu.records.firstOrNull() assertNotNull(recordDeclaration) @@ -696,7 +699,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { "(int)void*", listOf(tu.primitiveType("int")), listOf(tu.incompleteType().reference(POINTER)), - CPPLanguage(), + language, ), methodWithParam.type, ) @@ -715,7 +718,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { "()void*", listOf(), listOf(tu.incompleteType().reference(POINTER)), - CPPLanguage(), + language, ), inlineMethod.type, ) @@ -724,12 +727,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val inlineConstructor = recordDeclaration.constructors[0] assertEquals(recordDeclaration.name.localName, inlineConstructor.name.localName) assertEquals( - FunctionType( - "()SomeClass", - listOf(), - listOf(tu.objectType("SomeClass")), - CPPLanguage(), - ), + FunctionType("()SomeClass", listOf(), listOf(tu.objectType("SomeClass")), language), inlineConstructor.type, ) assertTrue(inlineConstructor.hasBody()) @@ -743,7 +741,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { "(int)SomeClass", listOf(tu.primitiveType("int")), listOf(tu.objectType("SomeClass")), - CPPLanguage(), + language, ), constructorDefinition.type, ) diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt index 163d83f86a0..8deb590ed00 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt @@ -43,17 +43,16 @@ internal class CXXSymbolConfigurationTest : BaseTest() { @Test @Throws(TranslationException::class) fun testWithoutSymbols() { + val ctx = + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager(), + ) + // parse without symbols val tu = - CXXLanguageFrontend( - CPPLanguage(), - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ), - ) - .parse(File("src/test/resources/symbols.cpp")) + CXXLanguageFrontend(ctx, CPPLanguage(ctx)).parse(File("src/test/resources/symbols.cpp")) val main = tu.functions["main"] assertNotNull(main) @@ -87,12 +86,9 @@ internal class CXXSymbolConfigurationTest : BaseTest() { .build() // let's try with symbol definitions + val ctx = TranslationContext(config, ScopeManager(), TypeManager()) val tu = - CXXLanguageFrontend( - CPPLanguage(), - TranslationContext(config, ScopeManager(), TypeManager()), - ) - .parse(File("src/test/resources/symbols.cpp")) + CXXLanguageFrontend(ctx, CPPLanguage(ctx)).parse(File("src/test/resources/symbols.cpp")) val main = tu.functions["main"] assertNotNull(main) diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt index b7067a4a7ff..87c62b2dd2e 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.golang +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration import de.fraunhofer.aisec.cpg.graph.primitiveType @@ -35,8 +36,8 @@ import de.fraunhofer.aisec.cpg.graph.unknownType import org.neo4j.ogm.annotation.Transient /** The Go language. */ -class GoLanguage : - Language(), +class GoLanguage(ctx: TranslationContext) : + Language(ctx), HasShortCircuitOperators, HasGenerics, HasStructs, diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index 6f8a5634b4e..34624329415 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt @@ -65,8 +65,8 @@ import java.net.URI with = GoEvaluationOrderGraphPass::class, ) @SupportsParallelParsing(false) -class GoLanguageFrontend(language: Language, ctx: TranslationContext) : - LanguageFrontend(language, ctx) { +class GoLanguageFrontend(ctx: TranslationContext, language: Language) : + LanguageFrontend(ctx, language) { private var currentFileSet: GoStandardLibrary.Ast.FileSet? = null private var currentModule: GoStandardLibrary.Modfile.File? = null diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt index 9b52d242d5b..430b9afa3d0 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt @@ -162,7 +162,7 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx) { private fun addBuiltIn(): TranslationUnitDeclaration { val builtin = newTranslationUnitDeclaration("builtin.go") - builtin.language = GoLanguage() + builtin.language = GoLanguage(TranslationContext()) scopeManager.resetToGlobal(builtin) return with(builtin) { diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index b027c6f17a7..93555163fb9 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -1126,6 +1126,9 @@ class GoLanguageFrontendTest : BaseTest() { } assertNotNull(result) + val language = result.finalCtx.availableLanguage() + assertNotNull(language) + val meter = result.variables["util.Meter"] assertNotNull(meter) assertLocalName("Length", meter.type) @@ -1147,7 +1150,7 @@ class GoLanguageFrontendTest : BaseTest() { // We should be able to resolve the call from our stored "do" function to funcy assertInvokes(funcy, result.functions["do"]) - val refs = result.refs.filter { it.name.localName != GoLanguage().anonymousIdentifier } + val refs = result.refs.filter { it.name.localName != language.anonymousIdentifier } refs.forEach { assertNotNull(it.refersTo, "${it.name}'s referTo is empty") } } diff --git a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt index 44901595283..1c344923f1f 100644 --- a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt +++ b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt @@ -69,8 +69,8 @@ import org.ini4j.Profile.Section * - [setComment] not implemented as this is not used (no [Handler] pattern implemented) * - Comments in general are not supported. */ -class IniFileFrontend(language: Language, ctx: TranslationContext) : - LanguageFrontend(language, ctx) { +class IniFileFrontend(ctx: TranslationContext, language: Language) : + LanguageFrontend(ctx, language) { private lateinit var uri: URI private lateinit var region: Region diff --git a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileLanguage.kt b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileLanguage.kt index fa8388077cb..720510dc271 100644 --- a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileLanguage.kt +++ b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileLanguage.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.ini +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.StringType import de.fraunhofer.aisec.cpg.graph.types.Type @@ -38,7 +39,7 @@ import kotlin.reflect.KClass * - all `key`s are unique per section * - the file is accepted by the [ini4j library](https://ini4j.sourceforge.net/) */ -class IniFileLanguage : Language() { +class IniFileLanguage(ctx: TranslationContext) : Language(ctx) { override val fileExtensions = listOf("ini", "conf") override val namespaceDelimiter: String = "." // no such thing diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt index cc2d1416b17..895682a6bb7 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt @@ -66,7 +66,10 @@ class JavaCallResolverHelper { // In case the reference is just called "super", this is a direct superclass, either // defined explicitly or java.lang.Object by default - if (memberExpression.base.name.toString() == JavaLanguage().superClassKeyword) { + if ( + memberExpression.base.name.toString() == + (memberExpression.language as? JavaLanguage)?.superClassKeyword + ) { if (curClass.superClasses.isNotEmpty()) { target = curClass.superClasses[0].root.recordDeclaration } else { diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt index 207edc1bc99..50eb7f94322 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt @@ -27,6 +27,7 @@ package de.fraunhofer.aisec.cpg.frontends.java import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration @@ -41,8 +42,8 @@ import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The Java language. */ -open class JavaLanguage : - Language(), +open class JavaLanguage(ctx: TranslationContext) : + Language(ctx), HasClasses, HasSuperClasses, HasGenerics, diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt index 9ac671f7ade..5903031ea36 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt @@ -81,8 +81,8 @@ import kotlin.jvm.optionals.getOrNull ) // this pass is always required for Java @RegisterExtraPass(JavaImportResolver::class) @RegisterExtraPass(JavaExtraPass::class) -open class JavaLanguageFrontend(language: Language, ctx: TranslationContext) : - LanguageFrontend(language, ctx) { +open class JavaLanguageFrontend(ctx: TranslationContext, language: Language) : + LanguageFrontend(ctx, language) { var context: CompilationUnit? = null var javaSymbolResolver: JavaSymbolSolver? diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt index c18b480a526..c0118381d4a 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt @@ -29,6 +29,8 @@ import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSol import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.frontends.UnknownLanguage import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage import de.fraunhofer.aisec.cpg.frontends.java.JavaLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* @@ -47,7 +49,9 @@ class JavaExternalTypeHierarchyResolver(ctx: TranslationContext) : ComponentPass override fun accept(component: Component) { val provider = object : ContextProvider, LanguageProvider, ScopeProvider { - override val language = JavaLanguage() + override val language: Language<*> + get() = ctx.availableLanguage() ?: UnknownLanguage + override val ctx: TranslationContext = this@JavaExternalTypeHierarchyResolver.ctx override val scope: Scope? get() = scopeManager.globalScope diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt index 9d33d6b6878..4059e7b7eca 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt @@ -743,7 +743,7 @@ internal class EOGTest : BaseTest() { val topLevel = Path.of("src", "test", "resources", "eog") val result = analyze(listOf(topLevel.resolve("EOG.java").toFile()), topLevel, true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } // Test If-Block @@ -932,7 +932,7 @@ internal class EOGTest : BaseTest() { val file = File("src/main/java/de/fraunhofer/aisec/cpg/passes/CallResolver.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertTrue(EvaluationOrderGraphPass.checkEOGInvariant(tu)) } @@ -949,7 +949,7 @@ internal class EOGTest : BaseTest() { val topLevel = toTranslate.parentFile.toPath() val tu = analyzeAndGetFirstTU(listOf(toTranslate), topLevel, true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } var nodes = SubgraphWalker.flattenAST(tu) // TODO: until explicitly added Return Statements are either removed again or code and diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/ConstructorsTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/ConstructorsTest.kt index f9fe533e746..f2b62106f77 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/ConstructorsTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/ConstructorsTest.kt @@ -40,7 +40,7 @@ internal class ConstructorsTest : BaseTest() { @Test @Throws(Exception::class) fun testJava() { - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } val constructors = result.allChildren() val noArg = findByUniquePredicate(constructors) { it.parameters.isEmpty() } val singleArg = findByUniquePredicate(constructors) { it.parameters.size == 1 } diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/SuperCallTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/SuperCallTest.kt index 371775af28c..3ad087d84f1 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/SuperCallTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/calls/SuperCallTest.kt @@ -42,7 +42,7 @@ internal class SuperCallTest : BaseTest() { @Test @Throws(Exception::class) fun testSimpleCall() { - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } val records = result.records val superClass = findByUniqueName(records, "SuperClass") val superMethods = superClass.methods @@ -58,7 +58,7 @@ internal class SuperCallTest : BaseTest() { @Test @Throws(Exception::class) fun testInterfaceCall() { - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } val records = result.records val interface1 = findByUniqueName(records, "Interface1") val interface1Methods = interface1.methods @@ -81,7 +81,7 @@ internal class SuperCallTest : BaseTest() { @Test @Throws(Exception::class) fun testSuperField() { - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } val records = result.records val superClass = findByUniqueName(records, "SuperClass") val superField = findByUniqueName(superClass.fields, "field") @@ -103,7 +103,7 @@ internal class SuperCallTest : BaseTest() { @Test @Throws(Exception::class) fun testInnerCall() { - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } val records = result.records val superClass = findByUniqueName(records, "SuperClass") val superMethods = superClass.methods @@ -119,7 +119,7 @@ internal class SuperCallTest : BaseTest() { @Test @Throws(Exception::class) fun testNoExcessFields() { - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } val records = result.records val superClass = records["SuperClass"] diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.kt index c65742190be..06c2529bf16 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/variable_resolution/VariableResolverJavaTest.kt @@ -185,7 +185,7 @@ internal class VariableResolverJavaTest : BaseTest() { topLevel.resolve("ExternalClass.java"), ) .map(Path::toFile) - val result = analyze(fileNames, topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze(fileNames, topLevel, true) { it.registerLanguage() } val calls = result.calls { it.name.localName == "printLog" } val records = result.records diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt index 053af5379ab..748fe6bdf7b 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontendTest.kt @@ -52,7 +52,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/LargeNegativeNumber.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val declaration = tu.records["LargeNegativeNumber"] @@ -89,7 +89,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/components/ForStmt.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val declaration = tu.declarations[0] as? RecordDeclaration @@ -117,7 +117,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/components/ForEachStmt.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val declaration = tu.declarations[0] as? RecordDeclaration assertNotNull(declaration) @@ -162,7 +162,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/components/TryStmt.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val declaration = tu.declarations[0] as? RecordDeclaration @@ -218,7 +218,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/components/LiteralExpr.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val declaration = tu.declarations[0] as? RecordDeclaration @@ -281,7 +281,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/RecordDeclaration.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertNotNull(tu) @@ -317,7 +317,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/NameExpression.java") val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertNotNull(declaration) } @@ -327,7 +327,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/cfg/Switch.java") val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val graphNodes = SubgraphWalker.flattenAST(declaration) @@ -351,7 +351,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/components/CastExpr.java") val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertNotNull(declaration) @@ -399,7 +399,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/Arrays.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertNotNull(tu) @@ -451,7 +451,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/FieldAccess.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertNotNull(tu) @@ -481,7 +481,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/MemberCallExpression.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertNotNull(tu) @@ -494,7 +494,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/FieldAccess.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } assertNotNull(tu) @@ -572,7 +572,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/Issue285.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val record = tu.declarations(0) assertNotNull(record) @@ -597,7 +597,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { assertNotNull(initializer) assertTrue(initializer is MemberCallExpression) - val call = initializer as? MemberCallExpression + val call = initializer assertLocalName("get", call) val staticCall = nodes.filterIsInstance().firstOrNull { it.isStatic } assertNotNull(staticCall) @@ -610,7 +610,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file2 = File("src/test/resources/fix-328/Animal.java") val result = analyze(listOf(file1, file2), file1.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val tu = findByUniqueName(result.components.flatMap { it.translationUnits }, file1.toString()) @@ -635,8 +635,8 @@ internal class JavaLanguageFrontendTest : BaseTest() { @Test fun testOverrideHandler() { /** A simple extension of the [JavaLanguageFrontend] to demonstrate handler overriding. */ - class MyJavaLanguageFrontend(language: JavaLanguage, ctx: TranslationContext) : - JavaLanguageFrontend(language, ctx) { + class MyJavaLanguageFrontend(ctx: TranslationContext, language: JavaLanguage) : + JavaLanguageFrontend(ctx, language) { init { this.declarationHandler = object : DeclarationHandler(this@MyJavaLanguageFrontend) { @@ -659,7 +659,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { } } - class MyJavaLanguage : JavaLanguage() { + class MyJavaLanguage(ctx: TranslationContext) : JavaLanguage(ctx) { override val frontend = MyJavaLanguageFrontend::class } @@ -733,7 +733,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/OuterClass.java") val result = analyze(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val tu = result.components.flatMap { it.translationUnits }.firstOrNull() assertNotNull(tu) @@ -769,7 +769,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val file = File("src/test/resources/compiling/ForEach.java") val tu = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val p = tu.namespaces["compiling"] @@ -796,7 +796,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { val result = analyze(listOf(file), file.parentFile.toPath(), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val record = result.records["Operators"] assertNotNull(record) @@ -842,7 +842,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { fun testEnums() { val parentFile = File("src/test/resources/compiling/enums/") val result = - analyze(".java", parentFile.toPath(), true) { it.registerLanguage(JavaLanguage()) } + analyze(".java", parentFile.toPath(), true) { it.registerLanguage() } val enum = result.records["Enums"] as EnumDeclaration assertNotNull(enum) diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StaticImportsTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StaticImportsTest.kt index 104ba20e7fc..f23a7864385 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StaticImportsTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StaticImportsTest.kt @@ -52,7 +52,7 @@ internal class StaticImportsTest : BaseTest() { topLevel, true, ) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val methods = result.methods val test = findByUniqueName(methods, "test") @@ -83,7 +83,7 @@ internal class StaticImportsTest : BaseTest() { fun testAsteriskImport() { val result = analyze("java", topLevel.resolve("asterisk"), true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() } val methods = result.methods val main = methods["main", SearchModifier.UNIQUE] diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt index 9bc2774b4c4..8e028e948e4 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt @@ -39,7 +39,9 @@ internal class TypeTests : BaseTest() { @Throws(Exception::class) fun testParameterizedTypes() { val topLevel = Path.of("src", "test", "resources", "types") - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } + val language = result.finalCtx.availableLanguage() + assertNotNull(language) // Check Parameterized val recordDeclarations = result.records @@ -65,7 +67,7 @@ internal class TypeTests : BaseTest() { // Return Type of get Method val methodDeclarationGet = findByUniqueName(methodDeclarations, "get") assertEquals( - FunctionType("get()T", listOf(), listOf(typeT), JavaLanguage()), + FunctionType("get()T", listOf(), listOf(typeT), language), methodDeclarationGet.type, ) } @@ -74,7 +76,7 @@ internal class TypeTests : BaseTest() { @Throws(Exception::class) fun graphTest() { val topLevel = Path.of("src", "test", "resources", "types") - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } val variables = result.allChildren() val recordDeclarations = result.records @@ -110,7 +112,7 @@ internal class TypeTests : BaseTest() { @Test fun testCommonTypeTestJava() { val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy") - val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } + val result = analyze("java", topLevel, true) { it.registerLanguage() } with(result) { val root = assertResolvedType("multistep.Root") val level0 = assertResolvedType("multistep.Level0") diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt index 103018d4d4a..8b2ddd0f666 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/CallResolverTest.kt @@ -117,7 +117,7 @@ class CallResolverTest : BaseTest() { fun testJava() { val result = analyze("java", topLevel, true) { - it.registerLanguage(JavaLanguage()) + it.registerLanguage() it.inferenceConfiguration( InferenceConfiguration.builder().inferRecords(false).build() ) diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt index 02487863951..5b1966da57d 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt @@ -25,11 +25,12 @@ */ package de.fraunhofer.aisec.cpg.frontends.jvm +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass -class JVMLanguage : Language() { +class JVMLanguage(ctx: TranslationContext) : Language(ctx) { override val fileExtensions: List get() = listOf("class", "java", "jimple", "jar") diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt index 0fba55ce807..d69d4bcd14d 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt @@ -51,9 +51,9 @@ import sootup.jimple.parser.JimpleView typealias SootType = sootup.core.types.Type class JVMLanguageFrontend( - language: Language>, ctx: TranslationContext, -) : LanguageFrontend(language, ctx) { + language: Language>, +) : LanguageFrontend(ctx, language) { val declarationHandler = DeclarationHandler(this) val statementHandler = StatementHandler(this) diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt index c1e84c625bd..773ec616e8d 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.llvm +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.FloatingPointType import de.fraunhofer.aisec.cpg.graph.types.IntegerType @@ -33,7 +34,7 @@ import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The LLVM IR language. */ -class LLVMIRLanguage : Language() { +class LLVMIRLanguage(ctx: TranslationContext) : Language(ctx) { override val fileExtensions = listOf("ll") override val namespaceDelimiter = "::" @Transient diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt index 0de46944ccf..0a31d2975ec 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt @@ -54,8 +54,8 @@ import org.bytedeco.llvm.global.LLVM.* * resort to use [Pointer] as the AST node type here. */ @RegisterExtraPass(CompressLLVMPass::class) -class LLVMIRLanguageFrontend(language: Language, ctx: TranslationContext) : - LanguageFrontend(language, ctx) { +class LLVMIRLanguageFrontend(ctx: TranslationContext, language: Language) : + LanguageFrontend(ctx, language) { val statementHandler = StatementHandler(this) val declarationHandler = DeclarationHandler(this) diff --git a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt index 882d8885b71..253beaf2843 100644 --- a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt +++ b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt @@ -25,7 +25,10 @@ */ package de.fraunhofer.aisec.cpg.frontends.llvm -import de.fraunhofer.aisec.cpg.* +import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationConfiguration +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration @@ -35,7 +38,6 @@ import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.test.* import java.nio.file.Path import kotlin.test.* -import kotlin.test.Test import org.junit.jupiter.api.assertThrows class LLVMIRLanguageFrontendTest { @@ -43,15 +45,13 @@ class LLVMIRLanguageFrontendTest { fun testExceptionBrokenFile() { val topLevel = Path.of("src", "test", "resources", "llvm") - val frontend = - LLVMIRLanguageFrontend( - LLVMIRLanguage(), - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ), + val ctx = + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager(), ) + val frontend = LLVMIRLanguageFrontend(ctx, LLVMIRLanguage(ctx)) val exception = assertThrows { frontend.parse(topLevel.resolve("main-broken.ll").toFile()) @@ -63,15 +63,13 @@ class LLVMIRLanguageFrontendTest { fun test1() { val topLevel = Path.of("src", "test", "resources", "llvm") - val frontend = - LLVMIRLanguageFrontend( - LLVMIRLanguage(), - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ), + val ctx = + TranslationContext( + TranslationConfiguration.builder().build(), + ScopeManager(), + TypeManager(), ) + val frontend = LLVMIRLanguageFrontend(ctx, LLVMIRLanguage(ctx)) frontend.parse(topLevel.resolve("main.ll").toFile()) } diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt index 4d4b6bee486..54a47ab6e3d 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.python +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation import de.fraunhofer.aisec.cpg.graph.Name @@ -44,8 +45,8 @@ import org.neo4j.ogm.annotation.Transient import org.neo4j.ogm.annotation.typeconversion.Convert /** The Python language. */ -class PythonLanguage : - Language(), +class PythonLanguage(ctx: TranslationContext) : + Language(ctx), HasShortCircuitOperators, HasOperatorOverloading, HasFunctionStyleConstruction, diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index 945da30301b..1203572a7ed 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -63,8 +63,8 @@ import kotlin.math.min */ @RegisterExtraPass(PythonAddDeclarationsPass::class) @SupportsParallelParsing(false) // https://github.com/Fraunhofer-AISEC/cpg/issues/2026 -class PythonLanguageFrontend(language: Language, ctx: TranslationContext) : - LanguageFrontend(language, ctx) { +class PythonLanguageFrontend(ctx: TranslationContext, language: Language) : + LanguageFrontend(ctx, language) { val lineSeparator = "\n" // TODO private val tokenTypeIndex = 0 private val jep = JepSingleton // configure Jep diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt index 0b9c15dd83e..3fe4f7ca98b 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt @@ -285,5 +285,5 @@ class PythonAddDeclarationsPass(ctx: TranslationContext) : ComponentPass(ctx), L } override val language: Language<*> - get() = ctx.config.languages.firstOrNull { it is PythonLanguage } ?: UnknownLanguage + get() = ctx.availableLanguage() ?: UnknownLanguage } diff --git a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt index 0a717bf60fe..8301c00879d 100644 --- a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt +++ b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.frontends.ruby import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression @@ -33,8 +34,8 @@ import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass /** The Ruby Language */ -class RubyLanguage() : - Language(), +class RubyLanguage(ctx: TranslationContext) : + Language(ctx), HasDefaultArguments, HasClasses, HasSuperClasses, diff --git a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguageFrontend.kt b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguageFrontend.kt index df34876f6a1..8e9a00f5674 100644 --- a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguageFrontend.kt +++ b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguageFrontend.kt @@ -39,8 +39,8 @@ import org.jruby.ast.RootNode import org.jruby.parser.Parser import org.jruby.parser.ParserConfiguration -class RubyLanguageFrontend(language: RubyLanguage, ctx: TranslationContext) : - LanguageFrontend(language, ctx) { +class RubyLanguageFrontend(ctx: TranslationContext, language: RubyLanguage) : + LanguageFrontend(ctx, language) { val declarationHandler: DeclarationHandler = DeclarationHandler(this) val expressionHandler: ExpressionHandler = ExpressionHandler(this) val statementHandler: StatementHandler = StatementHandler(this) diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt index 7152b329629..ad2ed24e2fa 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.typescript +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.* @@ -32,7 +33,8 @@ import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The JavaScript language. */ -open class JavaScriptLanguage : Language(), HasShortCircuitOperators { +open class JavaScriptLanguage(ctx: TranslationContext) : + Language(ctx), HasShortCircuitOperators { override val fileExtensions = listOf("js", "jsx") override val namespaceDelimiter = "." @Transient diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt index d7ca5ce1905..3aada2ca733 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt @@ -25,9 +25,9 @@ */ package de.fraunhofer.aisec.cpg.frontends.typescript -import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.TranslationContext /** The TypeScript language. */ -class TypeScriptLanguage : JavaScriptLanguage() { +class TypeScriptLanguage(ctx: TranslationContext) : JavaScriptLanguage(ctx) { override val fileExtensions = listOf("ts", "tsx") } diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt index 722380edbc1..6b8d24ad547 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguageFrontend.kt @@ -57,9 +57,9 @@ import java.nio.file.StandardCopyOption * also has built-in support for React dialects TSX and JSX. */ class TypeScriptLanguageFrontend( - language: Language, ctx: TranslationContext, -) : LanguageFrontend(language, ctx) { + language: Language, +) : LanguageFrontend(ctx, language) { val declarationHandler = DeclarationHandler(this) val statementHandler = StatementHandler(this)