Skip to content

Commit

Permalink
Added new NamespaceScope
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Nov 20, 2024
1 parent 307fd35 commit 6dab1a6
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 15 deletions.
14 changes: 7 additions & 7 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ class ScopeManager : ScopeProvider {
is RecordDeclaration -> RecordScope(nodeToScope)
is TemplateDeclaration -> TemplateScope(nodeToScope)
is TranslationUnitDeclaration -> FileScope(nodeToScope)
is NamespaceDeclaration -> newNameScopeIfNecessary(nodeToScope)
is NamespaceDeclaration -> newNamespaceIfNecessary(nodeToScope)
else -> {
LOGGER.error(
"No known scope for AST node of type {}",
Expand All @@ -269,23 +269,23 @@ class ScopeManager : ScopeProvider {
}

/**
* A small internal helper function used by [enterScope] to create a [NameScope].
* A small internal helper function used by [enterScope] to create a [NamespaceScope].
*
* The issue with name scopes, such as a namespace, is that it can exist across several files,
* i.e. translation units, represented by different [NamespaceDeclaration] nodes. But, in order
* to make namespace resolution work across files, only one [NameScope] must exist that holds
* all declarations, such as classes, independently of the translation units. Therefore, we need
* to check, whether such as node already exists. If it does already exist:
* - we update the scope map so that the current [NamespaceDeclaration] points to the existing
* [NameScope]
* [NamespaceScope]
* - we return null, indicating to [enterScope], that no new scope needs to be pushed by
* [enterScope].
*
* Otherwise, we return a new name scope.
* Otherwise, we return a new namespace scope.
*/
private fun newNameScopeIfNecessary(nodeToScope: NamespaceDeclaration): NameScope? {
private fun newNamespaceIfNecessary(nodeToScope: NamespaceDeclaration): NamespaceScope? {
val existingScope =
filterScopes { it is NameScope && it.name == nodeToScope.name }.firstOrNull()
filterScopes { it is NamespaceScope && it.name == nodeToScope.name }.firstOrNull()

return if (existingScope != null) {
// update the AST node to this namespace declaration
Expand All @@ -299,7 +299,7 @@ class ScopeManager : ScopeProvider {
// does not need to push a new scope
null
} else {
NameScope(nodeToScope)
NamespaceScope(nodeToScope)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ import de.fraunhofer.aisec.cpg.graph.Node

/**
* A scope which acts as a namespace with a certain name, which is prefixed to all local names
* declared in it. This could be a package or other structural elements, like a class. In the latter
* case, the derived [RecordScope] should be used.
* declared in it. This could be a package or other structural elements, like a class. In the first
* case, the derived [NamespaceScope], in the latter case, the derived [RecordScope] should be used.
*/
open class NameScope(node: Node?) : StructureDeclarationScope(node) {
sealed class NameScope(node: Node?) : StructureDeclarationScope(node) {

init {
astNode = node
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.graph.scopes

import de.fraunhofer.aisec.cpg.graph.declarations.NamespaceDeclaration

/**
* This scope is opened up by a [NamespaceDeclaration] and represents the scope of the whole
* namespace. This scope is special in a way that it will only exist once (per [GlobalScope]) and
* contains all symbols declared in this namespace, even if they are spread across multiple files.
*/
class NamespaceScope(astNode: NamespaceDeclaration) : NameScope(astNode)
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ typealias SymbolMap = MutableMap<Symbol, MutableList<Declaration>>
* restriction and can act as namespaces to avoid name collisions.
*/
@NodeEntity
abstract class Scope(
sealed class Scope(
@Relationship(value = "SCOPE", direction = Relationship.Direction.INCOMING)
@JsonBackReference
open var astNode: Node?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@
*/
package de.fraunhofer.aisec.cpg.graph.scopes

import de.fraunhofer.aisec.cpg.ScopeManager
import de.fraunhofer.aisec.cpg.graph.DeclarationHolder
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.*

open class StructureDeclarationScope(astNode: Node?) : ValueDeclarationScope(astNode) {
/**
* This sealed (and abstract) class represents a [Scope] that in addition to declare variables also
* defines structures, such as classes, namespaces, etc.
*
* This is actually only needed because of the legacy [ScopeManager.resolve] function.
*/
sealed class StructureDeclarationScope(astNode: Node?) : ValueDeclarationScope(astNode) {
val structureDeclarations: List<Declaration>
get() {
return symbols
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory
* Is a scope where local variables can be declared and independent of specific language constructs.
* Works for if, for, and extends to the block scope
*/
abstract class ValueDeclarationScope(astNode: Node?) : Scope(astNode) {
sealed class ValueDeclarationScope(astNode: Node?) : Scope(astNode) {
val valueDeclarations: List<ValueDeclaration>
get() {
return symbols.flatMap { it.value }.filterIsInstance<ValueDeclaration>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.ProblemNode
import de.fraunhofer.aisec.cpg.graph.declarations.NamespaceDeclaration
import de.fraunhofer.aisec.cpg.graph.scopes.NameScope
import de.fraunhofer.aisec.cpg.graph.scopes.NamespaceScope
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal
import de.fraunhofer.aisec.cpg.helpers.Util
import java.util.function.Supplier
Expand Down Expand Up @@ -100,7 +101,7 @@ abstract class GoHandler<ResultNode : Node?, HandlerNode : GoStandardLibrary.Ast
val namespace =
frontend.scopeManager
.filterScopes {
it is NameScope && (it.astNode as? NamespaceDeclaration)?.path == filename
it is NamespaceScope && (it.astNode as? NamespaceDeclaration)?.path == filename
}
.firstOrNull()
?.astNode as? NamespaceDeclaration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import de.fraunhofer.aisec.cpg.graph.Annotation
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.scopes.LocalScope
import de.fraunhofer.aisec.cpg.graph.scopes.NameScope
import de.fraunhofer.aisec.cpg.graph.scopes.NamespaceScope
import de.fraunhofer.aisec.cpg.graph.statements.*
import de.fraunhofer.aisec.cpg.graph.statements.AssertStatement
import de.fraunhofer.aisec.cpg.graph.statements.CatchClause
Expand Down Expand Up @@ -787,7 +788,7 @@ class StatementHandler(frontend: PythonLanguageFrontend) :
// behind that is that we wrap each file in a namespace (as defined in the python spec). So
// the "global" scope is actually our current namespace scope.
var pythonGlobalScope =
frontend.scopeManager.globalScope?.children?.firstOrNull { it is NameScope }
frontend.scopeManager.globalScope?.children?.firstOrNull { it is NamespaceScope }

return newLookupScopeStatement(
global.names.map { parseName(it).localName },
Expand Down

0 comments on commit 6dab1a6

Please sign in to comment.