Skip to content

Commit

Permalink
[javasrc2cpg] Populate generic signatures roughly following the class…
Browse files Browse the repository at this point in the history
… file signature format (#5274)

* First version of a JVM binary signature calculator for java parser classes.

* Add binary signatures to javasrc nodes

* Add generic signature for native foreach iterator tmp local

* Do some cleanup

* Add test for type parameter with multiple interface bounds

* Add generic tests description and add unspecified type to lambda type decls

---------

Co-authored-by: Markus Lottmann <[email protected]>
  • Loading branch information
johannescoetzee and ml86 authored Jan 31, 2025
1 parent 4f59572 commit b02caa7
Show file tree
Hide file tree
Showing 16 changed files with 1,739 additions and 103 deletions.
3 changes: 2 additions & 1 deletion joern-cli/frontends/javasrc2cpg/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ libraryDependencies ++= Seq(
"org.projectlombok" % "lombok" % Versions.lombok,
"org.scala-lang.modules" %% "scala-parallel-collections" % Versions.scalaParallel,
"org.scala-lang.modules" %% "scala-parser-combinators" % Versions.scalaParserCombinators,
"net.lingala.zip4j" % "zip4j" % Versions.zip4j
"net.lingala.zip4j" % "zip4j" % Versions.zip4j,
"org.ow2.asm" % "asm" % Versions.asm,
)

enablePlugins(JavaAppPackaging, LauncherJarPlugin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.github.javaparser.resolution.declarations.{
import com.github.javaparser.resolution.types.ResolvedType
import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametersMap
import com.github.javaparser.symbolsolver.JavaSymbolSolver
import io.joern.javasrc2cpg.astcreation.declarations.AstForDeclarationsCreator
import io.joern.javasrc2cpg.astcreation.declarations.{AstForDeclarationsCreator, BinarySignatureCalculator}
import io.joern.javasrc2cpg.astcreation.expressions.AstForExpressionsCreator
import io.joern.javasrc2cpg.astcreation.statements.AstForStatementsCreator
import io.joern.javasrc2cpg.scope.Scope
Expand Down Expand Up @@ -107,6 +107,7 @@ class AstCreator(
private[astcreation] val typeInfoCalc: TypeInfoCalculator =
TypeInfoCalculator(global, symbolSolver, keepTypeArguments)
private[astcreation] val bindingTableCache = mutable.HashMap.empty[String, BindingTable]
private[astcreation] val binarySignatureCalculator: BinarySignatureCalculator = new BinarySignatureCalculator(scope)

private[astcreation] val tempNameProvider: TemporaryNameProvider = new TemporaryNameProvider

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
ExpectedType(returnTypeFullName, expectedReturnType),
methodDeclaration.isStatic()
)
typeParameters.foreach { typeParameter => scope.addTopLevelType(typeParameter.name, typeParameter.typeFullName) }
typeParameters.foreach { typeParameter => scope.addTypeParameter(typeParameter.name, typeParameter.typeFullName) }

val genericSignature = binarySignatureCalculator.methodBinarySignature(methodDeclaration)
methodNode.genericSignature(genericSignature)

val parameterAsts = astsForParameterList(methodDeclaration.getParameters.asScala.toList)
val parameterTypes = argumentTypesForMethodLike(maybeResolved)
Expand All @@ -101,7 +104,7 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
val thisAst = thisNode.map(Ast(_)).toList

thisNode.foreach { node =>
scope.enclosingMethod.get.addParameter(node)
scope.enclosingMethod.get.addParameter(node, scope.enclosingTypeDecl.get.typeDecl.genericSignature)
}

val bodyAst = methodDeclaration.getBody.toScala
Expand Down Expand Up @@ -145,6 +148,7 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
val methodReturn =
newMethodReturnNode(parameterTypeFullName, line = line(parameter), column = column(parameter))

val genericSignature = binarySignatureCalculator.recordParameterAccessorBinarySignature(parameter)
val methodRoot = methodNode(
parameter,
parameterName,
Expand All @@ -153,7 +157,8 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
Option(signature),
filename,
Option(NodeTypes.TYPE_DECL),
Option(recordTypeFullName)
Option(recordTypeFullName),
genericSignature = Option(genericSignature)
)

val modifier = newModifierNode(ModifierTypes.PUBLIC)
Expand Down Expand Up @@ -230,7 +235,7 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
List(accessModifier, abstractModifier, staticVirtualModifier).flatten
}

private def getIdentifiersForTypeParameters(methodDeclaration: MethodDeclaration): List[NewIdentifier] = {
private def getIdentifiersForTypeParameters(methodDeclaration: CallableDeclaration[?]): List[NewIdentifier] = {
methodDeclaration.getTypeParameters.asScala.map { typeParameter =>
val name = typeParameter.getNameAsString
val typeFullName = tryWithSafeStackOverflow(typeParameter.getTypeBound.asScala.headOption).toOption.flatten
Expand Down Expand Up @@ -264,13 +269,17 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
}

def astForDefaultConstructor(originNode: Node, instanceFieldDeclarations: List[FieldDeclaration]): Ast = {
val parameters = scope.enclosingTypeDecl.get.recordParameters
val genericSignature = binarySignatureCalculator.defaultConstructorSignature(parameters)
val constructorNode = NewMethod()
.name(io.joern.x2cpg.Defines.ConstructorMethodName)
.filename(filename)
.isExternal(false)
.genericSignature(genericSignature)
.lineNumber(line(originNode))
.columnNumber(column(originNode))
scope.pushMethodScope(constructorNode, ExpectedType.Void, isStatic = false)

val parameters = scope.enclosingTypeDecl.get.recordParameters
val parameterAsts = parameters.zipWithIndex.map { case (param, idx) =>
astForParameter(param, idx + 1)
}
Expand All @@ -289,7 +298,7 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
constructorNode.signature(signature)

val thisNode = thisNodeForMethod(typeFullName, lineNumber = None, columnNumber = None)
scope.enclosingMethod.foreach(_.addParameter(thisNode))
scope.enclosingMethod.foreach(_.addParameter(thisNode, scope.enclosingTypeDecl.get.typeDecl.genericSignature))
val recordParameterAssignments = parameterAsts
.flatMap(_.nodes)
.collect { case param: nodes.NewMethodParameterIn => param }
Expand Down Expand Up @@ -392,7 +401,8 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
val annotationAsts = parameter.getAnnotations.asScala.map(astForAnnotationExpr)
val ast = Ast(parameterNode)

scope.enclosingMethod.get.addParameter(parameterNode)
scope.enclosingMethod.get
.addParameter(parameterNode, binarySignatureCalculator.variableBinarySignature(parameter.getType))

ast.withChildren(annotationAsts)
}
Expand Down Expand Up @@ -463,15 +473,21 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
instanceFieldDeclarations: List[FieldDeclaration]
): List[PartialConstructorDeclaration] = {
constructorDeclarations.map { constructorDeclaration =>
val constructorNode = createPartialMethod(constructorDeclaration)
.name(io.joern.x2cpg.Defines.ConstructorMethodName)

scope.pushMethodScope(constructorNode, ExpectedType.Void, isStatic = false)
val maybeResolved = Option
.when(constructorDeclaration.isConstructorDeclaration)(
tryWithSafeStackOverflow(constructorDeclaration.resolve()).toOption
)
.flatten
val constructorNode = createPartialMethod(constructorDeclaration)
.name(io.joern.x2cpg.Defines.ConstructorMethodName)

scope.pushMethodScope(constructorNode, ExpectedType.Void, isStatic = false)
constructorDeclaration match {
case regularConstructor: ConstructorDeclaration =>
val typeParameters = getIdentifiersForTypeParameters(regularConstructor)
typeParameters.foreach(typeParam => scope.addTypeParameter(typeParam.name, typeParam.typeFullName))
case _ => // Compact constructor cannot have type parameters
}

val parameters = constructorDeclaration match {
case regularConstructor: ConstructorDeclaration => regularConstructor.getParameters.asScala.toList
Expand All @@ -497,15 +513,17 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
.fullName(fullName)
.signature(signature)

parameterAsts.foreach { ast =>
parameterAsts.zip(parameters).foreach { (ast, parameterNode) =>
ast.root match {
case Some(parameter: NewMethodParameterIn) => scope.enclosingMethod.get.addParameter(parameter)
case _ => // This should never happen
case Some(parameter: NewMethodParameterIn) =>
val genericType = binarySignatureCalculator.variableBinarySignature(parameterNode.getType)
scope.enclosingMethod.get.addParameter(parameter, genericType)
case _ => // This should never happen
}
}

val thisNode = thisNodeForMethod(typeFullName, line(constructorDeclaration), column(constructorDeclaration))
scope.enclosingMethod.get.addParameter(thisNode)
scope.enclosingMethod.get.addParameter(thisNode, scope.enclosingTypeDecl.get.typeDecl.genericSignature)

scope.pushBlockScope()
val recordParameterAssignments = constructorDeclaration match {
Expand Down Expand Up @@ -677,7 +695,22 @@ private[declarations] trait AstForMethodsCreator { this: AstCreator =>
val endColumn = declaration.getEnd.map(x => Integer.valueOf(x.column)).toScala

val placeholderFullName = ""
methodNode(declaration, declaration.getNameAsString(), methodCode, placeholderFullName, None, filename)

val genericSignature = declaration match {
case callableDeclaration: CallableDeclaration[_] =>
binarySignatureCalculator.methodBinarySignature(callableDeclaration)
case compactConstructor: CompactConstructorDeclaration =>
binarySignatureCalculator.defaultConstructorSignature(scope.enclosingTypeDecl.get.recordParameters)
}
methodNode(
declaration,
declaration.getNameAsString(),
methodCode,
placeholderFullName,
None,
filename,
genericSignature = Option(genericSignature)
)
}

def thisNodeForMethod(
Expand Down
Loading

0 comments on commit b02caa7

Please sign in to comment.