Skip to content

Commit d681dcf

Browse files
fix computed property call properties
1 parent 550578d commit d681dcf

File tree

4 files changed

+62
-68
lines changed

4 files changed

+62
-68
lines changed

joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForDeclSyntaxCreator.scala

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,12 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
10101010
scope.pushNewMethodScope(methodFullName, methodName, block, capturingRefNode)
10111011
localAstParentStack.push(block)
10121012

1013+
val selfTpe = fullNameOfEnclosingTypeDecl()
1014+
val selfParameterNode =
1015+
parameterInNode(node, "self", "self", 0, false, EvaluationStrategies.BY_SHARING, selfTpe)
1016+
scope.addVariable("self", selfParameterNode, selfTpe, VariableScopeManager.ScopeType.MethodScope)
1017+
val selfParameterNodeAst = Ast(selfParameterNode)
1018+
10131019
val parameterAsts = if (parameters.isEmpty && accessorSpecifier == "set") {
10141020
val name = "newValue" // Swift default parameter name for set accessors
10151021
val parameterNode = parameterInNode(node, name, name, 1, false, EvaluationStrategies.BY_VALUE, Some(tpe))
@@ -1032,34 +1038,13 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
10321038
}
10331039
.getOrElse(List.empty[Ast])
10341040

1035-
val syntheticBodyStmtAst = if (bodyStmtAsts.isEmpty) {
1036-
accessorSpecifier match {
1037-
case "set" =>
1038-
val selfNode = identifierNode(node, "self", "self", fullNameOfEnclosingTypeDecl())
1039-
scope.addVariableReference("self", selfNode, selfNode.typeFullName, EvaluationStrategies.BY_REFERENCE)
1040-
val fieldAccess = fieldAccessAst(node, node, Ast(selfNode), s"self.$variableName", variableName, tpe)
1041-
val sourceName = parameterAsts.head.root.collect { case p: NewMethodParameterIn => p.name }.get
1042-
val barIdentifier = identifierNode(node, sourceName, sourceName, tpe)
1043-
scope.addVariableReference(sourceName, barIdentifier, tpe, EvaluationStrategies.BY_REFERENCE)
1044-
List(createAssignmentCallAst(node, fieldAccess, Ast(barIdentifier), s"self.$variableName = $sourceName"))
1045-
case "get" =>
1046-
val selfNode = identifierNode(node, "self", "self", fullNameOfEnclosingTypeDecl())
1047-
scope.addVariableReference("self", selfNode, selfNode.typeFullName, EvaluationStrategies.BY_REFERENCE)
1048-
val fieldAccess = fieldAccessAst(node, node, Ast(selfNode), s"self.$variableName", variableName, tpe)
1049-
List(returnAst(returnNode(node, variableName), List(fieldAccess)))
1050-
case _ => List.empty[Ast]
1051-
}
1052-
} else {
1053-
bodyStmtAsts
1054-
}
1055-
10561041
val methodReturnNode_ = methodReturnNode(node, returnType)
10571042

1058-
val blockAst_ = blockAst(block, syntheticBodyStmtAst)
1043+
val blockAst_ = blockAst(block, bodyStmtAsts)
10591044
val astForMethod =
10601045
methodAstWithAnnotations(
10611046
methodNode_,
1062-
parameterAsts,
1047+
selfParameterNodeAst +: parameterAsts,
10631048
blockAst_,
10641049
methodReturnNode_,
10651050
modifiers = modifiers,
@@ -1245,30 +1230,9 @@ trait AstForDeclSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
12451230
}
12461231
.getOrElse(List.empty[Ast])
12471232

1248-
val syntheticBodyStmtAst = if (bodyStmtAsts.isEmpty) {
1249-
accessorSpecifier match {
1250-
case "set" =>
1251-
val selfNode = identifierNode(node, "self", "self", fullNameOfEnclosingTypeDecl())
1252-
scope.addVariableReference("self", selfNode, selfNode.typeFullName, EvaluationStrategies.BY_REFERENCE)
1253-
val fieldAccess = fieldAccessAst(node, node, Ast(selfNode), s"self.$variableName", variableName, tpe)
1254-
val sourceName = parameterAsts.head.root.collect { case p: NewMethodParameterIn => p.name }.get
1255-
val barIdentifier = identifierNode(node, sourceName, sourceName, tpe)
1256-
scope.addVariableReference(sourceName, barIdentifier, tpe, EvaluationStrategies.BY_REFERENCE)
1257-
List(createAssignmentCallAst(node, fieldAccess, Ast(barIdentifier), s"self.$variableName = $sourceName"))
1258-
case "get" =>
1259-
val selfNode = identifierNode(node, "self", "self", fullNameOfEnclosingTypeDecl())
1260-
scope.addVariableReference("self", selfNode, selfNode.typeFullName, EvaluationStrategies.BY_REFERENCE)
1261-
val fieldAccess = fieldAccessAst(node, node, Ast(selfNode), s"self.$variableName", variableName, tpe)
1262-
List(returnAst(returnNode(node, variableName), List(fieldAccess)))
1263-
case _ => List.empty[Ast]
1264-
}
1265-
} else {
1266-
bodyStmtAsts
1267-
}
1268-
12691233
val methodReturnNode_ = methodReturnNode(node, returnType)
12701234

1271-
val blockAst_ = blockAst(block, syntheticBodyStmtAst)
1235+
val blockAst_ = blockAst(block, bodyStmtAsts)
12721236
val astForMethod =
12731237
methodAstWithAnnotations(
12741238
methodNode_,

joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/passes/ExtensionsPass.scala

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,23 +112,34 @@ class ExtensionsPass(
112112
}
113113
}
114114

115-
/** Rewrites computed-property-style member accesses to the corresponding getter call when known.
115+
/** Rewrites computed property member accesses to the corresponding getter call when resolvable.
116116
*
117-
* For each `fieldAccess`, the base type and member name are used to derive candidate fullNames (e.g., `T.m` and
118-
* `T.m.getter`). If a matching entry exists in `memberPropertyMapping`, the access node's `METHOD_FULL_NAME` is
119-
* updated to the resolved getter fullName.
117+
* For each `fieldAccess` call node:
118+
* - reads the base expression's `TYPE_FULL_NAME` and the member name from the access arguments
119+
* - derives candidate member fullNames (e.g., `T.m` and `T.m.getter`)
120+
* - if a match exists in `memberPropertyMapping`, updates the access to reference the getter by setting:
121+
* - `NAME` and `METHOD_FULL_NAME` to the resolved getter
122+
* - `DISPATCH_TYPE` to `DYNAMIC_DISPATCH`
123+
* - ensures a `RECEIVER` edge from the access to the base expression and fixes argument indices/orders
120124
*/
121125
private def handleMemberPropertyCalls(diffGraph: DiffGraphBuilder): Unit = {
122126
val fieldAccesses = cpg.fieldAccess
123127
fieldAccesses.foreach { fieldAccess =>
124-
val memberFullNameMaybe = for {
125-
baseFullName <- fieldAccess.arguments(1).headOption.flatMap(typeFullNameOf)
128+
for {
129+
base <- fieldAccess.arguments(1).headOption
130+
baseFullName <- base.flatMap(typeFullNameOf)
126131
memberName <- fieldAccess.arguments(2).headOption.map(_.code)
127-
} yield Seq(s"$baseFullName.$memberName.getter", s"$baseFullName.$memberName")
128-
129-
memberFullNameMaybe.getOrElse(Seq.empty).foreach { memberFullName =>
130-
memberPropertyMapping.get(memberFullName).foreach { propertyFullName =>
131-
diffGraph.setNodeProperty(fieldAccess, PropertyNames.MethodFullName, propertyFullName)
132+
} {
133+
Seq(s"$memberName.getter", memberName).foreach { memberName =>
134+
val memberFullName = s"$baseFullName.$memberName"
135+
memberPropertyMapping.get(memberFullName).foreach { propertyFullName =>
136+
diffGraph.setNodeProperty(fieldAccess, PropertyNames.Name, memberName)
137+
diffGraph.setNodeProperty(fieldAccess, PropertyNames.MethodFullName, propertyFullName)
138+
diffGraph.setNodeProperty(fieldAccess, PropertyNames.DispatchType, DispatchTypes.DYNAMIC_DISPATCH)
139+
diffGraph.addEdge(fieldAccess, base, EdgeTypes.RECEIVER)
140+
diffGraph.setNodeProperty(base, PropertyNames.ArgumentIndex, 0)
141+
diffGraph.setNodeProperty(base, PropertyNames.Order, 1)
142+
}
132143
}
133144
}
134145
}

joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/ExtensionWithCompilerTests.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.joern.swiftsrc2cpg.passes.ast
22

33
import io.joern.swiftsrc2cpg.testfixtures.SwiftCompilerSrc2CpgSuite
4+
import io.shiftleft.codepropertygraph.generated.DispatchTypes
45
import io.shiftleft.semanticcpg.language.*
56

67
class ExtensionWithCompilerTests extends SwiftCompilerSrc2CpgSuite {
@@ -84,6 +85,32 @@ class ExtensionWithCompilerTests extends SwiftCompilerSrc2CpgSuite {
8485
"SwiftTest.Foo<extension>.y:Swift.Int"
8586
)
8687

88+
val List(aGetter) = cpg.call.nameExact("a.getter").codeExact("foo.a").l
89+
aGetter.dispatchType shouldBe DispatchTypes.DYNAMIC_DISPATCH
90+
aGetter.methodFullName shouldBe "SwiftTest.Foo.a.getter:Swift.Int"
91+
aGetter.receiver.argumentIndex.loneElement shouldBe 0
92+
aGetter.receiver.code.loneElement shouldBe "foo"
93+
94+
val List(bGetter) = cpg.call.nameExact("b.getter").codeExact("foo.b").l
95+
bGetter.dispatchType shouldBe DispatchTypes.DYNAMIC_DISPATCH
96+
bGetter.methodFullName shouldBe "SwiftTest.Foo<extension>.b.getter:Swift.Int"
97+
bGetter.receiver.argumentIndex.loneElement shouldBe 0
98+
bGetter.receiver.code.loneElement shouldBe "foo"
99+
100+
val List(xGetter) = cpg.call.nameExact("x").l
101+
xGetter.dispatchType shouldBe DispatchTypes.DYNAMIC_DISPATCH
102+
xGetter.code shouldBe "foo.x"
103+
xGetter.methodFullName shouldBe "SwiftTest.Foo.x:Swift.Int"
104+
xGetter.receiver.argumentIndex.loneElement shouldBe 0
105+
xGetter.receiver.code.loneElement shouldBe "foo"
106+
107+
val List(yGetter) = cpg.call.nameExact("y").l
108+
yGetter.dispatchType shouldBe DispatchTypes.DYNAMIC_DISPATCH
109+
yGetter.code shouldBe "foo.y"
110+
yGetter.methodFullName shouldBe "SwiftTest.Foo<extension>.y:Swift.Int"
111+
yGetter.receiver.argumentIndex.loneElement shouldBe 0
112+
yGetter.receiver.code.loneElement shouldBe "foo"
113+
87114
cpg.typeDecl.nameExact("Foo").member.name.sorted shouldBe Seq("a", "b", "x", "y")
88115
}
89116

joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/SimpleAstCreationPassTest.scala

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -479,22 +479,14 @@ class SimpleAstCreationPassTest extends AstSwiftSrc2CpgSuite {
479479

480480
val List(get, set, willSet, didSet) = cpg.typeDecl.nameExact("Foo").boundMethod.nameNot("init").l
481481
get.fullName shouldBe "Test0.swift:<global>.Foo.bar.getter:Swift.Int"
482-
val List(getReturn) = get.body.astChildren.isReturn.l
483-
getReturn.code shouldBe "bar"
484-
val List(thisBar) = getReturn.astChildren.isCall.l
485-
thisBar.name shouldBe Operators.fieldAccess
486-
thisBar.typeFullName shouldBe "Swift.Int"
487-
inside(thisBar.argument.l) { case List(base: Identifier, field: FieldIdentifier) =>
488-
base.name shouldBe "self"
489-
base.typeFullName shouldBe "Test0.swift:<global>.Foo"
490-
field.canonicalName shouldBe "bar"
491-
}
492482

493483
set.fullName shouldBe "Test0.swift:<global>.Foo.bar.setter:Swift.Int"
494-
val List(setParam) = set.parameter.l
484+
val List(self, setParam) = set.parameter.l
485+
self.name shouldBe "self"
486+
self.typeFullName shouldBe "Test0.swift:<global>.Foo"
487+
495488
setParam.name shouldBe "newValue"
496489
setParam.typeFullName shouldBe "Swift.Int"
497-
set.body.astChildren.isCall.code.l shouldBe List("self.bar = newValue")
498490

499491
willSet.fullName shouldBe "Test0.swift:<global>.Foo.bar.willSet:Swift.Int"
500492
didSet.fullName shouldBe "Test0.swift:<global>.Foo.bar.didSet:Swift.Int"

0 commit comments

Comments
 (0)