Skip to content

Commit

Permalink
More Go imperovements
Browse files Browse the repository at this point in the history
Fixed problems with imports

Slightly better handling of underlying types

Resolve package names

Better import ordering
  • Loading branch information
oxisto committed Oct 20, 2023
1 parent 01708ff commit 9207616
Show file tree
Hide file tree
Showing 18 changed files with 368 additions and 438 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,44 @@ internal operator fun Double.div(other: Number): Number =
else -> throw UnsupportedOperationException()
}

infix fun Number.shl(other: Number): Number =
when (this) {
is Int -> this shl other
is Long -> this shl other
else -> throw UnsupportedOperationException()
}

private infix fun Int.shl(other: Number): Number =
when (other) {
is Int -> this shl other
else -> throw UnsupportedOperationException()
}

private infix fun Long.shl(other: Number): Number =
when (other) {
is Int -> this shl other
else -> throw UnsupportedOperationException()
}

infix fun Number.xor(other: Number): Number =
when (this) {
is Int -> this xor other
is Long -> this xor other
else -> throw UnsupportedOperationException()
}

private infix fun Int.xor(other: Number): Number =
when (other) {
is Int -> this xor other
else -> throw UnsupportedOperationException()
}

private infix fun Long.xor(other: Number): Number =
when (other) {
is Long -> this xor other
else -> throw UnsupportedOperationException()
}

internal operator fun Number.unaryMinus(): Number =
when (this) {
is Int -> -this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,16 @@ class DynamicInvokeResolver(ctx: TranslationContext) : ComponentPass(ctx) {
when (val type = expr.type) {
is FunctionType -> type.pointer() as FunctionPointerType
is FunctionPointerType -> type
else -> return
else -> {
// some languages allow other types to derive from a function type, in this case
// we need to look for a super type
val superType = type.superTypes.singleOrNull()
if (superType is FunctionType) {
superType.pointer() as FunctionPointerType
} else {
return
}
}
}

val invocationCandidates = mutableListOf<FunctionDeclaration>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,18 @@ class Inference(val start: Node, override val ctx: TranslationContext) :
return declaration
}

fun createInferredNamespaceDeclaration(name: Name, path: String?): NamespaceDeclaration {
fun createInferredNamespaceDeclaration(
name: Name,
path: String?,
origin: Node
): NamespaceDeclaration {
// Here be dragons. Jump to the scope that the node defines directly, so that we can
// delegate further operations to the scope manager. We also save the old scope so we can
// delegate further operations to the scope manager. We also save the old scope, so we can
// restore it.
return inferInScopeOf(start) {
log.debug(
debugWithFileLocation(
origin,
log,
"Inferring a new namespace declaration {} {}",
name,
if (path != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ package de.fraunhofer.aisec.cpg.frontends.golang
import de.fraunhofer.aisec.cpg.frontends.Handler
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.statements.expressions.Literal
import de.fraunhofer.aisec.cpg.helpers.Util
import java.util.function.Supplier
Expand Down Expand Up @@ -91,19 +93,42 @@ abstract class GoHandler<ResultNode : Node?, HandlerNode : GoStandardLibrary.Ast
*/
val GoStandardLibrary.Ast.ImportSpec.importName: String
get() {
val path = frontend.expressionHandler.handle(this.path) as? Literal<*>
val paths = (path?.value as? String)?.split("/") ?: listOf()
// We set the filename of the include declaration to the package path, i.e., its full
// path including any module identifiers. This way we can match the include declaration
// back to the namespace's path and name
val filename = path.value.removeSurrounding("\"").removeSurrounding("`")

// Return the last name in the path as the import name. However, if the last name is a
// module version (e.g., v5), then we need to return the second-to-last
var last = paths.lastOrNull()
last =
if (last?.startsWith("v") == true) {
paths.getOrNull(paths.size - 2)
} else {
last
}
// While it is convention that the respective Go package is named after the last path in
// the import name, this is purely a convention. We therefore need to find out the real
// package name. Currently, we do not support parallel parsing, so the order of imports
// might be suitable to look up the specific package in the scope manager. In the
// future, we might need a better solution.
val namespace =
frontend.scopeManager
.filterScopes {
it is NameScope && (it.astNode as? NamespaceDeclaration)?.path == filename
}
.firstOrNull()
?.astNode as? NamespaceDeclaration

return last ?: ""
if (namespace != null) {
return namespace.name.localName
} else {
val path = frontend.expressionHandler.handle(this.path) as? Literal<*>
val paths = (path?.value as? String)?.split("/") ?: listOf()

// Return the last name in the path as the import name. However, if the last name is
// a
// module version (e.g., v5), then we need to return the second-to-last
var last = paths.lastOrNull()
last =
if (last != null && last.length >= 2 && last[0] == 'v' && last[1].isDigit()) {
paths.getOrNull(paths.size - 2)
} else {
last
}

return last ?: ""
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ class GoLanguage :

// This makes lambda expression works, as long as we have the dedicated a
// FunctionPointerType
if (type is FunctionPointerType && superType is FunctionType) {
return type == superType.reference(PointerType.PointerOrigin.POINTER)
if (type is FunctionPointerType && superType.underlyingType is FunctionType) {
return type == superType.underlyingType?.reference(PointerType.PointerOrigin.POINTER)
}

// the unsafe.IntegerType is a fake type in the unsafe package, that accepts any integer
Expand Down Expand Up @@ -170,7 +170,7 @@ class GoLanguage :
superType.isInterface ||
superType.isMap ||
superType.isChannel ||
superType is FunctionType
superType.underlyingType is FunctionType
}

// We accept all kind of numbers if the literal is part of the call expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ package de.fraunhofer.aisec.cpg.frontends.golang
import de.fraunhofer.aisec.cpg.TranslationContext
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
import de.fraunhofer.aisec.cpg.frontends.golang.GoStandardLibrary.Modfile
import de.fraunhofer.aisec.cpg.frontends.golang.GoStandardLibrary.Parser
Expand Down Expand Up @@ -57,7 +56,6 @@ import java.net.URI
* We make use of JNA to call a dynamic library which exports C function wrappers around the Go API.
* This is needed because we cannot directly export Go structs and pointers to C.
*/
@SupportsParallelParsing(false)
@RegisterExtraPass(GoExtraPass::class)
@ReplacePass(
lang = GoLanguage::class,
Expand Down Expand Up @@ -446,29 +444,49 @@ class GoLanguageFrontend(language: Language<GoLanguageFrontend>, ctx: Translatio
}
}

/**
* Go has the concept of the [underlying type](https://go.dev/ref/spec#Underlying_types), in which
* new named types can be created on top of existing core types (such as function types, slices,
* etc.). The named types then derive certain properties of their underlying type.
*
* For type literals, e.g., a directly specified function type, the underlying type is the type
* itself.
*/
val Type?.underlyingType: Type?
get() {
return (this as? ObjectType)?.recordDeclaration?.superClasses?.singleOrNull()
return if (namedType) {
this?.superTypes?.singleOrNull()
} else {
this
}
}

val Type?.isOverlay: Boolean
/**
* In Go, types can be constructed based on existing types (see [underlyingType]) and if given a
* name, they are considered a [named type](https://go.dev/ref/spec#Types).
*
* Since these named types can also be augmented with methods (see
* https://go.dev/ref/spec#Method_sets), we need to model them as an [ObjectType] with an associated
* [RecordDeclaration] (of kind "type").
*/
val Type?.namedType: Boolean
get() {
return this is ObjectType && this.recordDeclaration?.kind == "overlay"
return this is ObjectType && this.recordDeclaration?.kind == "type"
}

val Type.isInterface: Boolean
get() {
return this is ObjectType && this.recordDeclaration?.kind == "interface"
return underlyingType is ObjectType && this.recordDeclaration?.kind == "interface"
}

val Type.isMap: Boolean
get() {
return this is ObjectType && this.name.localName == "map"
return underlyingType is ObjectType && this.name.localName == "map"
}

val Type.isChannel: Boolean
get() {
return this is ObjectType && this.name.localName == "chan"
return underlyingType is ObjectType && this.name.localName == "chan"
}

val HasType?.isNil: Boolean
Expand Down
Loading

0 comments on commit 9207616

Please sign in to comment.