Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package io.joern.gosrc2cpg.astcreation
import io.joern.gosrc2cpg.datastructures.MethodCacheMetaData
import io.joern.gosrc2cpg.parser.ParserAst.*
import io.joern.gosrc2cpg.parser.{ParserKeys, ParserNodeInfo}
import io.joern.x2cpg.{Ast, ValidationMode, Defines as XDefines}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, PropertyNames}
import ujson.Value
import scala.util.{Success, Try}
trait AstForMethodCallExpressionCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>
def astForCallExpression(expr: ParserNodeInfo): Seq[Ast] = {
val (methodName, signature, fullName, typeFullName, receiverAst) =
preReqForCallNode(createParserNodeInfo(expr.json(ParserKeys.Fun)))
val cpgCall = callNode(
expr,
expr.code,
methodName,
fullName,
DispatchTypes.STATIC_DISPATCH,
Some(signature),
Some(typeFullName)
)
Seq(callAst(cpgCall, astForArgs(expr.json(ParserKeys.Args)), receiverAst.headOption))
}
protected def astForConstructorCall(compositeLit: ParserNodeInfo): Seq[Ast] = {
val (methodName, signature, fullName, _, _) = preReqForCallNode(
createParserNodeInfo(compositeLit.json(ParserKeys.Type))
)
val cpgCall = callNode(
compositeLit,
compositeLit.code,
methodName,
fullName + "." + XDefines.ConstructorMethodName,
DispatchTypes.STATIC_DISPATCH,
Some(signature),
Some(fullName)
)
Seq(callAst(cpgCall, astForStructureDeclarationArgument(compositeLit.json(ParserKeys.Elts))))
}
private def preReqForCallNode(funcDetails: ParserNodeInfo): (String, String, String, String, Seq[Ast]) = {
val (aliasOpt, methodName) = funcDetails.node match
case Ident =>
(None, funcDetails.json(ParserKeys.Name).str)
case SelectorExpr =>
val xNode = createParserNodeInfo(funcDetails.json(ParserKeys.X))
(Some(xNode), funcDetails.json(ParserKeys.Sel)(ParserKeys.Name).str)
case x =>
logger.warn(
s"Unhandled class ${x.getClass} under astForCallExpression! file -> ${parserResult.fullPath} -> Line no -> ${funcDetails.lineNumber.get}"
)
(None, "")
callMethodFullNameTypeFullNameAndSignature(methodName, aliasOpt)
}
private def astForStructureDeclarationArgument(args: Value): Seq[Ast] = {
args.arrOpt
.getOrElse(Seq.empty)
.flatMap(x => {
val argument = createParserNodeInfo(x)
argument.node match
case BasicLit => astForNode(argument)
case Ident => astForNode(argument)
case _ => astForNode(argument)
})
.toSeq
}
private def astForArgs(args: Value): Seq[Ast] = {
args.arrOpt
.getOrElse(Seq.empty)
.flatMap(x => {
val argNode = createParserNodeInfo(x)
argNode.node match
case MapType => Seq(Ast(literalNode(argNode, argNode.code, Defines.map)))
case ChanType => Seq(Ast(literalNode(argNode, argNode.code, Defines.chan)))
case _ => astForNode(argNode)
})
.toSeq
}
private def callMethodFullNameTypeFullNameAndSignature(
methodName: String,
aliasName: Option[ParserNodeInfo] = None
): (String, String, String, String, Seq[Ast]) = {
// NOTE: There is an assumption that the import nodes have been processed before this method is being called
// and mapping of alias to their respective namespace is already done.
aliasName match
case None =>
// NOTE: If the given function is not found in builtinFunctions.
// Then we are assuming that the given function is defined inside same package as that of current file's package.
// This assumption will be invalid when another package is imported with alias "."
val methodFullName = s"$fullyQualifiedPackage.$methodName"
val methodInfo = goGlobal
.getMethodMetadata(fullyQualifiedPackage, methodName)
.getOrElse(MethodCacheMetaData(Defines.anyTypeName, s"$methodFullName()"))
val (signature, fullName, returnTypeFullName) =
Defines.builtinFunctions.getOrElse(methodName, (methodInfo.signature, methodFullName, methodInfo.returnType))
val probableLambdaTypeFullName = scope.lookupVariable(methodName) match
case Some((_, lambdaTypeFullName)) => Some(lambdaTypeFullName)
case _ =>
goGlobal.getStructTypeMemberType(fullyQualifiedPackage, methodName)
val (postLambdaFullname, postLambdaSignature, postLambdaReturnTypeFullName) = probableLambdaTypeFullName match
case Some(lambdaTypeFullName) =>
val (nameSpaceName, lambdaName) = goGlobal.splitNamespaceFromMember(lambdaTypeFullName)
goGlobal.getMethodMetadata(nameSpaceName, lambdaName) match {
case Some(metaData) => (lambdaTypeFullName, metaData.signature, metaData.returnType)
case _ => (fullName, signature, returnTypeFullName)
}
case _ =>
(fullName, signature, returnTypeFullName)
(methodName, postLambdaSignature, postLambdaFullname, postLambdaReturnTypeFullName, Seq.empty)
case Some(xnode) =>
xnode.node match
case Ident =>
Try(xnode.json(ParserKeys.Obj)) match
case Success(_) =>
// The presence of "Obj" field indicates its variable identifier and not an alias
processReceiverAst(methodName, xnode)
case _ =>
// Otherwise its an alias to imported namespace on which method call is made
val alias = xnode.json(ParserKeys.Name).str
val fullNamespace = resolveAliasToFullName(alias)
val callMethodFullName = s"$fullNamespace.$methodName"
val lambdaFullName =
goGlobal.getStructTypeMemberType(fullNamespace, methodName).getOrElse(callMethodFullName)
val (nameSpace, memberName) = goGlobal.splitNamespaceFromMember(lambdaFullName)
val MethodCacheMetaData(returnTypeFullNameCache, signatureCache) =
goGlobal
.getMethodMetadata(nameSpace, memberName)
.getOrElse(
MethodCacheMetaData(
s"$callMethodFullName.${Defines.ReturnType}.${XDefines.Unknown}",
s"$callMethodFullName()"
)
)
(methodName, signatureCache, lambdaFullName, returnTypeFullNameCache, Seq.empty)
case _ =>
// This will take care of chained method calls. It will call `astForCallExpression` in recursive way,
// and the call node is used as receiver to this current call node.
processReceiverAst(methodName, xnode)
}
private def processReceiverAst(
methodName: String,
xnode: ParserNodeInfo
): (String, String, String, String, Seq[Ast]) = {
val receiverAst = astForNode(xnode)
val receiverTypeFullName =
receiverAst.headOption
.flatMap(_.root)
.map(_.properties.get(PropertyNames.TYPE_FULL_NAME).get.toString)
.getOrElse(Defines.anyTypeName)
.stripPrefix("*")
val callMethodFullName = s"$receiverTypeFullName.$methodName"
val MethodCacheMetaData(returnTypeFullNameCache, signatureCache) = goGlobal
.getMethodMetadata(receiverTypeFullName, methodName)
.getOrElse(
MethodCacheMetaData(
s"$receiverTypeFullName.$methodName.${Defines.ReturnType}.${XDefines.Unknown}",
s"$callMethodFullName()"
)
)
(methodName, signatureCache, callMethodFullName, returnTypeFullNameCache, receiverAst)
}
}