Skip to content

Commit

Permalink
Fix a problem with Ruby's call graph (#2796)
Browse files Browse the repository at this point in the history
Co-authored-by: Fabian Yamaguchi <fabs@joern.io>
  • Loading branch information
fabsx00 and Fabian Yamaguchi committed Jun 2, 2023
1 parent e063274 commit 4a098ff
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ trait AstNodeBuilder { this: AstCreator =>
def callNode(node: BabelNodeInfo, code: String, name: String, dispatchType: String): NewCall = {
val fullName =
if (dispatchType == DispatchTypes.STATIC_DISPATCH) name
else x2cpg.Defines.DynamicCallUnknownFallName
else x2cpg.Defines.DynamicCallUnknownFullName
callNode(node, code, name, fullName, dispatchType, None, Some(Defines.Any))
}

Expand All @@ -146,7 +146,7 @@ trait AstNodeBuilder { this: AstCreator =>
.code(code)
.name(callName)
.methodFullName(
if (dispatchType == DispatchTypes.STATIC_DISPATCH) callName else x2cpg.Defines.DynamicCallUnknownFallName
if (dispatchType == DispatchTypes.STATIC_DISPATCH) callName else x2cpg.Defines.DynamicCallUnknownFullName
)
.dispatchType(dispatchType)
.lineNumber(line)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class NodeBuilder(diffGraph: DiffGraphBuilder) {
.NewCall()
.code(code)
.name(name)
.methodFullName(if (dispatchType == DispatchTypes.STATIC_DISPATCH) name else Defines.DynamicCallUnknownFallName)
.methodFullName(if (dispatchType == DispatchTypes.STATIC_DISPATCH) name else Defines.DynamicCallUnknownFullName)
.dispatchType(dispatchType)
.typeFullName(Constants.ANY)
.lineNumber(lineAndColumn.line)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1194,9 +1194,10 @@ class AstCreator(filename: String, global: Global)
def astForCallNode(localIdentifier: TerminalNode): Seq[Ast] = {
val column = localIdentifier.getSymbol().getCharPositionInLine()
val line = localIdentifier.getSymbol().getLine()
val name = getActualMethodName(localIdentifier.getText)
val callNode = NewCall()
.name(getActualMethodName(localIdentifier.getText))
.methodFullName(MethodFullNames.UnknownFullName)
.name(name)
.methodFullName(name)
.signature(localIdentifier.getText())
.typeFullName(MethodFullNames.UnknownFullName)
.dispatchType(DispatchTypes.STATIC_DISPATCH)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.joern.rubysrc2cpg.querying

import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.shiftleft.semanticcpg.language._

class CallGraphTests extends RubyCode2CpgFixture {

val cpg = code("""
|def bar(content)
|puts content
|end
|
|def foo
|bar( 1 )
|end
|""".stripMargin)

"should identify call from `foo` to `bar`" in {
val List(callToBar) = cpg.call("bar").l
callToBar.name shouldBe "bar"
callToBar.lineNumber shouldBe Some(7)
cpg.method("bar").caller.name.l shouldBe List("foo")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ object Defines {

// In some languages like Javascript dynamic calls do not provide any statically known
// method/function interface information. In those cases please use this value.
val DynamicCallUnknownFallName = "<unknownFullName>"
val DynamicCallUnknownFullName = "<unknownFullName>"
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class MethodStubCreator(cpg: Cpg) extends CpgPass(cpg) {
methodFullNameToNode.put(method.fullName, method)
}

for (call <- cpg.call if call.methodFullName != Defines.DynamicCallUnknownFallName) {
for (call <- cpg.call if call.methodFullName != Defines.DynamicCallUnknownFullName) {
methodToParameterCount.put(
CallSummary(call.name, call.signature, call.methodFullName, call.dispatchType),
call.argument.size
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.joern.x2cpg.passes.callgraph

import io.joern.x2cpg.Defines.DynamicCallUnknownFallName
import io.joern.x2cpg.Defines.DynamicCallUnknownFullName
import io.shiftleft.codepropertygraph.Cpg
import io.shiftleft.codepropertygraph.generated.nodes.{Call, Method, TypeDecl}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, EdgeTypes, PropertyNames}
Expand Down Expand Up @@ -168,7 +168,7 @@ class DynamicCallLinker(cpg: Cpg) extends CpgPass(cpg) {

private def linkDynamicCall(call: Call, dstGraph: DiffGraphBuilder): Unit = {
// This call linker requires a method full name entry
if (call.methodFullName.equals("<empty>") || call.methodFullName.equals(DynamicCallUnknownFallName)) return
if (call.methodFullName.equals("<empty>") || call.methodFullName.equals(DynamicCallUnknownFullName)) return
// Support for overriding
resolveCallInSuperClasses(call)

Expand Down

0 comments on commit 4a098ff

Please sign in to comment.