From a9ab450ad7db5f53a5c669e71c1824c187cf85f7 Mon Sep 17 00:00:00 2001 From: Johannes Coetzee Date: Fri, 25 Jul 2025 14:26:23 +0200 Subject: [PATCH 1/3] Populate static receiver field for javasrc2cpg --- build.sbt | 2 +- .../AstForCallExpressionsCreator.scala | 2 + .../javasrc2cpg/querying/CallTests.scala | 51 ++++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index ac2565db82b5..ceb11723abaa 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := "joern" ThisBuild / organization := "io.joern" ThisBuild / scalaVersion := "3.6.4" -val cpgVersion = "1.7.43" +val cpgVersion = "1.7.45" lazy val joerncli = Projects.joerncli lazy val querydb = Projects.querydb diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala index de638990c248..ce015dba5587 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala @@ -82,6 +82,7 @@ trait AstForCallExpressionsCreator { this: AstCreator => } val receiverType = scopeAsts.rootType.filter(_ != TypeConstants.Any).orElse(receiverTypeOption) + val staticReceiver = Option.when(dispatchType == DispatchTypes.STATIC_DISPATCH)(receiverType).flatten val argumentsCode = getArgumentCodeString(call.getArguments) val codePrefix = scopeAsts.headOption @@ -103,6 +104,7 @@ trait AstForCallExpressionsCreator { this: AstCreator => .lineNumber(line(call)) .columnNumber(column(call)) .typeFullName(expressionTypeFullName.getOrElse(defaultTypeFallback())) + .staticReceiver(staticReceiver) callAst(callRoot, argumentAsts, scopeAsts.headOption) } diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala index 8e9727a4d251..f6376a7b704a 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala @@ -62,7 +62,10 @@ class NewCallTests extends JavaSrcCode2CpgFixture { | |""".stripMargin) - cpg.call.name("valueOf").methodFullName.l shouldBe List("java.lang.String.valueOf:java.lang.String(boolean)") + inside(cpg.call.name("valueOf").l) { case List(valueOfCall) => + valueOfCall.methodFullName shouldBe "java.lang.String.valueOf:java.lang.String(boolean)" + valueOfCall.staticReceiver shouldBe Some("java.lang.String") + } } "they are instance methods imported from java.lang.* should be resolved" in { @@ -131,7 +134,51 @@ class NewCallTests extends JavaSrcCode2CpgFixture { |} |""".stripMargin) - cpg.call.name("foo").methodFullName.l shouldBe List("foo.Foo.foo:java.lang.String()") + inside(cpg.call.name("foo").l) { case List(fooCall) => + fooCall.methodFullName shouldBe "foo.Foo.foo:java.lang.String()" + fooCall.staticReceiver shouldBe Some("foo.Foo") + } + } + + "calls to inherited static methods" should { + val cpg = code( + """ + |package foo; + | + |class Foo { + | public static String foo() { + | return "FOO"; + | } + |} + |""".stripMargin) + .moreCode( + """ + |package bar; + | + |import foo.Foo; + | + |class Bar extends Foo { } + |""".stripMargin) + .moreCode( + """ + |package baz; + | + |import bar.Bar; + | + |class Baz { + | void test() { + | Bar.foo(); + | } + |} + |""".stripMargin + ) + + "have the correct staticReceiver set" in { + inside(cpg.call.name("foo").l) { case List(fooCall) => + fooCall.methodFullName shouldBe "bar.Bar.foo:java.lang.String()" + fooCall.staticReceiver shouldBe Some("bar.Bar") + } + } } "calls with unresolved receivers should have the correct fullnames" in { From ceec5cc4683acdaa68259c8b20ac2ceec3e0d483 Mon Sep 17 00:00:00 2001 From: Johannes Coetzee Date: Mon, 28 Jul 2025 15:14:46 +0200 Subject: [PATCH 2/3] Fix formatting --- .../expressions/AstForCallExpressionsCreator.scala | 2 +- .../io/joern/javasrc2cpg/querying/CallTests.scala | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala index ce015dba5587..483614c92a85 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala @@ -81,7 +81,7 @@ trait AstForCallExpressionsCreator { this: AstCreator => .toList } - val receiverType = scopeAsts.rootType.filter(_ != TypeConstants.Any).orElse(receiverTypeOption) + val receiverType = scopeAsts.rootType.filter(_ != TypeConstants.Any).orElse(receiverTypeOption) val staticReceiver = Option.when(dispatchType == DispatchTypes.STATIC_DISPATCH)(receiverType).flatten val argumentsCode = getArgumentCodeString(call.getArguments) diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala index f6376a7b704a..4dbed73be32e 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala @@ -141,8 +141,7 @@ class NewCallTests extends JavaSrcCode2CpgFixture { } "calls to inherited static methods" should { - val cpg = code( - """ + val cpg = code(""" |package foo; | |class Foo { @@ -151,16 +150,14 @@ class NewCallTests extends JavaSrcCode2CpgFixture { | } |} |""".stripMargin) - .moreCode( - """ + .moreCode(""" |package bar; | |import foo.Foo; | |class Bar extends Foo { } |""".stripMargin) - .moreCode( - """ + .moreCode(""" |package baz; | |import bar.Bar; @@ -170,8 +167,7 @@ class NewCallTests extends JavaSrcCode2CpgFixture { | Bar.foo(); | } |} - |""".stripMargin - ) + |""".stripMargin) "have the correct staticReceiver set" in { inside(cpg.call.name("foo").l) { case List(fooCall) => From 3a467126712f57231430b7e19bd501c87b0061bb Mon Sep 17 00:00:00 2001 From: Johannes Coetzee Date: Tue, 29 Jul 2025 14:40:46 +0200 Subject: [PATCH 3/3] Add tests for static calls on an instance --- .../javasrc2cpg/querying/CallTests.scala | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala index 4dbed73be32e..5b0d1e902de8 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala @@ -177,6 +177,91 @@ class NewCallTests extends JavaSrcCode2CpgFixture { } } + "calls to inherited static methods via an instance" should { + val cpg = code(""" + |package foo; + | + |class Foo { + | public static String foo() { + | return "FOO"; + | } + |} + |""".stripMargin) + .moreCode(""" + |package bar; + | + |import foo.Foo; + | + |class Bar extends Foo { + | public static String foo() { + | return "BAR"; + | } + |} + |""".stripMargin) + .moreCode(""" + |package baz; + | + |import bar.Bar; + | + |class Baz { + | void test(Bar b) { + | b.foo(); + | } + |} + |""".stripMargin) + + "have the correct staticReceiver set" in { + inside(cpg.call.name("foo").l) { case List(fooCall) => + fooCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH + fooCall.methodFullName shouldBe "bar.Bar.foo:java.lang.String()" + fooCall.staticReceiver shouldBe Some("bar.Bar") + } + } + } + + "calls to overridden static methods via an instance" should { + val cpg = code(""" + |package foo; + | + |class Foo { + | public static String foo() { + | return "FOO"; + | } + |} + |""".stripMargin) + .moreCode(""" + |package bar; + | + |import foo.Foo; + | + |class Bar extends Foo { + | public static String foo() { + | return "BAR"; + | } + |} + |""".stripMargin) + .moreCode(""" + |package baz; + | + |import bar.Bar; + |import foo.Foo; + | + |class Baz { + | void test(Foo f) { + | f.foo(); + | } + |} + |""".stripMargin) + + "have the correct staticReceiver set" in { + inside(cpg.call.name("foo").l) { case List(fooCall) => + fooCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH + fooCall.methodFullName shouldBe "foo.Foo.foo:java.lang.String()" + fooCall.staticReceiver shouldBe Some("foo.Foo") + } + } + } + "calls with unresolved receivers should have the correct fullnames" in { val cpg = code(""" |import a.*;