Skip to content

Commit 251ed58

Browse files
[php2cpg] Add method bindings to class methods to fix dataflow (#5592)
* [php2cpg] Add method bindings to fix dataflow * Use the empty string for php2cpg signatures * Sort test output for stability
1 parent 19dbdce commit 251ed58

File tree

9 files changed

+94
-71
lines changed

9 files changed

+94
-71
lines changed

joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForControlStructuresCreator.scala

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package io.joern.php2cpg.astcreation
22

3-
import io.joern.php2cpg.utils.{BlockScope, NamespaceScope, TypeScope, MethodScope}
3+
import io.joern.php2cpg.utils.{BlockScope, MethodScope, NamespaceScope, TypeScope}
44
import io.joern.php2cpg.astcreation.AstCreator.TypeConstants
55
import io.joern.php2cpg.parser.Domain.*
6-
import io.joern.x2cpg.Defines.UnresolvedSignature
76
import io.joern.x2cpg.utils.AstPropertiesUtil.RootProperties
87
import io.joern.x2cpg.{Ast, Defines, ValidationMode}
98
import io.shiftleft.codepropertygraph.generated.nodes.*
10-
import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, EdgeTypes, Operators}
9+
import io.shiftleft.codepropertygraph.generated.{
10+
ControlStructureTypes,
11+
DispatchTypes,
12+
EdgeTypes,
13+
Operators,
14+
PropertyDefaults
15+
}
1116
import io.shiftleft.proto.cpg.Cpg.CpgStruct.Edge.EdgeType
1217

1318
trait AstForControlStructuresCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>
@@ -200,17 +205,9 @@ trait AstForControlStructuresCreator(implicit withSchemaValidation: ValidationMo
200205

201206
// Update asts
202207
val nextIterIdent = astForIdentifierWithLocalRef(iterIdentifier.copy, localN)
203-
val nextSignature = "void()"
204208
val nextCallCode = s"${nextIterIdent.rootCodeOrEmpty}${InstanceMethodDelimiter}next()"
205-
val nextCallNode = callNode(
206-
stmt,
207-
nextCallCode,
208-
"next",
209-
"Iterator.next",
210-
DispatchTypes.DYNAMIC_DISPATCH,
211-
Some(nextSignature),
212-
Some(Defines.Any)
213-
)
209+
val nextCallNode =
210+
callNode(stmt, nextCallCode, "next", "Iterator.next", DispatchTypes.DYNAMIC_DISPATCH, None, Some(Defines.Any))
214211
val nextCallAst = callAst(nextCallNode, base = Option(nextIterIdent))
215212
val itemUpdateAst = itemInitAst.root match {
216213
case Some(initRoot: AstNodeNew) => itemInitAst.subTreeCopy(initRoot)
@@ -240,7 +237,6 @@ trait AstForControlStructuresCreator(implicit withSchemaValidation: ValidationMo
240237
// create assignment for value-part
241238
val valueAssign = {
242239
val iteratorIdentifierAst = astForIdentifierWithLocalRef(iteratorIdentifier, localN)
243-
val currentCallSignature = s"$UnresolvedSignature(0)"
244240
val currentCallCode = s"${iteratorIdentifierAst.rootCodeOrEmpty}${InstanceMethodDelimiter}current()"
245241
// `current` function is used to get the current element of given array
246242
// see https://www.php.net/manual/en/function.current.php & https://www.php.net/manual/en/iterator.current.php
@@ -250,7 +246,7 @@ trait AstForControlStructuresCreator(implicit withSchemaValidation: ValidationMo
250246
"current",
251247
"Iterator.current",
252248
DispatchTypes.DYNAMIC_DISPATCH,
253-
Some(currentCallSignature),
249+
None,
254250
Some(Defines.Any)
255251
)
256252
val currentCallAst = callAst(currentCallNode, base = Option(iteratorIdentifierAst))
@@ -268,19 +264,11 @@ trait AstForControlStructuresCreator(implicit withSchemaValidation: ValidationMo
268264
// try to create assignment for key-part
269265
val keyAssignOption = stmt.keyVar.map(keyVar =>
270266
val iteratorIdentifierAst = astForIdentifierWithLocalRef(iteratorIdentifier.copy, localN)
271-
val keyCallSignature = s"$UnresolvedSignature(0)"
272267
val keyCallCode = s"${iteratorIdentifierAst.rootCodeOrEmpty}${InstanceMethodDelimiter}key()"
273268
// `key` function is used to get the key of the current element
274269
// see https://www.php.net/manual/en/function.key.php & https://www.php.net/manual/en/iterator.key.php
275-
val keyCallNode = callNode(
276-
stmt,
277-
keyCallCode,
278-
"key",
279-
"Iterator.key",
280-
DispatchTypes.DYNAMIC_DISPATCH,
281-
Some(keyCallSignature),
282-
Some(Defines.Any)
283-
)
270+
val keyCallNode =
271+
callNode(stmt, keyCallCode, "key", "Iterator.key", DispatchTypes.DYNAMIC_DISPATCH, None, Some(Defines.Any))
284272
val keyCallAst = callAst(keyCallNode, base = Option(iteratorIdentifierAst))
285273
simpleAssignAst(stmt, astForExpr(keyVar), keyCallAst)
286274
)

joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForExpressionsCreator.scala

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,10 @@ import io.joern.php2cpg.astcreation.AstCreator.{NameConstants, TypeConstants, op
44
import io.joern.php2cpg.datastructures.ArrayIndexTracker
55
import io.joern.php2cpg.parser.Domain.*
66
import io.joern.php2cpg.utils.BlockScope
7-
import io.joern.x2cpg.Defines.{UnresolvedNamespace, UnresolvedSignature}
8-
import io.joern.x2cpg.Defines.UnresolvedSignature
97
import io.joern.x2cpg.utils.AstPropertiesUtil.RootProperties
108
import io.joern.x2cpg.{Ast, Defines, ValidationMode}
119
import io.shiftleft.codepropertygraph.generated.nodes.*
12-
import io.shiftleft.codepropertygraph.generated.{
13-
ControlStructureTypes,
14-
DispatchTypes,
15-
EdgeTypes,
16-
Operators,
17-
PropertyNames
18-
}
10+
import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, Operators, PropertyNames}
1911
import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal
2012

2113
import scala.collection.mutable
@@ -110,9 +102,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
110102

111103
val fullName = getMfn(call, name)
112104

113-
// Use method signature for methods that can be linked to avoid varargs issue.
114-
val signature = s"$UnresolvedSignature(${call.args.size})"
115-
val callRoot = callNode(call, code, name, fullName, dispatchType, Some(signature), Some(Defines.Any))
105+
val callRoot = callNode(call, code, name, fullName, dispatchType, None, Some(Defines.Any))
116106

117107
val receiverAst = targetAst match {
118108
case None if !scope.isTopLevel =>
@@ -140,9 +130,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
140130
case _ => getMfn(call, name)
141131
}
142132

143-
// Use method signature for methods that can be linked to avoid varargs issue.
144-
val signature = s"$UnresolvedSignature(${call.args.size})"
145-
val callRoot = callNode(call, code, name, fullName, dispatchType, Some(signature), Some(Defines.Any))
133+
val callRoot = callNode(call, code, name, fullName, dispatchType, None, Some(Defines.Any))
146134

147135
callAst(callRoot, arguments)
148136
}
@@ -886,7 +874,6 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
886874

887875
// Init node
888876
val initArgs = expr.args.map(astForCallArg)
889-
val initSignature = s"$UnresolvedSignature(${initArgs.size})"
890877
val initFullName = s"$className$MethodDelimiter$ConstructorMethodName"
891878
val initCode = s"new $className(${initArgs.map(_.rootCodeOrEmpty).mkString(",")})"
892879
val maybeTypeHint = scope.resolveIdentifier(className).map(_.name) // consider imported or defined types
@@ -896,7 +883,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) {
896883
ConstructorMethodName,
897884
initFullName,
898885
DispatchTypes.STATIC_DISPATCH,
899-
Some(initSignature),
886+
None,
900887
maybeTypeHint.orElse(Some(Defines.Any)) // TODO Review Note: Should the hint be under dynamicTypeHintFullName?
901888
)
902889
val initReceiver = astForIdentifierWithLocalRef(tmpIdentifier.copy, local)

joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForFunctionsCreator.scala

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import io.joern.php2cpg.astcreation.AstCreator.{NameConstants, TypeConstants}
44
import io.joern.php2cpg.parser.Domain.*
55
import io.joern.php2cpg.parser.Domain.PhpModifiers.containsAccessModifier
66
import io.joern.php2cpg.utils.MethodScope
7-
import io.joern.x2cpg.Defines.UnresolvedSignature
87
import io.joern.x2cpg.utils.AstPropertiesUtil.RootProperties
98
import io.joern.x2cpg.{Ast, Defines, ValidationMode}
109
import io.shiftleft.codepropertygraph.generated.nodes.*
@@ -110,8 +109,6 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
110109
val methodName = decl.name.name
111110
val fullName = fullNameOverride.getOrElse(composeMethodFullName(methodName))
112111

113-
val signature = s"$UnresolvedSignature(${decl.params.size})"
114-
115112
val parameters = thisParam.toList ++ decl.params.zipWithIndex.map { case (param, idx) =>
116113
astForParam(param, idx + 1)
117114
}
@@ -132,7 +129,7 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
132129
val methodRef =
133130
if methodName == NamespaceTraversal.globalNamespaceName then None
134131
else Option(methodRefNode(decl, s"$methodCode", fullName, Defines.Any))
135-
val method = methodNode(decl, methodName, methodCode, fullName, Some(signature), relativeFileName)
132+
val method = methodNode(decl, methodName, methodCode, fullName, None, relativeFileName)
136133

137134
scope.surroundingScopeFullName.map(method.astParentFullName(_))
138135
scope.surroundingAstLabel.map(method.astParentType(_))
@@ -157,11 +154,9 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
157154
scope.popScope()
158155
val methodAst = methodAstWithAnnotations(method, parameters, methodBody, methodReturn, modifiers, attributeAsts)
159156

160-
Ast.storeInDiffGraph(methodAst, diffGraph)
161157
val methodRefAst = methodRef.map(Ast(_)).getOrElse(Ast())
162158

163-
// method gets added via the AST_PARENT_FULLNAME property
164-
Ast()
159+
methodAst
165160
}
166161

167162
private def thisParamAstForMethod(originNode: PhpNode): Ast = {
@@ -195,8 +190,6 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
195190
protected def defaultConstructorAst(originNode: PhpNode): Ast = {
196191
val fullName = composeMethodFullName(ConstructorMethodName)
197192

198-
val signature = s"$UnresolvedSignature(0)"
199-
200193
val modifiers =
201194
List(ModifierTypes.VIRTUAL, ModifierTypes.PUBLIC, ModifierTypes.CONSTRUCTOR).map(modifierNode(originNode, _))
202195

@@ -206,7 +199,7 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
206199
astForMemberAssignment(fieldInit.originNode, fieldInit.memberNode, fieldInit.value, isField = true)
207200
}
208201

209-
val method = methodNode(originNode, ConstructorMethodName, fullName, fullName, Some(signature), relativeFileName)
202+
val method = methodNode(originNode, ConstructorMethodName, fullName, fullName, None, relativeFileName)
210203
val methodRef = methodRefNode(originNode, fullName, fullName, Defines.Any)
211204

212205
val methodBodyBlock = blockNode(originNode)
@@ -221,12 +214,10 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
221214
val methodReturn = methodReturnNode(originNode, Defines.Any)
222215

223216
val methodAst = methodAstWithAnnotations(method, thisParam :: Nil, methodBody, methodReturn, modifiers)
224-
Ast.storeInDiffGraph(methodAst, diffGraph)
225217

226218
scope.popScope()
227219

228-
// AST gets added via AST_PARENT_FULLNAME property
229-
Ast()
220+
methodAst
230221
}
231222

232223
protected def astForAttributeGroup(attrGrp: PhpAttributeGroup): Seq[Ast] = {
@@ -266,13 +257,12 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
266257
case Nil => None
267258

268259
case inits =>
269-
val signature = s"${TypeConstants.Void}()"
270-
val fullName = composeMethodFullName(Defines.StaticInitMethodName)
260+
val fullName = composeMethodFullName(Defines.StaticInitMethodName)
271261
val methodNode_ = methodNode(
272262
node,
273263
Defines.StaticInitMethodName,
274264
fullName,
275-
signature,
265+
PropertyDefaults.Signature,
276266
Option(relativeFileName).getOrElse(PropertyDefaults.Filename)
277267
)
278268

joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForTypesCreator.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,23 @@ import io.joern.php2cpg.parser.Domain.*
55
import io.joern.php2cpg.utils.TypeScope
66
import io.joern.x2cpg.{Ast, Defines, ValidationMode}
77
import io.shiftleft.codepropertygraph.generated.nodes.{
8+
NewBinding,
89
NewCall,
910
NewFieldIdentifier,
1011
NewIdentifier,
1112
NewMember,
13+
NewMethod,
1214
NewTypeDecl,
1315
NewTypeRef
1416
}
15-
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, ModifierTypes, NodeTypes, Operators}
17+
import io.shiftleft.codepropertygraph.generated.{
18+
DispatchTypes,
19+
EdgeTypes,
20+
ModifierTypes,
21+
NodeTypes,
22+
Operators,
23+
PropertyDefaults
24+
}
1625
import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal
1726

1827
trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>
@@ -52,6 +61,14 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
5261
Ast(typeDecl).withChildren(modifiers).withChildren(bodyStmts :+ clinitAst).withChildren(annotationAsts)
5362
}
5463

64+
private def addBindingsForTypeDecl(typeDecl: NewTypeDecl, bodyAsts: List[Ast]): Unit = {
65+
bodyAsts.flatMap(_.root).collect { case method: NewMethod =>
66+
val binding = NewBinding().name(method.name).methodFullName(method.fullName)
67+
diffGraph.addEdge(typeDecl, binding, EdgeTypes.BINDS)
68+
diffGraph.addEdge(binding, method, EdgeTypes.REF)
69+
}
70+
}
71+
5572
private def astForAnonymousClass(
5673
stmt: PhpClassLikeStmt,
5774
dynamicStmts: List[PhpStmt],
@@ -140,6 +157,8 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
140157
diffGraph.addNode(metaTypeDeclMember)
141158
}
142159

160+
addBindingsForTypeDecl(typeDeclTemp, bodyStmts)
161+
143162
val prefixAst = createTypeRefPointer(typeDeclTemp)
144163
val typeDeclAst = Ast(typeDeclTemp)
145164
.withChildren(modifiers)
@@ -245,6 +264,7 @@ trait AstForTypesCreator(implicit withSchemaValidation: ValidationMode) { this:
245264
scope.popScope()
246265

247266
val classTypeDeclAst = Ast(typeDecl).withChildren(modifiers).withChildren(bodyStmts).withChildren(annotationAsts)
267+
addBindingsForTypeDecl(typeDecl, bodyStmts)
248268

249269
scope.pushNewScope(TypeScope(metaTypeDeclNode, metaTypeDeclFullName))
250270

joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/CallTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class CallTests extends PhpCode2CpgFixture {
7373
inside(cpg.call.l) { case List(fooCall) =>
7474
fooCall.name shouldBe "foo"
7575
fooCall.methodFullName shouldBe s"foo"
76-
fooCall.signature shouldBe s"${Defines.UnresolvedSignature}(1)"
76+
fooCall.signature shouldBe ""
7777
fooCall.receiver.isEmpty shouldBe true
7878
fooCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH
7979
fooCall.lineNumber shouldBe Some(2)

joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/ClosureTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class ClosureTests extends PhpCode2CpgFixture {
8181
val expectedName = s"foo.php:<global>.<lambda>0"
8282
closureMethod.name shouldBe expectedName
8383
closureMethod.fullName shouldBe expectedName
84-
closureMethod.signature shouldBe s"${Defines.UnresolvedSignature}(1)"
84+
closureMethod.signature shouldBe ""
8585
closureMethod.code shouldBe s"function $expectedName($$value) use($$use1, &$$use2)"
8686
closureMethod.parameter.size shouldBe 1
8787

@@ -153,7 +153,7 @@ class ClosureTests extends PhpCode2CpgFixture {
153153
val expectedName = "foo.php:<global>.<lambda>0"
154154
closureMethod.name shouldBe expectedName
155155
closureMethod.fullName shouldBe expectedName
156-
closureMethod.signature shouldBe s"${Defines.UnresolvedSignature}(1)"
156+
closureMethod.signature shouldBe ""
157157
closureMethod.code shouldBe s"function $expectedName($$value)"
158158
closureMethod.parameter.size shouldBe 1
159159

joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/MethodTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class MethodTests extends PhpCode2CpgFixture {
2121

2222
inside(cpg.method.name("foo").l) { case List(fooMethod) =>
2323
fooMethod.fullName shouldBe "foo"
24-
fooMethod.signature shouldBe s"${Defines.UnresolvedSignature}(0)"
24+
fooMethod.signature shouldBe ""
2525
fooMethod.lineNumber shouldBe Some(2)
2626
fooMethod.lineNumberEnd shouldBe Some(2)
2727
fooMethod.code shouldBe "function foo()"

joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/OperatorTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ class OperatorTests extends PhpCode2CpgFixture {
710710
absCall.name shouldBe "abs"
711711
absCall.methodFullName shouldBe "abs"
712712
absCall.code shouldBe "abs($a)"
713-
absCall.signature shouldBe s"${Defines.UnresolvedSignature}(1)"
713+
absCall.signature shouldBe ""
714714
}
715715
}
716716

0 commit comments

Comments
 (0)