diff --git a/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/analysis/ApplicationGraph.kt b/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/analysis/ApplicationGraph.kt
deleted file mode 100644
index cca5cf1ce..000000000
--- a/jacodb-api-common/src/main/kotlin/org/jacodb/api/common/analysis/ApplicationGraph.kt
+++ /dev/null
@@ -1,40 +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.api.common.analysis
-
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.CommonProject
-import org.jacodb.api.common.cfg.CommonInst
-
-/**
- * Provides both CFG and call graph (i.e., the supergraph in terms of RHS95 paper).
- */
-interface ApplicationGraph
- where Method : CommonMethod,
- Statement : CommonInst {
-
- fun predecessors(node: Statement): Sequence
- fun successors(node: Statement): Sequence
-
- fun callees(node: Statement): Sequence
- fun callers(method: Method): Sequence
-
- fun entryPoints(method: Method): Sequence
- fun exitPoints(method: Method): Sequence
-
- fun methodOf(node: Statement): Method
-}
diff --git a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/analysis/JcApplicationGraph.kt b/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/analysis/JcApplicationGraph.kt
deleted file mode 100644
index a251cb06b..000000000
--- a/jacodb-api-jvm/src/main/kotlin/org/jacodb/api/jvm/analysis/JcApplicationGraph.kt
+++ /dev/null
@@ -1,29 +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.api.jvm.analysis
-
-import org.jacodb.api.common.analysis.ApplicationGraph
-import org.jacodb.api.jvm.JcClasspath
-import org.jacodb.api.jvm.JcMethod
-import org.jacodb.api.jvm.cfg.JcInst
-
-/**
- * Interface for [JcApplicationGraph] built with jacodb.
- */
-interface JcApplicationGraph : ApplicationGraph {
- val cp: JcClasspath
-}
diff --git a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsApplicationGraph.kt b/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsApplicationGraph.kt
deleted file mode 100644
index 071fe78bb..000000000
--- a/jacodb-ets/src/main/kotlin/org/jacodb/ets/graph/EtsApplicationGraph.kt
+++ /dev/null
@@ -1,249 +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.ets.graph
-
-import mu.KotlinLogging
-import org.jacodb.api.common.analysis.ApplicationGraph
-import org.jacodb.ets.base.CONSTRUCTOR_NAME
-import org.jacodb.ets.base.EtsStmt
-import org.jacodb.ets.base.UNKNOWN_FILE_NAME
-import org.jacodb.ets.model.EtsClass
-import org.jacodb.ets.model.EtsClassSignature
-import org.jacodb.ets.model.EtsFileSignature
-import org.jacodb.ets.model.EtsMethod
-import org.jacodb.ets.model.EtsMethodSignature
-import org.jacodb.ets.model.EtsScene
-import org.jacodb.ets.utils.Maybe
-import org.jacodb.ets.utils.callExpr
-
-private val logger = KotlinLogging.logger {}
-
-interface EtsApplicationGraph : ApplicationGraph {
- val cp: EtsScene
-}
-
-private fun EtsFileSignature?.isUnknown(): Boolean =
- this == null || fileName.isBlank() || fileName == UNKNOWN_FILE_NAME
-
-private fun EtsClassSignature.isUnknown(): Boolean =
- name.isBlank()
-
-private fun EtsClassSignature.isIdeal(): Boolean =
- !isUnknown() && !file.isUnknown()
-
-enum class ComparisonResult {
- Equal,
- NotEqual,
- Unknown,
-}
-
-fun compareFileSignatures(
- sig1: EtsFileSignature?,
- sig2: EtsFileSignature?,
-): ComparisonResult = when {
- sig1.isUnknown() -> ComparisonResult.Unknown
- sig2.isUnknown() -> ComparisonResult.Unknown
- sig1?.fileName == sig2?.fileName -> ComparisonResult.Equal
- else -> ComparisonResult.NotEqual
-}
-
-fun compareClassSignatures(
- sig1: EtsClassSignature,
- sig2: EtsClassSignature,
-): ComparisonResult = when {
- sig1.isUnknown() -> ComparisonResult.Unknown
- sig2.isUnknown() -> ComparisonResult.Unknown
- sig1.name == sig2.name -> compareFileSignatures(sig1.file, sig2.file)
- else -> ComparisonResult.NotEqual
-}
-
-class EtsApplicationGraphImpl(
- override val cp: EtsScene,
-) : EtsApplicationGraph {
-
- override fun predecessors(node: EtsStmt): Sequence {
- val graph = node.method.flowGraph()
- val predecessors = graph.predecessors(node)
- return predecessors.asSequence()
- }
-
- override fun successors(node: EtsStmt): Sequence {
- val graph = node.method.flowGraph()
- val successors = graph.successors(node)
- return successors.asSequence()
- }
-
- private val cacheClassWithIdealSignature: MutableMap> = hashMapOf()
- private val cacheMethodWithIdealSignature: MutableMap> = hashMapOf()
- private val cachePartiallyMatchedCallees: MutableMap> = hashMapOf()
-
- private fun lookupClassWithIdealSignature(signature: EtsClassSignature): Maybe {
- require(signature.isIdeal())
-
- if (signature in cacheClassWithIdealSignature) {
- return cacheClassWithIdealSignature.getValue(signature)
- }
-
- val matched = cp.projectAndSdkClasses
- .asSequence()
- .filter { it.signature == signature }
- .toList()
- if (matched.isEmpty()) {
- cacheClassWithIdealSignature[signature] = Maybe.none()
- return Maybe.none()
- } else {
- val s = matched.singleOrNull()
- ?: error("Multiple classes with the same signature: $matched")
- cacheClassWithIdealSignature[signature] = Maybe.some(s)
- return Maybe.some(s)
- }
- }
-
- override fun callees(node: EtsStmt): Sequence {
- val expr = node.callExpr ?: return emptySequence()
-
- val callee = expr.method
-
- // Note: the resolving code below expects that at least the current method signature is known.
- check(node.method.enclosingClass.isIdeal()) {
- "Incomplete signature in method: ${node.method}"
- }
-
- // Note: specific resolve for constructor:
- if (callee.name == CONSTRUCTOR_NAME) {
- if (!callee.enclosingClass.isIdeal()) {
- // Constructor signature is garbage. Sorry, can't do anything in such case.
- return emptySequence()
- }
-
- // Here, we assume that the constructor signature is ideal.
- check(callee.enclosingClass.isIdeal())
-
- val cls = lookupClassWithIdealSignature(callee.enclosingClass)
- if (cls.isSome) {
- return sequenceOf(cls.getOrThrow().ctor)
- } else {
- return emptySequence()
- }
- }
-
- // If the callee signature is ideal, resolve it directly:
- if (callee.enclosingClass.isIdeal()) {
- if (callee in cacheMethodWithIdealSignature) {
- val resolved = cacheMethodWithIdealSignature.getValue(callee)
- if (resolved.isSome) {
- return sequenceOf(resolved.getOrThrow())
- } else {
- return emptySequence()
- }
- }
-
- val cls = lookupClassWithIdealSignature(callee.enclosingClass)
-
- val resolved = run {
- if (cls.isNone) {
- emptySequence()
- } else {
- cls.getOrThrow().methods.asSequence().filter { it.name == callee.name }
- }
- }
- if (resolved.none()) {
- cacheMethodWithIdealSignature[callee] = Maybe.none()
- return emptySequence()
- }
- val r = resolved.singleOrNull()
- ?: error("Multiple methods with the same complete signature: ${resolved.toList()}")
- cacheMethodWithIdealSignature[callee] = Maybe.some(r)
- return sequenceOf(r)
- }
-
- // If the callee signature is not ideal, resolve it via a partial match...
- check(!callee.enclosingClass.isIdeal())
-
- val cls = lookupClassWithIdealSignature(node.method.enclosingClass).let {
- if (it.isNone) {
- error("Could not find the enclosing class: ${node.method.enclosingClass}")
- }
- it.getOrThrow()
- }
-
- // If the complete signature match failed,
- // try to find the unique not-the-same neighbour method in the same class:
- val neighbors = cls.methods
- .asSequence()
- .filter { it.name == callee.name }
- .filterNot { it.name == node.method.name }
- .toList()
- if (neighbors.isNotEmpty()) {
- val s = neighbors.singleOrNull()
- ?: error("Multiple methods with the same name: $neighbors")
- cachePartiallyMatchedCallees[callee] = listOf(s)
- return sequenceOf(s)
- }
-
- // NOTE: cache lookup MUST be performed AFTER trying to match the neighbour!
- if (callee in cachePartiallyMatchedCallees) {
- return cachePartiallyMatchedCallees.getValue(callee).asSequence()
- }
-
- // If the neighbour match failed,
- // try to *uniquely* resolve the callee via a partial signature match:
- val resolved = cp.projectAndSdkClasses
- .asSequence()
- .filter { compareClassSignatures(it.signature, callee.enclosingClass) != ComparisonResult.NotEqual }
- // Note: exclude current class:
- .filterNot { compareClassSignatures(it.signature, node.method.enclosingClass) != ComparisonResult.NotEqual }
- // Note: omit constructors!
- .flatMap { it.methods.asSequence() }
- .filter { it.name == callee.name }
- .toList()
- if (resolved.isEmpty()) {
- cachePartiallyMatchedCallees[callee] = emptyList()
- return emptySequence()
- }
- val r = resolved.singleOrNull() ?: run {
- logger.warn { "Multiple methods with the same partial signature '${callee}': $resolved" }
- cachePartiallyMatchedCallees[callee] = emptyList()
- return emptySequence()
- }
- cachePartiallyMatchedCallees[callee] = listOf(r)
- return sequenceOf(r)
- }
-
- override fun callers(method: EtsMethod): Sequence {
- // Note: currently, nobody uses `callers`, so if is safe to disable it for now.
- // Note: comparing methods by signature may be incorrect, and comparing only by name fails for constructors.
- TODO("disabled for now, need re-design")
- // return cp.classes.asSequence()
- // .flatMap { it.methods }
- // .flatMap { it.cfg.instructions }
- // .filterIsInstance()
- // .filter { it.expr.method == method.signature }
- }
-
- override fun entryPoints(method: EtsMethod): Sequence {
- return method.flowGraph().entries.asSequence()
- }
-
- override fun exitPoints(method: EtsMethod): Sequence {
- return method.flowGraph().exits.asSequence()
- }
-
- override fun methodOf(node: EtsStmt): EtsMethod {
- return node.location.method
- }
-}