diff --git a/build.sbt b/build.sbt index f5329a001fdf..1055c0f8513c 100644 --- a/build.sbt +++ b/build.sbt @@ -2,6 +2,7 @@ name := "joern" ThisBuild / organization := "io.joern" ThisBuild / scalaVersion := "3.6.4" + val cpgVersion = "1.7.46" lazy val joerncli = Projects.joerncli 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..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,8 @@ 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) 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..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 @@ -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,132 @@ 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 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 {