Skip to content

Commit

Permalink
[swiftsrc2cpg] Implemented Actor/Async tests (#4404)
Browse files Browse the repository at this point in the history
Also put in proper method signatures and fullnames
  • Loading branch information
max-leuthaeuser authored Mar 27, 2024
1 parent ac17101 commit 25806a4
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import io.shiftleft.codepropertygraph.generated.nodes.NewNamespaceBlock
import io.shiftleft.codepropertygraph.generated.nodes.NewTypeDecl
import io.shiftleft.codepropertygraph.generated.ControlStructureTypes
import io.shiftleft.codepropertygraph.generated.PropertyNames
import io.shiftleft.passes.IntervalKeyPool

import scala.collection.mutable

Expand All @@ -34,6 +35,10 @@ object AstCreatorHelper {

trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>

private val anonClassKeyPool = new IntervalKeyPool(first = 0, last = Long.MaxValue)

protected def nextAnonClassName(): String = s"<anon-class>${anonClassKeyPool.next}"

protected def notHandledYet(node: SwiftNode): Ast = {
val text =
s"""Node type '${node.toString}' not handled yet!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,19 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {

case class AstAndMethod(ast: Ast, method: NewMethod, methodBlock: Ast)

private def paramSignature(
node: FunctionParameterClauseSyntax | ClosureShorthandParameterListSyntax | ClosureParameterClauseSyntax
): String = {
node match {
case f: FunctionParameterClauseSyntax =>
f.parameters.children.map(c => code(c.`type`)).mkString("(", ",", ")")
case c: ClosureParameterClauseSyntax =>
c.parameters.children.map(c => c.`type`.fold(Defines.Any)(code)).mkString("(", ",", ")")
case c: ClosureShorthandParameterListSyntax =>
c.children.map(_ => Defines.Any).mkString("(", ",", ")")
}
}

protected def astForFunctionLike(
node: FunctionDeclLike,
shouldCreateFunctionReference: Boolean = false,
Expand All @@ -607,16 +620,39 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
val modifiers = modifiersForFunctionLike(node)
val (methodName, methodFullName) = calcMethodNameAndFullName(node)
val filename = parserResult.filename
val (signature, returnType) = node match {
case f: FunctionDeclSyntax =>
val returnType = f.signature.returnClause.fold(Defines.Any)(c => code(c.`type`))
(s"$returnType${paramSignature(f.signature.parameterClause)}", returnType)
case a: AccessorDeclSyntax =>
val returnType = Defines.Any
(s"$returnType${a.parameters.fold("()")(code)}", returnType)
case i: InitializerDeclSyntax =>
val (_, returnType) = astParentInfo()
(s"$returnType${paramSignature(i.signature.parameterClause)}", returnType)
case _: DeinitializerDeclSyntax =>
val returnType = Defines.Any
(s"$returnType()", returnType)
case s: SubscriptDeclSyntax =>
val returnType = code(s.returnClause.`type`)
(s"$returnType${paramSignature(s.parameterClause)}", returnType)
case c: ClosureExprSyntax =>
val returnType = c.signature.flatMap(_.returnClause).fold(Defines.Any)(r => code(r.`type`))
val paramClauseCode = c.signature.flatMap(_.parameterClause).fold("()")(paramSignature)
(s"$returnType$paramClauseCode", returnType)
}
registerType(returnType)
val methodFullNameAndSignature = s"$methodFullName:$signature"

val methodRefNode_ = if (!shouldCreateFunctionReference) {
None
} else {
Option(methodRefNode(node, methodName, methodFullName, methodFullName))
Option(methodRefNode(node, methodName, methodFullNameAndSignature, methodFullNameAndSignature))
}

val callAst = if (shouldCreateAssignmentCall && shouldCreateFunctionReference) {
val idNode = identifierNode(node, methodName)
val idLocal = localNode(node, methodName, methodName, methodFullName).order(0)
val idLocal = localNode(node, methodName, methodName, methodFullNameAndSignature).order(0)
diffGraph.addEdge(localAstParentStack.head, idLocal, EdgeTypes.AST)
scope.addVariable(methodName, idLocal, BlockScope)
scope.addVariableReference(methodName, idNode)
Expand All @@ -634,32 +670,8 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
typeRefIdStack.headOption
}

val (signature, returnType) = node match {
case f: FunctionDeclSyntax =>
val returnType = f.signature.returnClause.fold(Defines.Any)(c => code(c.`type`))
(s"$returnType $methodFullName ${code(f.signature.parameterClause)}", returnType)
case a: AccessorDeclSyntax =>
val returnType = Defines.Any
(s"$returnType $methodFullName ${a.parameters.fold("()")(code)}", returnType)
case i: InitializerDeclSyntax =>
val (_, returnType) = astParentInfo()
(s"$returnType $methodFullName ${i.signature.parameterClause.parameters.children.map(code)}", returnType)
case _: DeinitializerDeclSyntax =>
val returnType = Defines.Any
(s"$returnType $methodFullName ()", returnType)
case s: SubscriptDeclSyntax =>
val returnType = code(s.returnClause.`type`)
(s"$returnType $methodFullName ${s.parameterClause.parameters.children.map(code)}", returnType)
case c: ClosureExprSyntax =>
val returnType = c.signature.flatMap(_.returnClause).fold(Defines.Any)(r => code(r.`type`))
val paramClauseCode =
c.signature.flatMap(_.parameterClause).fold("")(code).stripPrefix("(").stripSuffix(")")
(s"$returnType $methodFullName ($paramClauseCode)", returnType)
}
registerType(returnType)

val codeString = code(node)
val methodNode_ = methodNode(node, methodName, codeString, methodFullName, Option(signature), filename)
val methodNode_ = methodNode(node, methodName, codeString, methodFullNameAndSignature, Option(signature), filename)
val block = blockNode(node, PropertyDefaults.Code, Defines.Any)
methodAstParentStack.push(methodNode_)
scope.pushNewMethodScope(methodFullName, methodName, block, capturingRefNode)
Expand Down Expand Up @@ -744,7 +756,7 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
localAstParentStack.pop()
methodAstParentStack.pop()

val typeDeclAst = createFunctionTypeAndTypeDeclAst(node, methodNode_, methodName, methodFullName)
val typeDeclAst = createFunctionTypeAndTypeDeclAst(node, methodNode_, methodName, methodFullNameAndSignature)
Ast.storeInDiffGraph(astForMethod, diffGraph)
Ast.storeInDiffGraph(typeDeclAst, diffGraph)
diffGraph.addEdge(methodAstParentStack.head, methodNode_, EdgeTypes.AST)
Expand Down Expand Up @@ -839,7 +851,7 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {

private def astForSubscriptDeclSyntax(node: SubscriptDeclSyntax): Ast = notHandledYet(node)

private def handleTypeAliasInitializer(node: TypeSyntax): String = {
protected def handleTypeAliasInitializer(node: TypeSyntax): String = {
astForTypeSyntax(node).root match
case Some(id: NewIdentifier) => id.name
case Some(typeDecl: NewTypeDecl) => typeDecl.fullName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,7 @@ trait AstForSyntaxCreator(implicit withSchemaValidation: ValidationMode) { this:
// TODO: handle ellipsis
// TODO: handle defaultValue
val name = node.secondName.fold(code(node.firstName))(code)
val tpe = code(node.`type`)
registerType(tpe)
val tpe = handleTypeAliasInitializer(node.`type`)
val parameterNode =
parameterInNode(
node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import io.shiftleft.codepropertygraph.generated.EdgeTypes
trait AstForTypeSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
this: AstCreator =>

private val AnonTypeDeclNamePrefix = "_anon_cdecl"

private def typeDeclForTypeSyntax(node: TypeSyntax): NewTypeDecl = {
val name = generateUnusedVariableName(usedVariableNames, AnonTypeDeclNamePrefix)
val name = nextAnonClassName()
val (typeName, typeFullName) = calcTypeNameAndFullName(name)
registerType(typeFullName)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CodeDumperFromContentTests extends SwiftSrc2CpgSuite {
inside(cpg.method.nameExact("my_func").dumpRaw.l) {
case content :: Nil =>
content.linesIterator.map(_.strip).l shouldBe List(
"func my_func(param1: Int) -> Int { /* <=== Test0.swift:<global>:my_func */",
"func my_func(param1: Int) -> Int { /* <=== Test0.swift:<global>:my_func:Int(Int) */",
"let x: Int = foo(p: param1)",
"}"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@
package io.joern.swiftsrc2cpg.passes.ast

import io.joern.swiftsrc2cpg.testfixtures.AstSwiftSrc2CpgSuite
import io.shiftleft.codepropertygraph.generated._
import io.shiftleft.codepropertygraph.generated.nodes._
import io.shiftleft.semanticcpg.language._

class ActorTests extends AstSwiftSrc2CpgSuite {

"ActorTests" should {

"testActor1" ignore {
val cpg = code("actor MyActor1 {}")
???
"testActor1" in {
val cpg = code("actor MyActor1 {}")
val List(myActor1) = cpg.typeDecl.nameExact("MyActor1").l
myActor1.fullName shouldBe "Test0.swift:<global>:MyActor1"
myActor1.member shouldBe empty
myActor1.boundMethod shouldBe empty
}

"testActor2" ignore {
"testActor2" in {
val cpg = code("""
|actor MyActor2 {
| init() {}
| func hello() {}
|}""".stripMargin)
???
val List(myActor2) = cpg.typeDecl.nameExact("MyActor2").l
myActor2.fullName shouldBe "Test0.swift:<global>:MyActor2"
myActor2.member.name.l shouldBe List("hello")
val List(constructor) = myActor2.method.isConstructor.l
constructor.name shouldBe "init"
constructor.fullName shouldBe "Test0.swift:<global>:MyActor2:init:Test0.swift:<global>:MyActor2()"
val List(hello) = myActor2.boundMethod.l
hello.name shouldBe "hello"
hello.fullName shouldBe "Test0.swift:<global>:MyActor2:hello:ANY()"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,48 @@
package io.joern.swiftsrc2cpg.passes.ast

import io.joern.swiftsrc2cpg.testfixtures.AstSwiftSrc2CpgSuite

import io.joern.swiftsrc2cpg.testfixtures.AstSwiftSrc2CpgSuite
import io.shiftleft.codepropertygraph.generated.*
import io.shiftleft.codepropertygraph.generated.nodes.*
import io.shiftleft.semanticcpg.language.*
import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal

class AsyncSyntaxTests extends AstSwiftSrc2CpgSuite {

"AsyncSyntaxTests" should {

"testAsyncSyntax1" ignore {
"testAsyncSyntax1" in {
val cpg = code("""
|func asyncGlobal1() async { }
|func asyncGlobal2() async throws { }
|""".stripMargin)
???
val List(asyncGlobal1, asyncGlobal2) = cpg.method.internal.nameNot(NamespaceTraversal.globalNamespaceName).l
asyncGlobal1.name shouldBe "asyncGlobal1"
asyncGlobal1.fullName shouldBe "Test0.swift:<global>:asyncGlobal1:ANY()"
asyncGlobal2.name shouldBe "asyncGlobal2"
asyncGlobal2.fullName shouldBe "Test0.swift:<global>:asyncGlobal2:ANY()"
}

"testAsyncSyntax2" ignore {
"testAsyncSyntax2" in {
val cpg = code("""
|typealias AsyncFunc1 = () async -> ()
|typealias AsyncFunc2 = () async throws -> ()
|typealias AsyncFunc3 = (_ a: Bool, _ b: Bool) async throws -> ()
|""".stripMargin)
???
val List(f1, f2, f3) = cpg.typeDecl.where(_.aliasTypeFullName).l
f1.name shouldBe "AsyncFunc1"
f1.fullName shouldBe "Test0.swift:<global>:AsyncFunc1"
f1.aliasTypeFullName shouldBe Option("Test0.swift:<global>:<anon-class>0")
cpg.typeDecl.fullNameExact("Test0.swift:<global>:<anon-class>0").size shouldBe 1

f2.name shouldBe "AsyncFunc2"
f2.fullName shouldBe "Test0.swift:<global>:AsyncFunc2"
f2.aliasTypeFullName shouldBe Option("Test0.swift:<global>:<anon-class>1")
cpg.typeDecl.fullNameExact("Test0.swift:<global>:<anon-class>1").size shouldBe 1

f3.name shouldBe "AsyncFunc3"
f3.fullName shouldBe "Test0.swift:<global>:AsyncFunc3"
f3.aliasTypeFullName shouldBe Option("Test0.swift:<global>:<anon-class>2")
cpg.typeDecl.fullNameExact("Test0.swift:<global>:<anon-class>2").size shouldBe 1
}

"testAsyncSyntax3" ignore {
Expand All @@ -34,25 +54,36 @@ class AsyncSyntaxTests extends AstSwiftSrc2CpgSuite {
???
}

"testAsyncSyntax4" ignore {
val cpg = code("let _ = await asyncGlobal1()")
???
"testAsyncSyntax4" in {
val cpg = code("let _ = await asyncGlobal1()")
val List(awaitCall) = cpg.call.nameExact("<operator>.await").l
val List(call) = awaitCall.argument.isCall.l
call.name shouldBe "asyncGlobal1"
}

"testAsyncSyntax5" ignore {
"testAsyncSyntax5" in {
val cpg = code("""
|let _ = { () async in 5 }
|let _ = { () throws in 5 }
|let _ = { () async throws in 5 }""".stripMargin)
???
val List(f1, f2, f3) = cpg.method.internal.nameNot(NamespaceTraversal.globalNamespaceName).l
f1.name shouldBe "<lambda>0"
f1.fullName shouldBe "Test0.swift:<global>:<lambda>0:ANY()"
f2.name shouldBe "<lambda>1"
f2.fullName shouldBe "Test0.swift:<global>:<lambda>1:ANY()"
f3.name shouldBe "<lambda>2"
f3.fullName shouldBe "Test0.swift:<global>:<lambda>2:ANY()"
}

"testAsyncSyntax6" ignore {
val cpg = code("""
|func testAwait() async {
| let _ = await asyncGlobal1()
|}""".stripMargin)
???
val List(f) = cpg.method.nameExact("testAwait").l
val List(awaitCall) = f.call.nameExact("<operator>.await").l
val List(call) = awaitCall.argument.isCall.l
call.name shouldBe "asyncGlobal1"
}
}

Expand Down
Loading

0 comments on commit 25806a4

Please sign in to comment.