Skip to content

Commit

Permalink
added missing pass
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Mar 4, 2023
1 parent d6e7562 commit 1730f8b
Showing 1 changed file with 131 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2023, 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

import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.frontends.golang.GoLanguage
import de.fraunhofer.aisec.cpg.graph.*
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.DeclaredReferenceExpression
import de.fraunhofer.aisec.cpg.graph.types.TypeParser
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
import de.fraunhofer.aisec.cpg.passes.order.ExecuteBefore

/**
* In Go, it is possible to convert compatible types by "calling" the type name as a function, such
* as
*
* ```go
* var i = int(2.0)
* ```
*
* This is also possible with more complex types, such as interfaces or aliased types, as long as
* they are compatible. Because types in the same package can be defined in multiple files, we
* cannot decide during the frontend run. Therefore, we need to execute this pass before the
* [CallResolver] and convert certain [CallExpression] nodes into a [CastExpression].
*/
@ExecuteBefore(CallResolver::class)
@ExecuteBefore(DFGPass::class)
class GoTypeCastingPass : Pass() {
override fun accept(t: TranslationResult) {
scopeManager = t.scopeManager

var walker = SubgraphWalker.ScopedWalker(scopeManager)
walker.registerHandler { _, parent, node ->
// We are only interested in CallExpressions
if (node is CallExpression) {
handleCall(node, parent)
}
}

for (tu in t.translationUnits) {
walker.iterate(tu)
}
}

private fun handleCall(call: CallExpression, parent: Node?) {
// We need to check, whether the "callee" refers to a type and if yes, convert it into a
// cast expression. And this is only really necessary, if the function call has a single
// argument.
val callee = call.callee
if (parent != null && callee is DeclaredReferenceExpression && call.arguments.size == 1) {
val language = parent.language ?: GoLanguage()

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

if (TypeManager.getInstance().typeExists(fqn.toString())) {
replaceCallWithCast(fqn, language, parent, call)
}
}
}
}

private fun replaceCallWithCast(
typeName: CharSequence,
language: Language<out LanguageFrontend>,
parent: Node,
call: CallExpression,
) {
val cast = parent.newCastExpression(call.code)
cast.location = call.location
cast.castType = TypeParser.createFrom(typeName, false, language)
cast.expression = call.arguments.single()

if (parent !is ArgumentHolder) {
log.error(
"Parent AST node of call expression is not an argument holder. Cannot convert to cast expression. Further analysis might not be entirely accurate."
)
return
}

val success = parent.replaceArgument(call, cast)
if (!success) {
log.error(
"Replacing call expression with cast expression was not successful. Further analysis might not be entirely accurate."
)
} else {
call.disconnectFromGraph()
}
}

override fun cleanup() {
// Nothing to do
}
}

0 comments on commit 1730f8b

Please sign in to comment.