All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jacodb.analysis.graph.SimplifiedJcApplicationGraph.kt Maven / Gradle / Ivy

/*
 *  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.JcClassType import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph import org.jacodb.api.cfg.JcExpr import org.jacodb.api.cfg.JcInst import org.jacodb.api.cfg.JcInstLocation import org.jacodb.api.cfg.JcInstVisitor import org.jacodb.api.cfg.JcVirtualCallExpr import org.jacodb.api.ext.cfg.callExpr import org.jacodb.api.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 callsites 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 impl: JcApplicationGraphImpl, private val bannedPackagePrefixes: List, ) : JcApplicationGraph by impl { private val hierarchyExtension = runBlocking { classpath.hierarchyExt() } private val visitedCallers: MutableMap> = mutableMapOf() private val cache: MutableMap> = mutableMapOf() private fun getOverrides(method: JcMethod): List { return if (cache.containsKey(method)) { cache[method]!! } else { val res = hierarchyExtension.findOverrides(method).toList() cache[method] = res res } } // 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 methodEntryLineNumber = method.flowGraph().entries.firstOrNull()?.lineNumber return JcNoopInst(JcInstLocationImpl(method, -1, methodEntryLineNumber?.let { it - 1 } ?: -1)) } override fun predecessors(node: JcInst): Sequence { val method = methodOf(node) return if (node == getStartInst(method)) { emptySequence() } else { if (node in impl.entryPoint(method)) { sequenceOf(getStartInst(method)) } else { impl.predecessors(node) } } } override fun successors(node: JcInst): Sequence { val method = methodOf(node) return if (node == getStartInst(method)) { impl.entryPoint(method) } else { impl.successors(node) } } private fun calleesUnmarked(node: JcInst): Sequence { val callees = impl.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 { visitedCallers.getOrPut(it) { 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.getOrDefault(method, mutableSetOf()).asSequence() override fun entryPoint(method: JcMethod): Sequence = sequenceOf(getStartInst(method)) companion object { } } 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" }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy