Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Converting LLVM frontend towithChildren style #1621

Draft
wants to merge 9 commits into
base: mk/backwards-ast
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,12 @@ import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.helpers.getCodeOfSubregion
import de.fraunhofer.aisec.cpg.passes.inference.IsImplicitProvider
import de.fraunhofer.aisec.cpg.passes.inference.IsInferredProvider
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation
import de.fraunhofer.aisec.cpg.sarif.Region
import java.net.URI
import java.util.*
import kotlin.collections.ArrayDeque
import org.slf4j.LoggerFactory

Expand Down Expand Up @@ -420,6 +417,31 @@ fun <T : Node> T.withChildren(
return this
}

/**
* This function can be used to set the [Node.astParent] of this node to the current node on the
* [AstStackProvider]'s stack. This is particularly useful if the node was created outside of the
* [withChildren] lambda. This is a usual pattern if the node is optionally wrapped in something
* else.
*
* Example:
* ```kotlin
* val expr = newReference("p")
*
* return if (isDeref) {
* newUnaryOperator("*").withChildren {
* it.input = expr.withParent()
* }
* } else {
* expr
* }
* ```
*/
context(AstStackProvider)
fun <T : Node> T.withParent(): T {
this.astParent = (this@AstStackProvider).astStack.lastOrNull()
return this
}

context(ContextProvider)
fun <T : Declaration> T.declare(): T {
val scopeManager =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,17 @@ class DeclarationHandler(lang: LLVMIRLanguageFrontend) :
// the pointer type
val type = frontend.typeOf(valueRef)

val variableDeclaration = newVariableDeclaration(name, type, false, rawNode = valueRef)

// cache binding
frontend.bindingsCache[valueRef.symbolName] = variableDeclaration

val size = LLVMGetNumOperands(valueRef)
// the first operand (if it exists) is an initializer
if (size > 0) {
val expr = frontend.expressionHandler.handle(LLVMGetOperand(valueRef, 0))
variableDeclaration.initializer = expr
return newVariableDeclaration(name, type, false, rawNode = valueRef).withChildren {
// cache binding
frontend.bindingsCache[valueRef.symbolName] = it

val size = LLVMGetNumOperands(valueRef)
// the first operand (if it exists) is an initializer
if (size > 0) {
val expr = frontend.expressionHandler.handle(LLVMGetOperand(valueRef, 0))
it.initializer = expr
}
}

return variableDeclaration
}

/**
Expand All @@ -97,77 +95,75 @@ class DeclarationHandler(lang: LLVMIRLanguageFrontend) :
*/
private fun handleFunction(func: LLVMValueRef): FunctionDeclaration {
val name = LLVMGetValueName(func)
val functionDeclaration = newFunctionDeclaration(name.string, rawNode = func)

// return types are a bit tricky, because the type of the function is a pointer to the
// function type, which then has the return type in it
val funcPtrType = LLVMTypeOf(func)
val funcType = LLVMGetElementType(funcPtrType)
val returnType = LLVMGetReturnType(funcType)
return newFunctionDeclaration(name.string, rawNode = func).withChildren(hasScope = true) {
// return types are a bit tricky, because the type of the function is a pointer to the
// function type, which then has the return type in it
val funcPtrType = LLVMTypeOf(func)
val funcType = LLVMGetElementType(funcPtrType)
val returnType = LLVMGetReturnType(funcType)

functionDeclaration.type = frontend.typeOf(returnType)
it.type = frontend.typeOf(returnType)

frontend.scopeManager.enterScope(functionDeclaration)
var param = LLVMGetFirstParam(func)
while (param != null) {
val namePair = frontend.getNameOf(param)
val paramName = namePair.first
val paramSymbolName = namePair.second

var param = LLVMGetFirstParam(func)
while (param != null) {
val namePair = frontend.getNameOf(param)
val paramName = namePair.first
val paramSymbolName = namePair.second
val type = frontend.typeOf(param)

val type = frontend.typeOf(param)
// TODO: support variardic
val decl = newParameterDeclaration(paramName, type, false, rawNode = param)

// TODO: support variardic
val decl = newParameterDeclaration(paramName, type, false, rawNode = param)
frontend.scopeManager.addDeclaration(decl)
frontend.bindingsCache[paramSymbolName] = decl

frontend.scopeManager.addDeclaration(decl)
frontend.bindingsCache[paramSymbolName] = decl

param = LLVMGetNextParam(param)
}
param = LLVMGetNextParam(param)
}

var bb = LLVMGetFirstBasicBlock(func)
while (bb != null) {
val stmt = frontend.statementHandler.handle(bb)

// Notice: we have one fundamental challenge here. Basic blocks in LLVM have a flat
// hierarchy, meaning that a function has a list of basic blocks, of which one can
// be unlabeled and is considered to be the entry. All other blocks need to have
// labels and can be reached by branching or jump instructions. If all blocks are
// labeled, then the first one is considered to be the entry.
//
// For our translation into the CPG we translate a basic block into a compound
// statement, i.e. a list of statements. However, in the CPG structure, a function
// definition does not have an entry, which specifies the first block, but it has a
// *body*, which comprises *all* statements within the abstract syntax tree of
// that function, hierarchically organized by compound statements. To emulate that, we
// take the first basic block as our body and add subsequent blocks as statements to
// the body. More specifically, we use the CPG node LabelStatement, which denotes the
// use of a label. Its property substatement contains the original basic block, parsed
// as a compound statement

// Take the entry block as our body
if (LLVMGetEntryBasicBlock(func) == bb && stmt is Block) {
functionDeclaration.body = stmt
} else if (LLVMGetEntryBasicBlock(func) == bb) {
functionDeclaration.body = newBlock()
if (stmt != null) {
(functionDeclaration.body as Block).addStatement(stmt)
}
} else {
// add the label statement, containing this basic block as a compound statement to
// our body (if we have none, which we should)
if (stmt != null) {
(functionDeclaration.body as? Block)?.addStatement(stmt)
var bb = LLVMGetFirstBasicBlock(func)
while (bb != null) {
val stmt = frontend.statementHandler.handle(bb)

// Notice: we have one fundamental challenge here. Basic blocks in LLVM have a flat
// hierarchy, meaning that a function has a list of basic blocks, of which one can
// be unlabeled and is considered to be the entry. All other blocks need to have
// labels and can be reached by branching or jump instructions. If all blocks are
// labeled, then the first one is considered to be the entry.
//
// For our translation into the CPG we translate a basic block into a compound
// statement, i.e. a list of statements. However, in the CPG structure, a function
// definition does not have an entry, which specifies the first block, but it has a
// *body*, which comprises *all* statements within the abstract syntax tree of
// that function, hierarchically organized by compound statements. To emulate that,
// we take the first basic block as our body and add subsequent blocks as statements
// to the body. More specifically, we use the CPG node LabelStatement, which denotes
// the use of a label. Its property sub-statement contains the original basic block,
// parsed as a compound statement

// Take the entry block as our body
if (LLVMGetEntryBasicBlock(func) == bb && stmt is Block) {
it.body = stmt
} else if (LLVMGetEntryBasicBlock(func) == bb) {
it.body =
newBlock().withChildren { block ->
if (stmt != null) {
block += stmt.withParent()
}
}
} else {
// add the label statement, containing this basic block as a compound statement
// to our body (if we have one, which we should)
(it.body as? Block)?.withChildren { block ->
if (stmt != null) {
block += stmt.withParent()
}
}
}
}

bb = LLVMGetNextBasicBlock(bb)
bb = LLVMGetNextBasicBlock(bb)
}
}

frontend.scopeManager.leaveScope(functionDeclaration)

return functionDeclaration
}

/**
Expand Down Expand Up @@ -206,25 +202,30 @@ class DeclarationHandler(lang: LLVMIRLanguageFrontend) :
return record
}

record = newRecordDeclaration(name, "struct")

val size = LLVMCountStructElementTypes(typeRef)
// We need to create a new record declaration but DO NOT create a new scope (yet)
record =
newRecordDeclaration(name, "struct").withChildren {
val size = LLVMCountStructElementTypes(typeRef)

for (i in 0 until size) {
val a = LLVMStructGetTypeAtIndex(typeRef, i)
val fieldType = frontend.typeOf(a, alreadyVisited)
for (i in 0 until size) {
val a = LLVMStructGetTypeAtIndex(typeRef, i)
val fieldType = frontend.typeOf(a, alreadyVisited)

// there are no names, so we need to invent some dummy ones for easier reading
val fieldName = "field_$i"
// there are no names, so we need to invent some dummy ones for easier reading
val fieldName = "field_$i"

frontend.scopeManager.enterScope(record)
// we need to enter the record's scope for each field individually, otherwise
// the above call to typeOf will be inside the record scope and then things go
// wrong
frontend.scopeManager.enterScope(it)

val field = newFieldDeclaration(fieldName, fieldType, listOf(), null, false)
val field = newFieldDeclaration(fieldName, fieldType, listOf(), null, false)

frontend.scopeManager.addDeclaration(field)
frontend.scopeManager.addDeclaration(field)

frontend.scopeManager.leaveScope(record)
}
frontend.scopeManager.leaveScope(it)
}
}

// add it to the global scope
frontend.scopeManager.addDeclaration(record)
Expand Down
Loading
Loading