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

[swiftsrc2cpg] Implemented Actor/Async tests #4404

Merged
merged 3 commits into from
Mar 27, 2024
Merged
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 @@ -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