@@ -4,6 +4,7 @@ import io.joern.swiftsrc2cpg.parser.SwiftNodeSyntax.*
44import io .joern .swiftsrc2cpg .passes .GlobalBuiltins
55import io .joern .x2cpg
66import io .joern .x2cpg .datastructures .Stack .*
7+ import io .joern .x2cpg .datastructures .VariableScopeManager
78import io .joern .x2cpg .frontendspecific .swiftsrc2cpg .Defines
89import io .joern .x2cpg .{Ast , ValidationMode }
910import io .shiftleft .codepropertygraph .generated .*
@@ -230,24 +231,75 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
230231 callAst(callNode_, args, Option (baseAst))
231232 }
232233
234+ private def constructorInvocationBlockAst (expr : FunctionCallExprSyntax ): Ast = {
235+ // get call is safe as this function is guarded by isRefToConstructor
236+ val tpe = fullnameProvider.typeFullname(expr).get
237+ registerType(tpe)
238+
239+ val callExprCode = code(expr)
240+ val blockNode_ = blockNode(expr, callExprCode, tpe)
241+ scope.pushNewBlockScope(blockNode_)
242+
243+ val tmpNodeName = scopeLocalUniqueName(" tmp" )
244+ val tmpNode = identifierNode(expr, tmpNodeName, tmpNodeName, tpe)
245+ val localTmpNode = localNode(expr, tmpNodeName, tmpNodeName, tpe)
246+ scope.addVariable(tmpNodeName, localTmpNode, tpe, VariableScopeManager .ScopeType .BlockScope )
247+
248+ val allocOp = Operators .alloc
249+ val allocCallNode = callNode(expr, allocOp, allocOp, allocOp, DispatchTypes .STATIC_DISPATCH )
250+ val assignmentCallOp = Operators .assignment
251+ val assignmentCallNode =
252+ callNode(expr, s " $tmpNodeName = $allocOp" , assignmentCallOp, assignmentCallOp, DispatchTypes .STATIC_DISPATCH )
253+ val assignmentAst = callAst(assignmentCallNode, List (Ast (tmpNode), Ast (allocCallNode)))
254+
255+ val baseNode = identifierNode(expr, tmpNodeName, tmpNodeName, tpe)
256+ scope.addVariableReference(tmpNodeName, baseNode, tpe, EvaluationStrategies .BY_SHARING )
257+
258+ val constructorCallNode = callNode(
259+ expr,
260+ callExprCode,
261+ " init" ,
262+ x2cpg.Defines .UnresolvedNamespace ,
263+ DispatchTypes .STATIC_DISPATCH ,
264+ Some (x2cpg.Defines .UnresolvedSignature ),
265+ Some (Defines .Void )
266+ )
267+ setFullNameInfoForCall(expr, constructorCallNode)
268+
269+ val trailingClosureAsts = expr.trailingClosure.toList.map(astForNode)
270+ val additionalTrailingClosuresAsts = expr.additionalTrailingClosures.children.map(c => astForNode(c.closure))
271+ val args = expr.arguments.children.map(astForNode) ++ trailingClosureAsts ++ additionalTrailingClosuresAsts
272+
273+ val constructorCallAst = callAst(constructorCallNode, args, base = Some (Ast (baseNode)))
274+
275+ val retNode = identifierNode(expr, tmpNodeName, tmpNodeName, tpe)
276+ scope.addVariableReference(tmpNodeName, retNode, tpe, EvaluationStrategies .BY_SHARING )
277+ val retAst = Ast (retNode)
278+
279+ scope.popScope()
280+ Ast (blockNode_).withChildren(Seq (assignmentAst, constructorCallAst, retAst))
281+ }
282+
233283 private def astForFunctionCallExprSyntax (node : FunctionCallExprSyntax ): Ast = {
234284 val callee = node.calledExpression
235285 val calleeCode = code(callee)
236286 if (GlobalBuiltins .builtins.contains(calleeCode)) {
237287 createBuiltinStaticCall(node, callee, calleeCode)
238288 } else {
239- // TODO: extend the GsonTypeInfoReader to query for information whether
240- // the call is a call to a static function and generate a proper static call here
241289 callee match {
242290 case m : MemberAccessExprSyntax if m.base.isEmpty || code(m.base.get) == " self" =>
243291 // referencing implicit self
244292 val selfTpe = typeForSelfExpression()
245293 val selfNode = identifierNode(node, " self" , " self" , selfTpe)
246294 scope.addVariableReference(" self" , selfNode, selfTpe, EvaluationStrategies .BY_REFERENCE )
247295 handleCallNodeArgs(node, Ast (selfNode), code(m.declName.baseName))
296+ case m : MemberAccessExprSyntax if isRefToStaticFunction(calleeCode) =>
297+ createBuiltinStaticCall(node, callee, calleeCode)
248298 case m : MemberAccessExprSyntax =>
249299 val memberCode = code(m.declName)
250300 handleCallNodeArgs(node, astForNode(m.base.get), memberCode)
301+ case other if isRefToConstructor(node, other) =>
302+ constructorInvocationBlockAst(node)
251303 case other if isRefToClosure(node, other) =>
252304 astForClosureCall(node)
253305 case declReferenceExprSyntax : DeclReferenceExprSyntax if code(declReferenceExprSyntax) != " self" =>
@@ -271,7 +323,6 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
271323
272324 val trailingClosureAsts = expr.trailingClosure.toList.map(astForNode)
273325 val additionalTrailingClosuresAsts = expr.additionalTrailingClosures.children.map(c => astForNode(c.closure))
274-
275326 val args = expr.arguments.children.map(astForNode) ++ trailingClosureAsts ++ additionalTrailingClosuresAsts
276327
277328 val callExprCode = code(expr)
@@ -290,19 +341,42 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
290341 private def isRefToClosure (func : FunctionCallExprSyntax , node : ExprSyntax ): Boolean = {
291342 if (! config.swiftBuild) {
292343 // Early exit; without types from the compiler we will be unable to identify closure calls anyway.
293- // This saves us the typeFullname lookup below.
344+ // This saves us the fullnameProvider lookups below.
294345 return false
295346 }
296347 node match {
297348 case refExpr : DeclReferenceExprSyntax
298- if refExpr.baseName.isInstanceOf [identifier] && refExpr.argumentNames.isEmpty &&
349+ if refExpr.baseName.isInstanceOf [identifier] &&
299350 fullnameProvider.declFullname(func).isEmpty &&
300351 fullnameProvider.typeFullname(refExpr).exists(_.startsWith(s " ${Defines .Function }< " )) =>
301352 true
302353 case _ => false
303354 }
304355 }
305356
357+ private def isRefToStaticFunction (calleeCode : String ): Boolean = {
358+ // TODO: extend the GsonTypeInfoReader to query for information whether the call is a call to a static function
359+ calleeCode.headOption.exists(_.isUpper) && ! calleeCode.contains(" (" ) && ! calleeCode.contains(" )" )
360+ }
361+
362+ private def isRefToConstructor (func : FunctionCallExprSyntax , node : ExprSyntax ): Boolean = {
363+ if (! config.swiftBuild) {
364+ // Early exit; without types from the compiler we will be unable to identify constructor calls anyway.
365+ // This saves us the fullnameProvider lookups below.
366+ return false
367+ }
368+ node match {
369+ case refExpr : DeclReferenceExprSyntax
370+ if refExpr.baseName.isInstanceOf [identifier] &&
371+ fullnameProvider.typeFullname(func).nonEmpty &&
372+ fullnameProvider.declFullname(func).exists { fullName =>
373+ fullName.contains(" .init(" ) && fullName.contains(" )->" )
374+ } =>
375+ true
376+ case _ => false
377+ }
378+ }
379+
306380 private def astForGenericSpecializationExprSyntax (node : GenericSpecializationExprSyntax ): Ast = {
307381 astForNode(node.expression)
308382 }
0 commit comments