Skip to content

Commit 625cdcd

Browse files
authored
cpggenerator: only return Some(frontend) if it's actually available (#5834)
* cpggenerator: only return `Some(frontend)` if it's actually available Before this, we'd return the frontend and happily invoke it. Only to then fail with a slightly different exception and a long and confusing stacktrace: ``` Caused by: java.lang.AssertionError: assertion failed: CPG generator does not exist at: path/to/js2cpg.sh at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8) at io.joern.console.cpgcreation.CpgGenerator.runShellCommand$$anonfun$1(CpgGenerator.scala:30) at io.joern.console.cpgcreation.CpgGenerator.runShellCommand$$anonfun$adapted$1(CpgGenerator.scala:47) at scala.util.Try$.apply(Try.scala:217) at io.joern.console.cpgcreation.CpgGenerator.runShellCommand(CpgGenerator.scala:47) at io.joern.console.cpgcreation.JsCpgGenerator.generate(JsCpgGenerator.scala:15) at io.joern.console.cpgcreation.CpgGeneratorFactory.runGenerator(CpgGeneratorFactory.scala:59) at io.joern.console.cpgcreation.ImportCode.$anonfun$5(ImportCode.scala:212) at scala.Option.flatMap(Option.scala:283) at io.joern.console.cpgcreation.ImportCode.io$joern$console$cpgcreation$ImportCode$$apply(ImportCode.scala:210) at io.joern.console.cpgcreation.ImportCode.apply(ImportCode.scala:45) ``` This changes the above to a more meaningful and helpful: ``` io.joern.console.ConsoleException: No suitable CPG generator found for: /home/mp/testdata/shiftleft-js-demo/src/Controllers/Login.js To get an overview of all available language modules, try `importCode`. To choose a specific language, try `importCode.<language>`." ``` * fmt * do this differently - some tests depend on how things were wired up previously
1 parent 97465ea commit 625cdcd

File tree

6 files changed

+43
-18
lines changed

6 files changed

+43
-18
lines changed

console/src/main/scala/io/joern/console/cpgcreation/CpgGeneratorFactory.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,25 @@ object CpgGeneratorFactory {
3131

3232
class CpgGeneratorFactory(config: ConsoleConfig) {
3333

34-
/** For a given input path, try to guess a suitable generator and return it
35-
*/
34+
/** For a given input path, try to guess a suitable generator and return it if it is available */
3635
def forCodeAt(inputPath: String): Option[CpgGenerator] =
3736
for {
3837
language <- guessLanguage(inputPath)
3938
cpgGenerator <- cpgGeneratorForLanguage(language, config.frontend, config.install.rootPath, args = Nil)
39+
if cpgGenerator.isAvailable
4040
} yield {
4141
report(s"Using generator for language: $language: ${cpgGenerator.getClass.getSimpleName}")
4242
cpgGenerator
4343
}
4444

45-
/** For a language, return the generator
46-
*/
45+
/** For a language, return the generator if it is available */
4746
def forLanguage(language: String): Option[CpgGenerator] = {
48-
Option(language)
49-
.filter(languageIsKnown)
50-
.flatMap { lang =>
51-
cpgGeneratorForLanguage(lang, config.frontend, config.install.rootPath, args = Nil)
52-
}
47+
for {
48+
lang <- Option(language)
49+
if languageIsKnown(lang)
50+
generator <- cpgGeneratorForLanguage(lang, config.frontend, config.install.rootPath, args = Nil)
51+
if generator.isAvailable
52+
} yield generator
5353
}
5454

5555
def languageIsKnown(language: String): Boolean = CpgGeneratorFactory.KNOWN_LANGUAGES.contains(language)

console/src/main/scala/io/joern/console/cpgcreation/ImportCode.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit
3434
*/
3535
def apply(inputPath: String, projectName: String = "", language: String = ""): Cpg = {
3636
checkInputPath(inputPath)
37+
val errHint =
38+
"To get an overview of all available language modules, try `importCode`.\nTo choose a specific language, try `importCode.<language>`."
3739
if (language != "") {
3840
generatorFactory.forLanguage(language) match {
39-
case None => throw new ConsoleException(s"No CPG generator exists for language: $language")
41+
case None => throw new ConsoleException(s"No CPG generator exists for language: $language\n$errHint")
4042
case Some(frontend) => apply(frontend, inputPath, projectName)
4143
}
4244
} else {
4345
generatorFactory.forCodeAt(inputPath) match {
44-
case None => throw new ConsoleException(s"No suitable CPG generator found for: $inputPath")
46+
case None => throw new ConsoleException(s"No suitable CPG generator found for: $inputPath\n$errHint")
4547
case Some(frontend) => apply(frontend, inputPath, projectName)
4648
}
4749
}
@@ -100,8 +102,9 @@ class ImportCode[T <: Project](console: io.joern.console.Console[T])(implicit
100102
config: FrontendConfig,
101103
rootPath: Path,
102104
args: List[String]
103-
): Option[CpgGenerator] =
105+
): Option[CpgGenerator] = {
104106
io.joern.console.cpgcreation.cpgGeneratorForLanguage(language, config, rootPath, args)
107+
}
105108

106109
def isAvailable: Boolean =
107110
cpgGeneratorForLanguage(language, config.frontend, config.install.rootPath, args = Nil).exists(_.isAvailable)

console/src/main/scala/io/joern/console/cpgcreation/LlvmCpgGenerator.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ case class LlvmCpgGenerator(config: FrontendConfig, rootPath: Path) extends CpgG
1717
runShellCommand(command, arguments).map(_ => outputPath)
1818
}
1919

20-
override def isAvailable: Boolean = rootPath.resolve("llvm2cpg.sh").toFile.exists()
20+
override def isAvailable: Boolean = {
21+
rootPath.resolve("llvm2cpg.sh").toFile.exists()
22+
}
2123

2224
override def isJvmBased = false
2325
}

console/src/main/scala/io/joern/console/cpgcreation/package.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import scala.util.Try
1010

1111
package object cpgcreation {
1212

13-
/** For a given language, return CPG generator script
13+
/** For a given language, return CPG generator script Note, this doesn't check if the generator is available, that is
14+
* done in the ImportCode class.
1415
*/
1516
def cpgGeneratorForLanguage(
1617
language: String,

console/src/test/scala/io/joern/console/ConsoleTests.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ class ConsoleTests extends AnyWordSpec with Matchers {
5353
intercept[ConsoleException] {
5454
console.importCode.swiftsrc(nonExistentDir)
5555
}.getMessage shouldBe s"Input path does not exist: '$nonExistentDir'"
56-
intercept[ConsoleException] {
57-
console.importCode.java(nonExistentDir)
58-
}.getMessage shouldBe s"Input path does not exist: '$nonExistentDir'"
5956
}
6057

6158
"provide overview of available language modules" in ConsoleFixture() { (console, _) =>

console/src/test/scala/io/joern/console/testing/ConsoleFixture.scala

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.joern.console.testing
33
import io.joern.console.cpgcreation.{CCpgGenerator, CpgGenerator, CpgGeneratorFactory, ImportCode}
44
import io.joern.console.workspacehandling.{Project, ProjectFile, WorkspaceLoader}
55
import io.joern.console.{Console, ConsoleConfig, FrontendConfig, InstallConfig}
6-
import io.joern.console.cpgcreation.{JsSrcCpgGenerator, SwiftSrcCpgGenerator}
6+
import io.joern.console.cpgcreation.{JavaSrcCpgGenerator, JsSrcCpgGenerator, SwiftSrcCpgGenerator}
77
import io.joern.console.cpgcreation.guessLanguage
88
import io.shiftleft.semanticcpg.utils.FileUtil.*
99
import io.shiftleft.codepropertygraph.generated.Languages
@@ -12,6 +12,7 @@ import io.shiftleft.utils.ProjectRoot
1212

1313
import java.nio.file.{Files, Path, Paths}
1414
import scala.util.Try
15+
import io.joern.console.cpgcreation.JavaSrcCpgGenerator
1516

1617
object ConsoleFixture {
1718
def apply[T <: Console[Project]](constructor: String => T = { x =>
@@ -77,6 +78,19 @@ class TestConsole(workspaceDir: String) extends Console[Project](TestWorkspaceLo
7778
}
7879
}
7980

81+
override def java: SourceBasedFrontend =
82+
new SourceBasedFrontend("testJavaSrcFrontend", Languages.JAVASRC, "", "java") {
83+
override def cpgGeneratorForLanguage(
84+
language: String,
85+
config: FrontendConfig,
86+
rootPath: Path,
87+
args: List[String]
88+
): Option[CpgGenerator] = {
89+
val newConfig = new ConsoleConfig(TestConsole.this.config.install, config.withArgs(args))
90+
new TestCpgGeneratorFactory(newConfig).forLanguage(language)
91+
}
92+
}
93+
8094
override def swiftsrc: SourceBasedFrontend =
8195
new SwiftSrcFrontend("testSwiftSrcFrontend", Languages.SWIFTSRC, "", "swift") {
8296
override def cpgGeneratorForLanguage(
@@ -107,6 +121,13 @@ class TestCpgGeneratorFactory(config: ConsoleConfig) extends CpgGeneratorFactory
107121
)
108122
}
109123

124+
private def newJavaSrcCpgGenerator(): JavaSrcCpgGenerator = {
125+
JavaSrcCpgGenerator(
126+
config.frontend,
127+
Path.of(ProjectRoot.relativise("joern-cli/frontends/javasrc2cpg/target/universal/stage/bin"))
128+
)
129+
}
130+
110131
private def newSwiftSrcCpgGenerator(): SwiftSrcCpgGenerator = {
111132
SwiftSrcCpgGenerator(
112133
config.frontend,
@@ -118,6 +139,7 @@ class TestCpgGeneratorFactory(config: ConsoleConfig) extends CpgGeneratorFactory
118139
guessLanguage(inputPath) match
119140
case Some(Languages.NEWC) => Option(newCCpgGenerator())
120141
case Some(Languages.JSSRC) => Option(newJsSrcCpgGenerator())
142+
case Some(Languages.JAVASRC) => Option(newJavaSrcCpgGenerator())
121143
case Some(Languages.SWIFTSRC) => Option(newSwiftSrcCpgGenerator())
122144
case _ => None // no other languages are tested here
123145
}

0 commit comments

Comments
 (0)