Joern is based on Scala. As of Joern 2.0.x we upgraded from Scala 2 to Scala 3. This is a major version upgrade and Scala 3 is essentially a new language with a new REPL implementation, so this may sound scary.
That being said, both the Scala as well as Joern maintainers have made an effort to minimize changes to the API, in order to ease the transition for users. Most importantly, the Joern workspace DSL (importCode(...)
etc.) and the CPG Traversal DSL (e.g. cpg.method.name("foo").l
) are unchanged. The latter is based on Scala collections API, which is actually identical (a shared library) between Scala 2 and Scala 3.
Depending on your use case you may not even notice a difference: we've tried to keep as much as possible just like it was - most importantly the query DSL. There are however a few changes - some that we believe are the better, and some were just unavoidable. Here's the most important ones as far as I can tell:
-
The 'import a script' magic
$file.foo
from Ammonite was replaced by the//> using file foo.sc
directive. This works in an active joern REPL as well as in scripts. -
Adding dependencies: the magic
$ivy.my-dependency
was replaced:
--dep
parameter, e.g../joern --dep com.michaelpollmeier:versionsort:1.0.7
. Can be specified multiple times.- For scripts there's a slightly nicer alternative that let's you specify your dependencies within the script itself:
//> using dep com.michaelpollmeier:versionsort:1.0.7
- all dependencies need to be known when joern starts, i.e. you can not dynamically add more dependencies to an active joern REPL session
- Script parameters: pass multiple
--param
parameters rather than one comma-separated list. Example:
// old
./joern --script foo.sc --params paramA=valueA,paramB=valueB
// new
./joern --script foo.sc --param paramA=valueA --param paramB=valueB
While that's slightly longer it is also less complex, easier to read, and you can pass values that contain commas :)
Apart from that, Scala 3 is a bit stricter when it comes to adding or leaving out ()
for function application. The compiler messages are (on average) much better than before, so hopefully it'll guide you as good as possible.
Depending on your level of integration with Joern you might not even notice anything. If you do, please check the lists above and below, and if that doesn't help: open a github issue or hit us up on discord.
Some more generic Scala-issues when upgrading Scala 2 to Scala 3:
- anonymous functions need an extra parenthesis around their parameter list:
Seq(1,2,3).map { i: Int => i + 1 }
// error: parentheses are required around the parameter of a lambda
// option 1: add parentheses, as suggested by compiler:
Seq(1,2,3).map { (i: Int) => i + 1 }
// option 2: drop type annotation (if possible):
Seq(1,2,3).map { i => i + 1 }
main
entrypoint:def main
instead ofextends App
See https://docs.scala-lang.org/scala3/book/methods-main-methods.html
object Main extends App {
println("hello world")
}
// depending on usage, may lead to NullPointerExceptions
// context: Scala3 doesn't support the 'magic' DelayedInit trait
// rewrite to:
object Main {
def main(args: Array[String]) = {
println("hello world")
}
}