Skip to content

Commit

Permalink
Added annotation to pass that requires a language trait
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Apr 4, 2024
1 parent 93728ff commit ef31c09
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,13 @@ interface HasShortCircuitOperators : LanguageTrait {
* A language trait, that specifies that this language treats functions "first-class citizens",
* meaning they can be assigned to variables and passed as arguments to other functions.
*/
interface HasFirstClassFunctions
interface HasFirstClassFunctions : LanguageTrait

/**
* A language trait, that specifies that this language has an "anonymous" identifier, used for
* unused parameters or suppressed assignments.
*/
interface HasAnonymousIdentifier {
interface HasAnonymousIdentifier : LanguageTrait {
val anonymousIdentifier: String
get() = "_"
}
Expand All @@ -245,7 +245,7 @@ interface HasAnonymousIdentifier {
* [GlobalScope], i.e., not within a namespace, but directly contained in a
* [TranslationUnitDeclaration].
*/
interface HasGlobalVariables {
interface HasGlobalVariables : LanguageTrait {
val globalVariableScopeClass: Class<out Node>
}

Expand All @@ -255,4 +255,4 @@ interface HasGlobalVariables {
* [CallExpression] and a [CastExpression], we need to employ an additional pass
* ([ReplaceCallCastPass]) after the initial language frontends are done.
*/
interface HasFunctionalCasts
interface HasFunctionalCasts : LanguageTrait
38 changes: 33 additions & 5 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,21 @@ package de.fraunhofer.aisec.cpg.passes
import de.fraunhofer.aisec.cpg.*
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.frontends.LanguageTrait
import de.fraunhofer.aisec.cpg.frontends.TranslationException
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.helpers.Benchmark
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker.ScopedWalker
import de.fraunhofer.aisec.cpg.passes.order.RequiredFrontend
import de.fraunhofer.aisec.cpg.passes.order.RequiresLanguageTrait
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.findAnnotations
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.primaryConstructor
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -112,16 +117,36 @@ sealed class Pass<T : Node>(final override val ctx: TranslationContext) :
* [RequiredFrontend]
*/
fun runsWithCurrentFrontend(usedFrontends: Collection<LanguageFrontend<*, *>>): Boolean {
if (!this.javaClass.isAnnotationPresent(RequiredFrontend::class.java)) return true
val requiredFrontend = this.javaClass.getAnnotation(RequiredFrontend::class.java).value
val requiredFrontend = this::class.findAnnotation<RequiredFrontend>() ?: return true
for (used in usedFrontends) {
if (used.javaClass == requiredFrontend.java) return true
if (used::class == requiredFrontend.value) return true
}
return false
}

companion object {
/**
* Checks, if the pass requires a specific [LanguageTrait] and if the current target of the pass
* has this trait.
*
* @return true, if the pass does not require a specific language trait or if it matches the
* [RequiresLanguageTrait].
*/
fun runsWithLanguageTrait(language: Language<*>?): Boolean {
if (language == null) {
return true
}

val requiresLanguageTraits = this::class.findAnnotations<RequiresLanguageTrait>()
for (requiresLanguageTrait in requiresLanguageTraits) {
if (!language::class.isSubclassOf(requiresLanguageTrait.value)) {
return false
}
}

return true
}

companion object {
val log: Logger = LoggerFactory.getLogger(Pass::class.java)
}

Expand Down Expand Up @@ -264,7 +289,10 @@ private inline fun <reified T : Node> consumeTarget(
val realClass = checkForReplacement(cls, language, ctx.config)

val pass = realClass.primaryConstructor?.call(ctx)
if (pass?.runsWithCurrentFrontend(executedFrontends) == true) {
if (
pass?.runsWithCurrentFrontend(executedFrontends) == true &&
pass.runsWithLanguageTrait(language)
) {
pass.accept(target)
pass.cleanup()
return pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,37 @@
package de.fraunhofer.aisec.cpg.passes

import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.frontends.Handler
import de.fraunhofer.aisec.cpg.frontends.HasFunctionalCasts
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CastExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
import de.fraunhofer.aisec.cpg.passes.order.DependsOn
import de.fraunhofer.aisec.cpg.passes.order.ExecuteBefore
import de.fraunhofer.aisec.cpg.passes.order.RequiresLanguageTrait

/**
* If a [Language] has the trait [HasFunctionalCasts], we cannot distinguish between a
* [CallExpression] and a [CastExpression] during the initial translation. This stems from the fact
* that we might not know all the types yet. We therefore need to handle them as regular call
* expression in a [LanguageFrontend] or [Handler] and then later replace them with a
* [CastExpression], if the [CallExpression.callee] refers to name of a [Type] rather than a
* function.
*/
@ExecuteBefore(EvaluationOrderGraphPass::class)
@DependsOn(TypeResolver::class)
@RequiresLanguageTrait(HasFunctionalCasts::class)
class ReplaceCallCastPass(ctx: TranslationContext) : TranslationUnitPass(ctx) {
private lateinit var walker: SubgraphWalker.ScopedWalker

override fun accept(tu: TranslationUnitDeclaration) {
// We need to make sure that the language has a specific trait
if (tu.language !is HasFunctionalCasts) {
return
}

walker = SubgraphWalker.ScopedWalker(ctx.scopeManager)
walker.registerHandler { _, parent, node ->
when (node) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $$$$$$\ $$$$$$$\ $$$$$$\
* $$ __$$\ $$ __$$\ $$ __$$\
* $$ / \__|$$ | $$ |$$ / \__|
* $$ | $$$$$$$ |$$ |$$$$\
* $$ | $$ ____/ $$ |\_$$ |
* $$ | $$\ $$ | $$ | $$ |
* \$$$$$ |$$ | \$$$$$ |
* \______/ \__| \______/
*
*/
package de.fraunhofer.aisec.cpg.passes.order

import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.frontends.LanguageTrait
import kotlin.reflect.KClass

/**
* This annotation can only enable a pass if its target language implements a given [LanguageTrait].
*
* This annotation is [Repeatable]. In this case, all specified language traits must exist on the
* [Language].
*/
@Repeatable annotation class RequiresLanguageTrait(val value: KClass<out LanguageTrait>)

0 comments on commit ef31c09

Please sign in to comment.