diff --git a/build.gradle.kts b/build.gradle.kts
index 15947c5cf..cc93e1660 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -172,11 +172,9 @@ if (!repoUrl.isNullOrEmpty()) {
project(":jacodb-api-storage"),
project(":jacodb-core"),
project(":jacodb-storage"),
- project(":jacodb-analysis"),
project(":jacodb-approximations"),
project(":jacodb-taint-configuration"),
project(":jacodb-ets"),
- project(":jacodb-panda-static"),
)
) {
tasks {
diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt
index 732c6c7e5..1ac3c0864 100644
--- a/buildSrc/src/main/kotlin/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/Dependencies.kt
@@ -22,7 +22,7 @@ object Versions {
const val jooq = "3.14.16"
const val juliet = "1.3.2"
const val junit = "5.9.2"
- const val kotlin = "2.0.20"
+ const val kotlin = "2.1.0"
const val kotlin_logging = "1.8.3"
const val kotlinx_benchmark = "0.4.6"
const val kotlinx_cli = "0.3.5"
diff --git a/jacodb-analysis/README.md b/jacodb-analysis/README.md
deleted file mode 100644
index cb6a4b854..000000000
--- a/jacodb-analysis/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Module `jacodb-analysis`
-
-The `jacodb-analysis` module allows launching application dataflow analyses.
-It contains an API to write custom analyses, and several ready-to-use analyses.
-
-## Units
-
-The [IFDS](https://dx.doi.org/10.1145/199448.199462) framework is the basis for this module.
-To make the implementation scalable, the analyzed code is split into the so-called units, so that the framework
-can analyze them concurrently.
-Information is shared between the units via summaries, but the lifecycle of each unit is controlled
-separately.
-
-## Get started
-
-The analysis entry point is the [runAnalysis] method. To call it, you have to provide:
-* `graph` — an application graph that is used for analysis. To obtain this graph, one should call the [newApplicationGraphForAnalysis] method.
-* `unitResolver` — an object that groups methods into units. Choose one from `UnitResolversLibrary`.
-Note that, in general, larger units mean more precise but also more resource-consuming analysis.
-* `ifdsUnitRunnerFactory` — an [IfdsUnitRunnerFactory] instance, that can create runners. Runners are used to analyze each unit.
-So this factory is what define concrete analysis.
- Ready-to-use runner factories are located in `RunnersLibrary`.
-* `methods` — a list of methods to analyze.
-
-For example, to detect the unused variables in the given `analyzedClass` methods, you may run the following code
-(assuming that `classpath` is an instance of [JcClasspath]):
-
-```kotlin
-val applicationGraph = runBlocking {
- classpath.newApplicationGraphForAnalysis()
-}
-
-val methodsToAnalyze = analyzedClass.declaredMethods
-val unitResolver = MethodUnitResolver
-val runnerFactory = UnusedVariableRunnerFactory
-
-runAnalysis(applicationGraph, unitResolver, runnerFactory, methodsToAnalyze)
-```
-
-## Implemented runners
-
-By now, the following runners are implemented:
-* `UnusedVariableRunner` that can detect issues like unused variable declaration, unused `return` value, etc.
-* `NpeRunner` that can find instructions with possible `null` value dereference.
-* Generic `TaintRunner` that can perform taint analysis.
-* `SqlInjectionRunner`, which finds places vulnerable to SQL injections, thus performing a specific kind of taint
- analysis.
-
-## Implementing your own analysis
-
-To implement a simple one-pass analysis, use [IfdsBaseUnitRunnerFactory].
-To instantiate it, you need an [AnalyzerFactory] instance, which is an object that can create [Analyzer] via
-[JcApplicationGraph].
-
-To instantiate an [Analyzer] interface, you have to specify the following:
-
-* `flowFunctions`, which describe the dataflow facts and their transmissions during the analysis;
-
-* semantics for these dataflow facts, i.e. how these facts produce vulnerabilities, summaries, etc.
-This should be done by implementing `handleNewEdge`, `handleNewCrossUnitCall` and `handleIfdsResult` methods.
-
-To implement bidirectional analysis, you may use composite [BidiIfdsUnitRunnerFactory].
-
-
-
-
-[runAnalysis]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis/run-analysis.html
-[newApplicationGraphForAnalysis]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.graph/new-application-graph-for-analysis.html
-[IfdsUnitRunnerFactory]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-ifds-unit-runner-factory/index.html
-[JcClasspath]: https://jacodb.org/docs/jacodb-api/org.jacodb.api/-jc-classpath/index.html
-[IfdsBaseUnitRunnerFactory]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-ifds-base-unit-runner-factory/index.html
-[AnalyzerFactory]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-analyzer-factory/index.html
-[Analyzer]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-analyzer/index.html
-[JcApplicationGraph]: https://jacodb.org/docs/jacodb-api/org.jacodb.api.analysis/-jc-application-graph/index.html
-[BidiIfdsUnitRunnerFactory]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-bidi-ifds-unit-runner-factory/index.html
\ No newline at end of file
diff --git a/jacodb-analysis/build.gradle.kts b/jacodb-analysis/build.gradle.kts
deleted file mode 100644
index dd121df72..000000000
--- a/jacodb-analysis/build.gradle.kts
+++ /dev/null
@@ -1,32 +0,0 @@
-plugins {
- kotlin("plugin.serialization")
- `java-test-fixtures`
-}
-
-dependencies {
- api(project(":jacodb-api-jvm"))
- api(project(":jacodb-core"))
- api(project(":jacodb-taint-configuration"))
- api(project(":jacodb-ets"))
- api(project(":jacodb-panda-static"))
-
- implementation(Libs.kotlin_logging)
- implementation(Libs.slf4j_simple)
- implementation(Libs.kotlinx_coroutines_core)
- implementation(Libs.kotlinx_serialization_json)
- api(Libs.sarif4k)
-
- testImplementation(project(":jacodb-api-jvm"))
- testImplementation(testFixtures(project(":jacodb-core")))
- testImplementation(testFixtures(project(":jacodb-storage")))
- testImplementation(kotlin("test"))
- testImplementation(Libs.mockk)
-
- // Additional deps for analysis:
- testImplementation(files("src/test/resources/pointerbench.jar"))
- testImplementation(Libs.joda_time)
- testImplementation(Libs.juliet_support)
- for (cweNum in listOf(89, 476, 563, 690)) {
- testImplementation(Libs.juliet_cwe(cweNum))
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/Condition.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/Condition.kt
deleted file mode 100644
index af05a9dd1..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/Condition.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.config
-
-import org.jacodb.analysis.ifds.Maybe
-import org.jacodb.analysis.ifds.onSome
-import org.jacodb.analysis.taint.Tainted
-import org.jacodb.analysis.util.Traits
-import org.jacodb.analysis.util.removeTrailingElementAccessors
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.cfg.CommonInst
-import org.jacodb.api.common.cfg.CommonValue
-import org.jacodb.api.jvm.cfg.JcValue
-import org.jacodb.api.jvm.ext.isAssignable
-import org.jacodb.taint.configuration.And
-import org.jacodb.taint.configuration.AnnotationType
-import org.jacodb.taint.configuration.ConditionVisitor
-import org.jacodb.taint.configuration.ConstantEq
-import org.jacodb.taint.configuration.ConstantGt
-import org.jacodb.taint.configuration.ConstantLt
-import org.jacodb.taint.configuration.ConstantMatches
-import org.jacodb.taint.configuration.ConstantTrue
-import org.jacodb.taint.configuration.ContainsMark
-import org.jacodb.taint.configuration.IsConstant
-import org.jacodb.taint.configuration.IsType
-import org.jacodb.taint.configuration.Not
-import org.jacodb.taint.configuration.Or
-import org.jacodb.taint.configuration.PositionResolver
-import org.jacodb.taint.configuration.SourceFunctionMatches
-import org.jacodb.taint.configuration.TypeMatches
-
-// TODO: replace 'JcInt' with 'CommonInt', etc
-
-context(Traits)
-open class BasicConditionEvaluator(
- internal val positionResolver: PositionResolver>,
-) : ConditionVisitor {
-
- override fun visit(condition: ConstantTrue): Boolean {
- return true
- }
-
- override fun visit(condition: Not): Boolean {
- return !condition.arg.accept(this)
- }
-
- override fun visit(condition: And): Boolean {
- return condition.args.all { it.accept(this) }
- }
-
- override fun visit(condition: Or): Boolean {
- return condition.args.any { it.accept(this) }
- }
-
- override fun visit(condition: IsType): Boolean {
- // Note: TaintConfigurationFeature.ConditionSpecializer is responsible for
- // expanding IsType condition upon parsing the taint configuration.
- error("Unexpected condition: $condition")
- }
-
- override fun visit(condition: AnnotationType): Boolean {
- // Note: TaintConfigurationFeature.ConditionSpecializer is responsible for
- // expanding AnnotationType condition upon parsing the taint configuration.
- error("Unexpected condition: $condition")
- }
-
- override fun visit(condition: IsConstant): Boolean {
- positionResolver.resolve(condition.position).onSome {
- return it.isConstant()
- }
- return false
- }
-
- override fun visit(condition: ConstantEq): Boolean {
- positionResolver.resolve(condition.position).onSome { value ->
- return value.eqConstant(condition.value)
- }
- return false
- }
-
- override fun visit(condition: ConstantLt): Boolean {
- positionResolver.resolve(condition.position).onSome { value ->
- return value.ltConstant(condition.value)
- }
- return false
- }
-
- override fun visit(condition: ConstantGt): Boolean {
- positionResolver.resolve(condition.position).onSome { value ->
- return value.gtConstant(condition.value)
- }
- return false
- }
-
- override fun visit(condition: ConstantMatches): Boolean {
- positionResolver.resolve(condition.position).onSome { value ->
- return value.matches(condition.pattern)
- }
- return false
- }
-
- override fun visit(condition: SourceFunctionMatches): Boolean {
- TODO("Not implemented yet")
- }
-
- override fun visit(condition: ContainsMark): Boolean {
- error("This visitor does not support condition $condition. Use FactAwareConditionEvaluator instead")
- }
-
- override fun visit(condition: TypeMatches): Boolean {
- positionResolver.resolve(condition.position).onSome { value ->
- return when (value) {
- is JcValue -> {
- value.type.isAssignable(condition.type)
- }
-
- else -> error("Cannot evaluate $condition for $value")
- }
- }
- return false
- }
-}
-
-context(Traits)
-class FactAwareConditionEvaluator(
- private val fact: Tainted,
- positionResolver: PositionResolver>,
-) : BasicConditionEvaluator(positionResolver) {
-
- override fun visit(condition: ContainsMark): Boolean {
- if (fact.mark != condition.mark) return false
- positionResolver.resolve(condition.position).onSome { value ->
- val variable = value.toPath()
-
- // FIXME: Adhoc for arrays
- val variableWithoutStars = variable.removeTrailingElementAccessors()
- val factWithoutStars = fact.variable.removeTrailingElementAccessors()
- if (variableWithoutStars == factWithoutStars) return true
-
- return variable == fact.variable
- }
- return false
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/Position.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/Position.kt
deleted file mode 100644
index e0842c397..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/Position.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.config
-
-import org.jacodb.analysis.ifds.AccessPath
-import org.jacodb.analysis.ifds.ElementAccessor
-import org.jacodb.analysis.ifds.Maybe
-import org.jacodb.analysis.ifds.fmap
-import org.jacodb.analysis.ifds.toMaybe
-import org.jacodb.analysis.util.Traits
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.CommonProject
-import org.jacodb.api.common.cfg.CommonAssignInst
-import org.jacodb.api.common.cfg.CommonInst
-import org.jacodb.api.common.cfg.CommonInstanceCallExpr
-import org.jacodb.api.common.cfg.CommonValue
-import org.jacodb.taint.configuration.AnyArgument
-import org.jacodb.taint.configuration.Argument
-import org.jacodb.taint.configuration.Position
-import org.jacodb.taint.configuration.PositionResolver
-import org.jacodb.taint.configuration.Result
-import org.jacodb.taint.configuration.ResultAnyElement
-import org.jacodb.taint.configuration.This
-
-context(Traits)
-class CallPositionToAccessPathResolver(
- private val callStatement: CommonInst,
-) : PositionResolver> {
- private val callExpr = callStatement.getCallExpr()
- ?: error("Call statement should have non-null callExpr")
-
- override fun resolve(position: Position): Maybe = when (position) {
- AnyArgument -> Maybe.none()
- is Argument -> callExpr.args[position.index].toPathOrNull().toMaybe()
- This -> (callExpr as? CommonInstanceCallExpr)?.instance?.toPathOrNull().toMaybe()
- Result -> (callStatement as? CommonAssignInst)?.lhv?.toPathOrNull().toMaybe()
- ResultAnyElement -> (callStatement as? CommonAssignInst)?.lhv?.toPathOrNull().toMaybe()
- .fmap { it + ElementAccessor }
- }
-}
-
-context(Traits)
-class CallPositionToValueResolver(
- private val callStatement: CommonInst,
-) : PositionResolver> {
- private val callExpr = callStatement.getCallExpr()
- ?: error("Call statement should have non-null callExpr")
-
- override fun resolve(position: Position): Maybe = when (position) {
- AnyArgument -> Maybe.none()
- is Argument -> Maybe.some(callExpr.args[position.index])
- This -> (callExpr as? CommonInstanceCallExpr)?.instance.toMaybe()
- Result -> (callStatement as? CommonAssignInst)?.lhv.toMaybe()
- ResultAnyElement -> Maybe.none()
- }
-}
-
-context(Traits)
-class EntryPointPositionToValueResolver(
- private val method: CommonMethod,
-) : PositionResolver> {
- override fun resolve(position: Position): Maybe = when (position) {
- This -> Maybe.some(method.thisInstance)
-
- is Argument -> {
- val p = method.parameters[position.index]
- getArgument(p).toMaybe()
- }
-
- AnyArgument, Result, ResultAnyElement -> error("Unexpected $position")
- }
-}
-
-context(Traits)
-class EntryPointPositionToAccessPathResolver(
- private val method: CommonMethod,
-) : PositionResolver> {
- override fun resolve(position: Position): Maybe = when (position) {
- This -> method.thisInstance.toPathOrNull().toMaybe()
-
- is Argument -> {
- val p = method.parameters[position.index]
- getArgument(p)?.toPathOrNull().toMaybe()
- }
-
- AnyArgument, Result, ResultAnyElement -> error("Unexpected $position")
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/TaintAction.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/TaintAction.kt
deleted file mode 100644
index 59403b099..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/config/TaintAction.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.config
-
-import org.jacodb.analysis.ifds.AccessPath
-import org.jacodb.analysis.ifds.Maybe
-import org.jacodb.analysis.ifds.fmap
-import org.jacodb.analysis.ifds.map
-import org.jacodb.analysis.taint.Tainted
-import org.jacodb.taint.configuration.AssignMark
-import org.jacodb.taint.configuration.CopyAllMarks
-import org.jacodb.taint.configuration.CopyMark
-import org.jacodb.taint.configuration.PositionResolver
-import org.jacodb.taint.configuration.RemoveAllMarks
-import org.jacodb.taint.configuration.RemoveMark
-
-class TaintActionEvaluator(
- private val positionResolver: PositionResolver>,
-) {
- fun evaluate(action: CopyAllMarks, fact: Tainted): Maybe> =
- positionResolver.resolve(action.from).map { from ->
- if (from != fact.variable) return@map Maybe.none()
- positionResolver.resolve(action.to).fmap { to ->
- setOf(fact, fact.copy(variable = to))
- }
- }
-
- fun evaluate(action: CopyMark, fact: Tainted): Maybe> {
- if (fact.mark != action.mark) return Maybe.none()
- return positionResolver.resolve(action.from).map { from ->
- if (from != fact.variable) return@map Maybe.none()
- positionResolver.resolve(action.to).fmap { to ->
- setOf(fact, fact.copy(variable = to))
- }
- }
- }
-
- fun evaluate(action: AssignMark): Maybe> =
- positionResolver.resolve(action.position).fmap { variable ->
- setOf(Tainted(variable, action.mark))
- }
-
- fun evaluate(action: RemoveAllMarks, fact: Tainted): Maybe> =
- positionResolver.resolve(action.position).map { variable ->
- if (variable != fact.variable) return@map Maybe.none()
- Maybe.some(emptySet())
- }
-
- fun evaluate(action: RemoveMark, fact: Tainted): Maybe> {
- if (fact.mark != action.mark) return Maybe.none()
- return positionResolver.resolve(action.position).map { variable ->
- if (variable != fact.variable) return@map Maybe.none()
- Maybe.some(emptySet())
- }
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/ApplicationGraphFactory.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/ApplicationGraphFactory.kt
deleted file mode 100644
index 026256ffb..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/ApplicationGraphFactory.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:JvmName("ApplicationGraphFactory")
-
-package org.jacodb.analysis.graph
-
-import kotlinx.coroutines.DelicateCoroutinesApi
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.future.future
-import org.jacodb.api.jvm.JcClasspath
-import org.jacodb.api.jvm.analysis.JcApplicationGraph
-import org.jacodb.impl.features.usagesExt
-import java.util.concurrent.CompletableFuture
-
-/**
- * Creates an instance of [SimplifiedJcApplicationGraph], see its docs for more info.
- */
-suspend fun JcClasspath.newApplicationGraphForAnalysis(
- bannedPackagePrefixes: List? = null,
-): JcApplicationGraph {
- val mainGraph = JcApplicationGraphImpl(this, usagesExt())
- return if (bannedPackagePrefixes != null) {
- SimplifiedJcApplicationGraph(mainGraph, bannedPackagePrefixes)
- } else {
- SimplifiedJcApplicationGraph(mainGraph, defaultBannedPackagePrefixes)
- }
-}
-
-/**
- * Async adapter for calling [newApplicationGraphForAnalysis] from Java.
- *
- * See also: [answer on StackOverflow](https://stackoverflow.com/a/52887677/3592218).
- */
-@OptIn(DelicateCoroutinesApi::class)
-fun JcClasspath.newApplicationGraphForAnalysisAsync(
- bannedPackagePrefixes: List? = null,
-): CompletableFuture =
- GlobalScope.future {
- newApplicationGraphForAnalysis(bannedPackagePrefixes)
- }
-
-val defaultBannedPackagePrefixes: List = listOf(
- "kotlin.",
- "java.",
- "jdk.internal.",
- "sun.",
- "com.sun.",
- "javax.",
-)
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardGraphs.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardGraphs.kt
deleted file mode 100644
index f3bf2e601..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardGraphs.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:JvmName("BackwardApplicationGraphs")
-
-package org.jacodb.analysis.graph
-
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.analysis.ApplicationGraph
-import org.jacodb.api.common.cfg.CommonInst
-import org.jacodb.api.jvm.JcClasspath
-import org.jacodb.api.jvm.JcMethod
-import org.jacodb.api.jvm.analysis.JcApplicationGraph
-import org.jacodb.api.jvm.cfg.JcInst
-
-private class BackwardApplicationGraphImpl(
- val forward: ApplicationGraph,
-) : ApplicationGraph
- where Method : CommonMethod,
- Statement : CommonInst {
-
- override fun predecessors(node: Statement) = forward.successors(node)
- override fun successors(node: Statement) = forward.predecessors(node)
-
- override fun callees(node: Statement) = forward.callees(node)
- override fun callers(method: Method) = forward.callers(method)
-
- override fun entryPoints(method: Method) = forward.exitPoints(method)
- override fun exitPoints(method: Method) = forward.entryPoints(method)
-
- override fun methodOf(node: Statement) = forward.methodOf(node)
-}
-
-@Suppress("UNCHECKED_CAST")
-val ApplicationGraph.reversed: ApplicationGraph
- where Method : CommonMethod,
- Statement : CommonInst
- get() = when (this) {
- is JcApplicationGraph -> this.reversed as ApplicationGraph
- is BackwardApplicationGraphImpl -> this.forward
- else -> BackwardApplicationGraphImpl(this)
- }
-
-private class BackwardJcApplicationGraphImpl(
- val forward: JcApplicationGraph,
-) : JcApplicationGraph,
- ApplicationGraph by BackwardApplicationGraphImpl(forward) {
-
- override val cp: JcClasspath
- get() = forward.cp
-}
-
-val JcApplicationGraph.reversed: JcApplicationGraph
- get() = if (this is BackwardJcApplicationGraphImpl) {
- this.forward
- } else {
- BackwardJcApplicationGraphImpl(this)
- }
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt
deleted file mode 100644
index c0001b11a..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.graph
-
-import org.jacodb.api.jvm.JcClasspath
-import org.jacodb.api.jvm.JcMethod
-import org.jacodb.api.jvm.analysis.JcApplicationGraph
-import org.jacodb.api.jvm.cfg.JcInst
-import org.jacodb.api.jvm.ext.cfg.callExpr
-import org.jacodb.impl.features.SyncUsagesExtension
-
-/**
- * Possible we will need JcRawInst instead of JcInst
- */
-open class JcApplicationGraphImpl(
- override val cp: JcClasspath,
- private val usages: SyncUsagesExtension,
-) : JcApplicationGraph {
- override fun predecessors(node: JcInst): Sequence {
- val graph = node.location.method.flowGraph()
- val predecessors = graph.predecessors(node)
- val throwers = graph.throwers(node)
- return predecessors.asSequence() + throwers.asSequence()
- }
-
- override fun successors(node: JcInst): Sequence {
- val graph = node.location.method.flowGraph()
- val successors = graph.successors(node)
- val catchers = graph.catchers(node)
- return successors.asSequence() + catchers.asSequence()
- }
-
- override fun callees(node: JcInst): Sequence {
- val callExpr = node.callExpr ?: return emptySequence()
- return sequenceOf(callExpr.method.method)
- }
-
- override fun callers(method: JcMethod): Sequence {
- return usages.findUsages(method).flatMap {
- it.flowGraph().instructions.asSequence().filter { inst ->
- val callExpr = inst.callExpr ?: return@filter false
- callExpr.method.method == method
- }
- }
- }
-
- override fun entryPoints(method: JcMethod): Sequence {
- return method.flowGraph().entries.asSequence()
- }
-
- override fun exitPoints(method: JcMethod): Sequence {
- return method.flowGraph().exits.asSequence()
- }
-
- override fun methodOf(node: JcInst): JcMethod {
- return node.location.method
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcNoopInst.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcNoopInst.kt
deleted file mode 100644
index c57d9123d..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcNoopInst.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.graph
-
-import org.jacodb.api.jvm.cfg.JcExpr
-import org.jacodb.api.jvm.cfg.JcInst
-import org.jacodb.api.jvm.cfg.JcInstLocation
-import org.jacodb.api.jvm.cfg.JcInstVisitor
-
-data class JcNoopInst(override val location: JcInstLocation) : JcInst {
- override val operands: List
- get() = emptyList()
-
- override fun accept(visitor: JcInstVisitor): T {
- return visitor.visitExternalJcInst(this)
- }
-
- override fun toString(): String = "noop"
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/SimplifiedJcApplicationGraph.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/SimplifiedJcApplicationGraph.kt
deleted file mode 100644
index 83388c0f9..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/SimplifiedJcApplicationGraph.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.graph
-
-import kotlinx.coroutines.runBlocking
-import org.jacodb.api.jvm.JcClassType
-import org.jacodb.api.jvm.JcMethod
-import org.jacodb.api.jvm.analysis.JcApplicationGraph
-import org.jacodb.api.jvm.cfg.JcInst
-import org.jacodb.api.jvm.cfg.JcVirtualCallExpr
-import org.jacodb.api.jvm.ext.cfg.callExpr
-import org.jacodb.api.jvm.ext.isSubClassOf
-import org.jacodb.impl.cfg.JcInstLocationImpl
-import org.jacodb.impl.features.hierarchyExt
-
-/**
- * This is adopted specially for IFDS [JcApplicationGraph] that
- * 1. Ignores method calls matching [bannedPackagePrefixes] (i.e., treats them as simple instructions with no callees)
- * 2. In [callers] returns only call sites that were visited before
- * 3. Adds a special [JcNoopInst] instruction to the beginning of each method
- * (because backward analysis may want for method to start with neutral instruction)
- */
-internal class SimplifiedJcApplicationGraph(
- private val graph: JcApplicationGraph,
- private val bannedPackagePrefixes: List,
-) : JcApplicationGraph by graph {
- private val hierarchyExtension = runBlocking {
- cp.hierarchyExt()
- }
-
- private val visitedCallers: MutableMap> = mutableMapOf()
-
- private val cache: MutableMap> = mutableMapOf()
-
- // For backward analysis we may want for method to start with "neutral" operation =>
- // we add noop to the beginning of every method
- private fun getStartInst(method: JcMethod): JcNoopInst {
- val lineNumber = method.flowGraph().entries.firstOrNull()?.lineNumber?.let { it - 1 } ?: -1
- return JcNoopInst(JcInstLocationImpl(method, -1, lineNumber))
- }
-
- override fun predecessors(node: JcInst): Sequence {
- val method = methodOf(node)
- return when (node) {
- getStartInst(method) -> {
- emptySequence()
- }
-
- in graph.entryPoints(method) -> {
- sequenceOf(getStartInst(method))
- }
-
- else -> {
- graph.predecessors(node)
- }
- }
- }
-
- override fun successors(node: JcInst): Sequence {
- val method = methodOf(node)
- return when (node) {
- getStartInst(method) -> {
- graph.entryPoints(method)
- }
-
- else -> {
- graph.successors(node)
- }
- }
- }
-
- private fun getOverrides(method: JcMethod): List {
- return if (cache.containsKey(method)) {
- cache[method]!!
- } else {
- val res = hierarchyExtension.findOverrides(method).toList()
- cache[method] = res
- res
- }
- }
-
- private fun calleesUnmarked(node: JcInst): Sequence {
- val callees = graph.callees(node).filterNot { callee ->
- bannedPackagePrefixes.any { callee.enclosingClass.name.startsWith(it) }
- }
-
- val callExpr = node.callExpr as? JcVirtualCallExpr ?: return callees
- val instanceClass = (callExpr.instance.type as? JcClassType)?.jcClass ?: return callees
-
- return callees
- .flatMap { callee ->
- val allOverrides = getOverrides(callee)
- .filter {
- it.enclosingClass isSubClassOf instanceClass ||
- // TODO: use only down-most override here
- instanceClass isSubClassOf it.enclosingClass
- }
-
- // TODO: maybe filter inaccessible methods here?
- allOverrides + sequenceOf(callee)
- }
- }
-
- override fun callees(node: JcInst): Sequence {
- return calleesUnmarked(node).also {
- it.forEach { method ->
- visitedCallers.getOrPut(method) { mutableSetOf() }.add(node)
- }
- }
- }
-
- /**
- * This is IFDS-algorithm aware optimization.
- * In IFDS we don't need all method callers, we need only method callers which we visited earlier.
- */
- // TODO: Think if this optimization is really needed
- override fun callers(method: JcMethod): Sequence =
- visitedCallers[method].orEmpty().asSequence()
-
- override fun entryPoints(method: JcMethod): Sequence = try {
- sequenceOf(getStartInst(method))
- } catch (e: Throwable) {
- // we couldn't find instructions list
- // TODO: maybe fix flowGraph()
- emptySequence()
- }
-
- override fun exitPoints(method: JcMethod): Sequence = try {
- graph.exitPoints(method)
- } catch (e: Throwable) {
- // we couldn't find instructions list
- // TODO: maybe fix flowGraph()
- emptySequence()
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/AccessPath.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/AccessPath.kt
deleted file mode 100644
index ad7ba3123..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/AccessPath.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.cfg.CommonValue
-
-data class AccessPath internal constructor(
- val value: CommonValue?,
- val accesses: List,
-) {
- init {
- if (value == null) {
- require(accesses.isNotEmpty())
- val a = accesses[0]
- require(a is FieldAccessor)
- require(a.isStatic)
- }
- }
-
- fun limit(n: Int): AccessPath = AccessPath(value, accesses.take(n))
-
- operator fun plus(accesses: List): AccessPath {
- for (accessor in accesses) {
- if (accessor is FieldAccessor && accessor.isStatic) {
- throw IllegalArgumentException("Unexpected static field: ${accessor.name}")
- }
- }
-
- return AccessPath(value, this.accesses + accesses)
- }
-
- operator fun plus(accessor: Accessor): AccessPath {
- if (accessor is FieldAccessor && accessor.isStatic) {
- throw IllegalArgumentException("Unexpected static field: ${accessor.name}")
- }
-
- return AccessPath(value, this.accesses + accessor)
- }
-
- override fun toString(): String {
- return value.toString() + accesses.joinToString("") { it.toSuffix() }
- }
-}
-
-val AccessPath.isOnHeap: Boolean
- get() = accesses.isNotEmpty()
-
-val AccessPath.isStatic: Boolean
- get() = value == null
-
-operator fun AccessPath.minus(other: AccessPath): List? {
- if (value != other.value) return null
- if (accesses.take(other.accesses.size) != other.accesses) return null
- return accesses.drop(other.accesses.size)
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Accessors.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Accessors.kt
deleted file mode 100644
index 22a29c38e..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Accessors.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-sealed interface Accessor {
- fun toSuffix(): String
-}
-
-data class FieldAccessor(
- val name: String,
- val isStatic: Boolean = false,
-) : Accessor {
- override fun toSuffix(): String = ".${name}"
- override fun toString(): String = name
-}
-
-object ElementAccessor : Accessor {
- override fun toSuffix(): String = "[*]"
- override fun toString(): String = "*"
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Analyzer.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Analyzer.kt
deleted file mode 100644
index 00e24c1ca..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Analyzer.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.cfg.CommonInst
-
-interface Analyzer
- where Method : CommonMethod,
- Statement : CommonInst {
-
- val flowFunctions: FlowFunctions
-
- fun handleNewEdge(
- edge: Edge,
- ): List
-
- fun handleCrossUnitCall(
- caller: Vertex,
- callee: Vertex,
- ): List
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Edge.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Edge.kt
deleted file mode 100644
index 9ed0e27cc..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Edge.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.cfg.CommonInst
-
-data class Edge(
- val from: Vertex,
- val to: Vertex,
-) {
- init {
- require(from.method == to.method)
- }
-
- val method: CommonMethod
- get() = from.method
-
- override fun toString(): String {
- return "(${from.fact} at ${from.statement}) -> (${to.fact} at ${to.statement}) in $method"
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/FlowFunctions.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/FlowFunctions.kt
deleted file mode 100644
index c6878bc39..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/FlowFunctions.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.cfg.CommonInst
-
-fun interface FlowFunction {
- fun compute(fact: Fact): Collection
-}
-
-interface FlowFunctions
- where Method : CommonMethod,
- Statement : CommonInst {
-
- /**
- * Method for obtaining initial domain facts at the method entrypoint.
- * Commonly, it is only `listOf(Zero)`.
- */
- fun obtainPossibleStartFacts(method: Method): Collection
-
- /**
- * Sequent flow function.
- *
- * ```
- * [ DO() ] :: current
- * |
- * | (sequent edge)
- * |
- * [ DO() ]
- * ```
- */
- fun obtainSequentFlowFunction(
- current: Statement,
- next: Statement,
- ): FlowFunction
-
- /**
- * Call-to-return-site flow function.
- *
- * ```
- * [ CALL p ] :: callStatement
- * :
- * : (call-to-return-site edge)
- * :
- * [ RETURN FROM p ] :: returnSite
- * ```
- */
- fun obtainCallToReturnSiteFlowFunction(
- callStatement: Statement,
- returnSite: Statement,
- ): FlowFunction
-
- /**
- * Call-to-start flow function.
- *
- * ```
- * [ CALL p ] :: callStatement
- * : \
- * : \ (call-to-start edge)
- * : \
- * : [ START p ]
- * : |
- * : [ EXIT p ]
- * : /
- * : /
- * [ RETURN FROM p ]
- * ```
- */
- fun obtainCallToStartFlowFunction(
- callStatement: Statement,
- calleeStart: Statement,
- ): FlowFunction
-
- /**
- * Exit-to-return-site flow function.
- *
- * ```
- * [ CALL p ] :: callStatement
- * : \
- * : \
- * : [ START p ]
- * : |
- * : [ EXIT p ] :: exitStatement
- * : /
- * : / (exit-to-return-site edge)
- * : /
- * [ RETURN FROM p ] :: returnSite
- * ```
- */
- fun obtainExitToReturnSiteFlowFunction(
- callStatement: Statement,
- returnSite: Statement,
- exitStatement: Statement,
- ): FlowFunction
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/IfdsResult.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/IfdsResult.kt
deleted file mode 100644
index 51e2e6fa1..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/IfdsResult.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.cfg.CommonInst
-
-/**
- * Aggregates all facts and edges found by the tabulation algorithm.
- */
-class IfdsResult internal constructor(
- val pathEdgesBySink: Map, Collection>>,
- val facts: Map>,
- val reasons: Map, Set>>,
- val zeroFact: Fact,
-) {
-
- constructor(
- pathEdges: Collection>,
- facts: Map>,
- reasons: Map, Set>>,
- zeroFact: Fact,
- ) : this(
- pathEdges.groupByTo(HashMap()) { it.to },
- facts,
- reasons,
- zeroFact
- )
-
- fun buildTraceGraph(sink: Vertex): TraceGraph {
- val sources: MutableSet> =
- hashSetOf()
- val edges: MutableMap, MutableSet>> =
- hashMapOf()
- val unresolvedCrossUnitCalls: MutableMap, MutableSet>> =
- hashMapOf()
- val visited: MutableSet, Vertex>> =
- hashSetOf()
-
- fun addEdge(
- from: Vertex,
- to: Vertex,
- ) {
- if (from != to) {
- edges.getOrPut(from) { hashSetOf() }.add(to)
- }
- }
-
- fun dfs(
- edge: Edge,
- lastVertex: Vertex,
- stopAtMethodStart: Boolean,
- ) {
- if (!visited.add(edge to lastVertex)) {
- return
- }
-
- // Note: loop-edge represents method start
- if (stopAtMethodStart && edge.from == edge.to) {
- addEdge(edge.from, lastVertex)
- return
- }
-
- val vertex = edge.to
- if (vertex.fact == zeroFact) {
- addEdge(vertex, lastVertex)
- sources.add(vertex)
- return
- }
-
- for (reason in reasons[edge].orEmpty()) {
- when (reason) {
- is Reason.Sequent -> {
- val predEdge = reason.edge
- if (predEdge.to.fact == vertex.fact) {
- dfs(predEdge, lastVertex, stopAtMethodStart)
- } else {
- addEdge(predEdge.to, lastVertex)
- dfs(predEdge, predEdge.to, stopAtMethodStart)
- }
- }
-
- is Reason.CallToStart -> {
- val predEdge = reason.edge
- if (!stopAtMethodStart) {
- addEdge(predEdge.to, lastVertex)
- dfs(predEdge, predEdge.to, false)
- }
- }
-
- is Reason.ThroughSummary -> {
- val predEdge = reason.edge
- val summaryEdge = reason.summaryEdge
- addEdge(summaryEdge.to, lastVertex) // Return to next vertex
- addEdge(predEdge.to, summaryEdge.from) // Call to start
- dfs(summaryEdge, summaryEdge.to, true) // Expand summary edge
- dfs(predEdge, predEdge.to, stopAtMethodStart) // Continue normal analysis
- }
-
- is Reason.CrossUnitCall -> {
- addEdge(edge.to, lastVertex)
- unresolvedCrossUnitCalls.getOrPut(reason.caller) { hashSetOf() }.add(edge.to)
- }
-
- is Reason.External -> {
- TODO("External reason is not supported yet")
- }
-
- is Reason.Initial -> {
- sources.add(vertex)
- addEdge(edge.to, lastVertex)
- }
- }
- }
- }
-
- for (edge in pathEdgesBySink[sink].orEmpty()) {
- dfs(edge, edge.to, false)
- }
- return TraceGraph(sink, sources, edges, unresolvedCrossUnitCalls)
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Manager.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Manager.kt
deleted file mode 100644
index 85541f581..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Manager.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import kotlinx.coroutines.CoroutineScope
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.cfg.CommonInst
-
-interface Manager
- where Method : CommonMethod,
- Statement : CommonInst {
-
- fun handleEvent(event: Event)
-
- fun handleControlEvent(event: ControlEvent)
-
- fun subscribeOnSummaryEdges(
- method: @UnsafeVariance Method,
- scope: CoroutineScope,
- handler: (Edge) -> Unit,
- )
-}
-
-sealed interface ControlEvent
-
-data class QueueEmptinessChanged(
- val runner: Runner<*, *, *>,
- val isEmpty: Boolean,
-) : ControlEvent
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Reason.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Reason.kt
deleted file mode 100644
index a0a3396bf..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Reason.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.cfg.CommonInst
-
-sealed interface Reason {
-
- object Initial : Reason
-
- object External : Reason
-
- data class CrossUnitCall(
- val caller: Vertex,
- ) : Reason
-
- data class Sequent(
- val edge: Edge,
- ) : Reason
-
- data class CallToStart(
- val edge: Edge,
- ) : Reason
-
- data class ThroughSummary(
- val edge: Edge,
- val summaryEdge: Edge,
- ) : Reason
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Runner.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Runner.kt
deleted file mode 100644
index 06b7f2ea2..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Runner.kt
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.channels.getOrElse
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.isActive
-import org.jacodb.analysis.graph.JcNoopInst
-import org.jacodb.analysis.taint.TaintZeroFact
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.analysis.ApplicationGraph
-import org.jacodb.api.common.cfg.CommonCallExpr
-import org.jacodb.api.common.cfg.CommonInst
-import org.jacodb.api.jvm.cfg.JcInst
-import org.jacodb.ets.base.EtsStmt
-import java.util.concurrent.ConcurrentHashMap
-import org.jacodb.api.jvm.ext.cfg.callExpr as jcCallExpr
-import org.jacodb.ets.utils.callExpr as etsCallExpr
-import org.jacodb.panda.staticvm.cfg.PandaInst as StaticPandaInst
-import org.jacodb.panda.staticvm.utils.callExpr as staticPandaCallExpr
-
-private val logger = mu.KotlinLogging.logger {}
-
-interface Runner
- where Method : CommonMethod,
- Statement : CommonInst {
-
- val graph: ApplicationGraph
- val unit: UnitType
-
- suspend fun run(startMethods: List)
- fun submitNewEdge(edge: Edge, reason: Reason)
- fun getIfdsResult(): IfdsResult
-}
-
-class UniRunner(
- private val manager: Manager,
- override val graph: ApplicationGraph,
- private val analyzer: Analyzer,
- private val unitResolver: UnitResolver,
- override val unit: UnitType,
- private val zeroFact: Fact,
-) : Runner
- where Method : CommonMethod,
- Statement : CommonInst {
-
- private val Statement.callExpr: CommonCallExpr?
- get() = when (this) {
- is JcInst -> jcCallExpr
- is StaticPandaInst -> staticPandaCallExpr
- is EtsStmt -> etsCallExpr
- else -> error("Unsupported statement type: $this")
- }
-
- private val flowSpace: FlowFunctions = analyzer.flowFunctions
- private val workList: Channel> = Channel(Channel.UNLIMITED)
- internal val pathEdges: MutableSet> = ConcurrentHashMap.newKeySet()
- private val reasons =
- ConcurrentHashMap, MutableSet>>()
-
- private val summaryEdges: MutableMap, MutableSet>> =
- hashMapOf()
- private val callerPathEdgeOf: MutableMap, MutableSet>> =
- hashMapOf()
-
- private val queueIsEmpty = QueueEmptinessChanged(runner = this, isEmpty = true)
- private val queueIsNotEmpty = QueueEmptinessChanged(runner = this, isEmpty = false)
-
- override suspend fun run(startMethods: List) {
- for (method in startMethods) {
- addStart(method)
- }
-
- tabulationAlgorithm()
- }
-
- private fun addStart(method: Method) {
- require(unitResolver.resolve(method) == unit)
- val startFacts = flowSpace.obtainPossibleStartFacts(method)
- for (startFact in startFacts) {
- for (start in graph.entryPoints(method)) {
- val vertex = Vertex(start, startFact)
- val edge = Edge(vertex, vertex) // loop
- propagate(edge, Reason.Initial)
- }
- }
- }
-
- override fun submitNewEdge(edge: Edge, reason: Reason) {
- propagate(edge, reason)
- }
-
- private fun propagate(
- edge: Edge,
- reason: Reason,
- ): Boolean {
- val method = graph.methodOf(edge.from.statement)
- require(unitResolver.resolve(method) == unit) {
- "Propagated edge must be in the same unit"
- }
-
- reasons.computeIfAbsent(edge) { ConcurrentHashMap.newKeySet() }.add(reason)
-
- // Handle only NEW edges:
- if (pathEdges.add(edge)) {
- val doPrintOnlyForward = true
- val doPrintZero = false
- if (!doPrintOnlyForward || edge.from.statement is JcNoopInst) {
- if (doPrintZero || edge.to.fact != TaintZeroFact) {
- logger.trace {
- "Propagating edge=${edge} in method=${method.name} with reason=${reason}"
- }
- }
- }
-
- // Send edge to analyzer/manager:
- for (event in analyzer.handleNewEdge(edge)) {
- manager.handleEvent(event)
- }
-
- // Add edge to worklist:
- workList.trySend(edge).getOrThrow()
-
- return true
- }
-
- return false
- }
-
- private suspend fun tabulationAlgorithm() = coroutineScope {
- while (isActive) {
- val edge = workList.tryReceive().getOrElse {
- manager.handleControlEvent(queueIsEmpty)
- val edge = workList.receive()
- manager.handleControlEvent(queueIsNotEmpty)
- edge
- }
- tabulationAlgorithmStep(edge, this@coroutineScope)
- }
- }
-
- private val Method.isExtern: Boolean
- get() = unitResolver.resolve(this) != unit
-
- private fun tabulationAlgorithmStep(
- currentEdge: Edge,
- scope: CoroutineScope,
- ) {
- val (startVertex, currentVertex) = currentEdge
- val (current, currentFact) = currentVertex
-
- val currentCallees = graph.callees(current).toList()
- val currentIsCall = current.callExpr != null
- val currentIsExit = current in graph.exitPoints(graph.methodOf(current))
-
- if (currentIsCall) {
- // Propagate through the call-to-return-site edge:
- for (returnSite in graph.successors(current)) {
- val factsAtReturnSite = flowSpace
- .obtainCallToReturnSiteFlowFunction(current, returnSite)
- .compute(currentFact)
- for (returnSiteFact in factsAtReturnSite) {
- val returnSiteVertex = Vertex(returnSite, returnSiteFact)
- val newEdge = Edge(startVertex, returnSiteVertex)
- propagate(newEdge, Reason.Sequent(currentEdge))
- }
- }
-
- // Propagate through the call:
- for (callee in currentCallees) {
- for (calleeStart in graph.entryPoints(callee)) {
- val factsAtCalleeStart = flowSpace
- .obtainCallToStartFlowFunction(current, calleeStart)
- .compute(currentFact)
- for (calleeStartFact in factsAtCalleeStart) {
- val calleeStartVertex = Vertex(calleeStart, calleeStartFact)
-
- if (callee.isExtern) {
- // Initialize analysis of callee:
- for (event in analyzer.handleCrossUnitCall(currentVertex, calleeStartVertex)) {
- manager.handleEvent(event)
- }
-
- // Subscribe on summary edges:
- manager.subscribeOnSummaryEdges(callee, scope) { summaryEdge ->
- if (summaryEdge.from == calleeStartVertex) {
- handleSummaryEdge(currentEdge, summaryEdge)
- } else {
- logger.trace { "Skipping unsuitable summary edge: $summaryEdge" }
- }
- }
- } else {
- // Save info about the call for summary edges that will be found later:
- callerPathEdgeOf.getOrPut(calleeStartVertex) { hashSetOf() }.add(currentEdge)
-
- // Initialize analysis of callee:
- run {
- val newEdge = Edge(calleeStartVertex, calleeStartVertex) // loop
- propagate(newEdge, Reason.CallToStart(currentEdge))
- }
-
- // Handle already-found summary edges:
- for (exitVertex in summaryEdges[calleeStartVertex].orEmpty()) {
- val summaryEdge = Edge(calleeStartVertex, exitVertex)
- handleSummaryEdge(currentEdge, summaryEdge)
- }
- }
- }
- }
- }
- } else {
- if (currentIsExit) {
- // Propagate through the summary edge:
- for (callerPathEdge in callerPathEdgeOf[startVertex].orEmpty()) {
- handleSummaryEdge(currentEdge = callerPathEdge, summaryEdge = currentEdge)
- }
-
- // Add new summary edge:
- summaryEdges.getOrPut(startVertex) { hashSetOf() }.add(currentVertex)
- }
-
- // Simple (sequential) propagation to the next instruction:
- for (next in graph.successors(current)) {
- val factsAtNext = flowSpace
- .obtainSequentFlowFunction(current, next)
- .compute(currentFact)
- for (nextFact in factsAtNext) {
- val nextVertex = Vertex(next, nextFact)
- val newEdge = Edge(startVertex, nextVertex)
- propagate(newEdge, Reason.Sequent(currentEdge))
- }
- }
- }
- }
-
- private fun handleSummaryEdge(
- currentEdge: Edge,
- summaryEdge: Edge,
- ) {
- val (startVertex, currentVertex) = currentEdge
- val caller = currentVertex.statement
- for (returnSite in graph.successors(caller)) {
- val (exit, exitFact) = summaryEdge.to
- val finalFacts = flowSpace
- .obtainExitToReturnSiteFlowFunction(caller, returnSite, exit)
- .compute(exitFact)
- for (returnSiteFact in finalFacts) {
- val returnSiteVertex = Vertex(returnSite, returnSiteFact)
- val newEdge = Edge(startVertex, returnSiteVertex)
- propagate(newEdge, Reason.ThroughSummary(currentEdge, summaryEdge))
- }
- }
- }
-
- private fun getFinalFacts(): Map> {
- val resultFacts: MutableMap> = hashMapOf()
- for (edge in pathEdges) {
- resultFacts.getOrPut(edge.to.statement) { hashSetOf() }.add(edge.to.fact)
- }
- return resultFacts
- }
-
- override fun getIfdsResult(): IfdsResult {
- val facts = getFinalFacts()
- return IfdsResult(pathEdges, facts, reasons, zeroFact)
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Summary.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Summary.kt
deleted file mode 100644
index c7e06ef31..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Summary.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.cfg.CommonInst
-import java.util.concurrent.ConcurrentHashMap
-
-/**
- * A common interface for anything that should be remembered
- * and used after the analysis of some unit is completed.
- */
-interface Summary {
- val method: Method
-}
-
-interface SummaryEdge : Summary {
-
- val edge: Edge
-
- override val method: CommonMethod
- get() = edge.method
-}
-
-interface Vulnerability : Summary {
- val message: String
- val sink: Vertex
-
- override val method: CommonMethod
- get() = sink.method
-}
-
-/**
- * Contains summaries for many methods and allows to update them and subscribe for them.
- */
-interface SummaryStorage> {
-
- /**
- * A list of all methods for which summaries are not empty.
- */
- val knownMethods: List
-
- /**
- * Adds [summary] the summaries storage of its method.
- */
- fun add(summary: T)
-
- /**
- * @return a flow with all facts summarized for the given [method].
- * Already received facts, along with the facts that will be sent to this storage later,
- * will be emitted to the returned flow.
- */
- fun getFacts(method: CommonMethod): Flow
-
- /**
- * @return a list will all facts summarized for the given [method] so far.
- */
- fun getCurrentFacts(method: CommonMethod): List
-}
-
-class SummaryStorageImpl> : SummaryStorage {
-
- private val summaries = ConcurrentHashMap>()
- private val outFlows = ConcurrentHashMap>()
-
- override val knownMethods: List
- get() = summaries.keys.toList()
-
- private fun getFlow(method: CommonMethod): MutableSharedFlow {
- return outFlows.computeIfAbsent(method) {
- MutableSharedFlow(replay = Int.MAX_VALUE)
- }
- }
-
- override fun add(summary: T) {
- val isNew = summaries.computeIfAbsent(summary.method) {
- ConcurrentHashMap.newKeySet()
- }.add(summary)
- if (isNew) {
- val flow = getFlow(summary.method)
- check(flow.tryEmit(summary))
- }
- }
-
- override fun getFacts(method: CommonMethod): SharedFlow {
- return getFlow(method)
- }
-
- override fun getCurrentFacts(method: CommonMethod): List {
- return getFacts(method).replayCache
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/TraceGraph.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/TraceGraph.kt
deleted file mode 100644
index 13de77959..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/TraceGraph.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.cfg.CommonInst
-
-data class TraceGraph(
- val sink: Vertex,
- val sources: MutableSet>,
- val edges: MutableMap, MutableSet>>,
- val unresolvedCrossUnitCalls: Map, Set>>,
-) {
-
- /**
- * Returns all traces from [sources] to [sink].
- */
- fun getAllTraces(): Sequence>> =
- sources.asSequence().flatMap { getAllTraces(mutableListOf(it)) }
-
- private fun getAllTraces(
- trace: MutableList>,
- ): Sequence>> = sequence {
- val v = trace.last()
- if (v == sink) {
- yield(trace.toList()) // copy list
- return@sequence
- }
- for (u in edges[v].orEmpty()) {
- if (u !in trace) {
- trace.add(u)
- yieldAll(getAllTraces(trace))
- trace.removeLast()
- }
- }
- }
-
- /**
- * Merges [upGraph] into this graph.
- */
- fun mergeWithUpGraph(
- upGraph: TraceGraph,
- entryPoints: Set>,
- ) {
- sources.addAll(upGraph.sources)
-
- for (edge in upGraph.edges) {
- edges.getOrPut(edge.key) { hashSetOf() }.addAll(edge.value)
- }
-
- edges.getOrPut(upGraph.sink) { hashSetOf() }.addAll(entryPoints)
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/UnitResolver.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/UnitResolver.kt
deleted file mode 100644
index 5afa581af..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/UnitResolver.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:Suppress("FunctionName")
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.jvm.JcClassOrInterface
-import org.jacodb.api.jvm.JcMethod
-import org.jacodb.api.jvm.ext.packageName
-
-interface UnitType
-
-data class MethodUnit(val method: JcMethod) : UnitType {
- override fun toString(): String {
- return "MethodUnit(${method.name})"
- }
-}
-
-data class ClassUnit(val clazz: JcClassOrInterface) : UnitType {
- override fun toString(): String {
- return "ClassUnit(${clazz.simpleName})"
- }
-}
-
-data class PackageUnit(val packageName: String) : UnitType {
- override fun toString(): String {
- return "PackageUnit($packageName)"
- }
-}
-
-object SingletonUnit : UnitType {
- override fun toString(): String = javaClass.simpleName
-}
-
-object UnknownUnit : UnitType {
- override fun toString(): String = javaClass.simpleName
-}
-
-/**
- * Sets a mapping from a [Method] to abstract domain [UnitType].
- *
- * Therefore, it splits all methods into units, containing one or more method each
- * (unit is a set of methods with same value of [UnitType] returned by [resolve]).
- *
- * To get more info about how it is used in analysis, see [runAnalysis].
- */
-fun interface UnitResolver {
- fun resolve(method: Method): UnitType
-
- companion object {
- fun getByName(name: String): UnitResolver = when (name) {
- "method" -> MethodUnitResolver
- "class" -> ClassUnitResolver(false)
- "package" -> PackageUnitResolver
- "singleton" -> SingletonUnitResolver
- else -> error("Unknown unit resolver '$name'")
- }
- }
-}
-
-fun interface JcUnitResolver : UnitResolver
-
-val MethodUnitResolver = JcUnitResolver { method ->
- MethodUnit(method)
-}
-
-private val ClassUnitResolverWithNested = JcUnitResolver { method ->
- val clazz = generateSequence(method.enclosingClass) { it.outerClass }.last()
- ClassUnit(clazz)
-}
-private val ClassUnitResolverWithoutNested = JcUnitResolver { method ->
- val clazz = method.enclosingClass
- ClassUnit(clazz)
-}
-
-fun ClassUnitResolver(includeNested: Boolean) =
- if (includeNested) {
- ClassUnitResolverWithNested
- } else {
- ClassUnitResolverWithoutNested
- }
-
-val PackageUnitResolver = JcUnitResolver { method ->
- PackageUnit(method.enclosingClass.packageName)
-}
-
-val SingletonUnitResolver = JcUnitResolver {
- SingletonUnit
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Vertex.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Vertex.kt
deleted file mode 100644
index a3830ee6b..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/ifds/Vertex.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.ifds
-
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.cfg.CommonInst
-
-data class Vertex(
- val statement: Statement,
- val fact: Fact,
-) {
- val method: CommonMethod
- get() = statement.method
-
- override fun toString(): String {
- return "$fact at $statement in $method"
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/AbstractFlowAnalysis.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/AbstractFlowAnalysis.kt
deleted file mode 100644
index 492c41c86..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/AbstractFlowAnalysis.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.impl.custom
-
-import org.jacodb.api.jvm.cfg.JcBytecodeGraph
-
-abstract class AbstractFlowAnalysis(override val graph: JcBytecodeGraph) : FlowAnalysis {
-
- override fun newEntryFlow(): T = newFlow()
-
- protected open fun merge(successor: NODE, income1: T, income2: T, outcome: T) {
- merge(income1, income2, outcome)
- }
-
- open fun ins(s: NODE): T? {
- return ins[s]
- }
-
- protected fun mergeInto(successor: NODE, input: T, incoming: T) {
- val tmp = newFlow()
- merge(successor, input, incoming, tmp)
- copy(tmp, input)
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/BackwardFlowAnalysis.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/BackwardFlowAnalysis.kt
deleted file mode 100644
index 53c35774b..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/BackwardFlowAnalysis.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.impl.custom
-
-import org.jacodb.api.jvm.cfg.JcBytecodeGraph
-
-abstract class BackwardFlowAnalysis(graph: JcBytecodeGraph) : FlowAnalysisImpl(graph) {
-
- override val isForward: Boolean = false
-
- override fun run() {
- runAnalysis(FlowAnalysisDirection.BACKWARD, outs, ins)
- }
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysis.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysis.kt
deleted file mode 100644
index 105ed664d..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysis.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.impl.custom
-
-import org.jacodb.api.jvm.cfg.JcBytecodeGraph
-
-interface FlowAnalysis {
-
- val ins: MutableMap
- val outs: MutableMap
-
- val graph: JcBytecodeGraph
-
- val isForward: Boolean
-
- fun newFlow(): T
-
- fun newEntryFlow(): T
-
- fun merge(in1: T, in2: T, out: T)
-
- fun copy(source: T?, dest: T)
-
- fun run()
-}
diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysisImpl.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysisImpl.kt
deleted file mode 100644
index 454436c19..000000000
--- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/impl/custom/FlowAnalysisImpl.kt
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright 2022 UnitTestBot contributors (utbot.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jacodb.analysis.impl.custom
-
-import org.jacodb.api.jvm.cfg.JcBytecodeGraph
-import org.jacodb.api.jvm.cfg.JcGotoInst
-import java.util.*
-
-enum class Flow {
- IN {
- override fun getFlow(e: FlowEntry): F? {
- return e.inFlow
- }
- },
- OUT {
- override fun getFlow(e: FlowEntry): F? {
- return e.outFlow
- }
- };
-
- abstract fun getFlow(e: FlowEntry): F?
-}
-
-/**
- * Creates a new `Entry` graph based on a `JcGraph`. This includes pseudo topological order, local
- * access for predecessors and successors, a graph entry-point, connected component marker.
- */
-private fun JcBytecodeGraph.newScope(
- direction: FlowAnalysisDirection,
- entryFlow: T,
- isForward: Boolean,
-): List> {
- val size = toList().size
- val s = ArrayDeque>(size)
- val scope = ArrayList>(size)
- val visited = HashMap>((size + 1) * 4 / 3)
-
- // out of scope node
- val instructions: List?
- val actualEntries = direction.entries(this)
- if (actualEntries.isNotEmpty()) {
- // normal cases: there is at least
- // one return statement for a backward analysis
- // or one entry statement for a forward analysis
- instructions = actualEntries
- } else {
- // cases without any entry statement
- if (isForward) {
- // case of a forward flow analysis on
- // a method without any entry point
- throw RuntimeException("No entry point for method in forward analysis")
- } else {
- // case of backward analysis on
- // a method which potentially has
- // an infinite loop and no return statement
- instructions = ArrayList()
- val head = entries.first()
-
- // collect all 'goto' statements to catch the 'goto' from the infinite loop
- val visitedInst = HashSet()
- val list = arrayListOf(head)
- var temp: NODE
- while (list.isNotEmpty()) {
- temp = list.removeAt(0)
- visitedInst.add(temp)
-
- // only add 'goto' statements
- if (temp is JcGotoInst) {
- instructions.add(temp)
- }
- for (next in successors(temp)) {
- if (visitedInst.contains(next)) {
- continue
- }
- list.add(next)
- }
- }
-
- if (instructions.isEmpty()) {
- throw RuntimeException("Backward analysis on an empty entry set.")
- }
- }
- }
- val root = RootEntry()
- root.visitEntry(instructions, visited)
- root.inFlow = entryFlow
- root.outFlow = entryFlow
-
- val sv: Array?> = arrayOfNulls(size)
- val si = IntArray(size)
- var index = 0
- var i = 0
- var entry: FlowEntry = root
- while (true) {
- if (i < entry.outs.size) {
- val next = entry.outs[i++]
-
- // an unvisited child node
- if (next.number == Int.MIN_VALUE) {
- next.number = s.size
- s.add(next)
- next.visitEntry(direction.outOf(this, next.data), visited)
-
- // save old
- si[index] = i
- sv[index] = entry
- index++
- i = 0
- entry = next
- }
- } else {
- if (index == 0) {
- assert(scope.size <= size)
- scope.reverse()
- return scope
- }
- scope.add(entry)
- s.pop(entry)
-
- // restore old
- index--
- entry = sv[index]!!
- i = si[index]
- }
- }
-}
-
-private fun FlowEntry.visitEntry(
- instructions: List,
- visited: MutableMap>,
-): Array> {
- val n = instructions.size
- return Array(n) {
- instructions[it].toEntry(this, visited)
- }.also {
- outs = it
- }
-}
-
-private fun NODE.toEntry(
- pred: FlowEntry,
- visited: MutableMap>,
-): FlowEntry {
- // either we reach a new node or a merge node, the latter one is rare
- // so put and restore should be better that a lookup
-
- val newEntry = LeafEntry(this, pred)
- val oldEntry = visited.putIfAbsent(this, newEntry) ?: return newEntry
-
- // no restore required
-
- // adding self ref (real strongly connected with itself)
- if (oldEntry === pred) {
- oldEntry.isStronglyConnected = true
- }
-
- // merge nodes are rare, so this is ok
- val length = oldEntry.ins.size
- oldEntry.ins = Arrays.copyOf(oldEntry.ins, length + 1)
- oldEntry.ins[length] = pred
- return oldEntry
-}
-
-private fun Deque>.pop(entry: FlowEntry) {
- var min = entry.number
- for (e in entry.outs) {
- assert(e.number > Int.MIN_VALUE)
- min = min.coerceAtMost(e.number)
- }
-
- // not our SCC
- if (min != entry.number) {
- entry.number = min
- return
- }
-
- // we only want real SCCs (size > 1)
- var last = removeLast()
- last.number = Int.MAX_VALUE
- if (last === entry) {
- return
- }
- last.isStronglyConnected = true
- while (true) {
- last = removeLast()
- assert(last.number >= entry.number)
- last.isStronglyConnected = true
- last.number = Int.MAX_VALUE
- if (last === entry) {
- assert(last.ins.size >= 2)
- return
- }
- }
-}
-
-enum class FlowAnalysisDirection {
- BACKWARD {
- override fun entries(g: JcBytecodeGraph): List