Skip to content

Commit 3f5e88c

Browse files
authored
Merge pull request #41 from SpringKill-team/feature/enhance-callgraph
Feature/enhance callgraph
2 parents f6888b3 + 40e7c13 commit 3f5e88c

14 files changed

Lines changed: 314 additions & 329 deletions

File tree

.run/Run Plugin.run.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<option value="runIde" />
1515
</list>
1616
</option>
17-
<option name="vmOptions" value="-Xmx10240m" />
17+
<option name="vmOptions" value="-Xmx15240m" />
1818
</ExternalSystemSettings>
1919
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
2020
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>

src/main/kotlin/org/skgroup/securityinspector/actions/BuildCallGraphAction.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class BuildCallGraphAction : AnAction() {
3030
}
3131

3232
override fun update(e: AnActionEvent) {
33-
// val project = e.project
34-
// val psiElement = e.getData(CommonDataKeys.PSI_ELEMENT)
3533

3634
CoroutineScope(Dispatchers.Default).launch {
3735
val isMethod = readAction {

src/main/kotlin/org/skgroup/securityinspector/actions/SearchAsSinkAction.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class SearchAsSinkAction : AnAction() {
3030
}
3131

3232
override fun update(e: AnActionEvent) {
33-
// val project = e.project
34-
// val psiElement = e.getData(CommonDataKeys.PSI_ELEMENT)
3533

3634
CoroutineScope(Dispatchers.Default).launch {
3735

src/main/kotlin/org/skgroup/securityinspector/analysis/graphs/callgraph/CallGraph.kt

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,6 @@ data class CallGraph(
1717
val edgeTypes: MutableMap<Pair<MethodNode, MethodNode>, EdgeType> = mutableMapOf(),
1818
val nodeTypes: MutableMap<MethodNode, NodeType> = mutableMapOf()
1919
) {
20-
fun addEdge(from: MethodNode, to: MethodNode, type: EdgeType) {
21-
edges.getOrPut(from) { mutableSetOf() }.add(to)
22-
edgeTypes[Pair(from, to)] = type
23-
nodes.add(from)
24-
nodes.add(to)
25-
}
26-
27-
fun addSpecialNode(node: MethodNode, type: NodeType) {
28-
nodeTypes[node] = type
29-
}
30-
3120
fun merge(other: CallGraph) {
3221
// 合并节点
3322
this.nodes.addAll(other.nodes)
@@ -42,4 +31,8 @@ data class CallGraph(
4231
this.nodeTypes.putAll(other.nodeTypes)
4332
}
4433

34+
fun findSinks() = nodes.filter { edges[it].isNullOrEmpty() }
35+
36+
fun findRoots() = nodes.filter { it !in edges.values.flatten().toSet() }
37+
4538
}

src/main/kotlin/org/skgroup/securityinspector/analysis/graphs/callgraph/CallGraphBuilder.kt

Lines changed: 10 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import com.intellij.openapi.application.ApplicationManager
44
import com.intellij.openapi.project.Project
55
import com.intellij.psi.*
66
import com.intellij.psi.search.GlobalSearchScope
7-
import com.intellij.psi.search.searches.ClassInheritorsSearch
87
import com.intellij.psi.search.searches.ReferencesSearch
98
import com.intellij.psi.util.PsiTreeUtil
109
import org.skgroup.securityinspector.analysis.ast.nodes.MethodNode
1110
import org.skgroup.securityinspector.analysis.di.DIProcessor
1211
import org.skgroup.securityinspector.enums.RefMode
1312
import org.skgroup.securityinspector.utils.GraphUtils
14-
import java.util.ArrayDeque
13+
import org.skgroup.securityinspector.utils.PsiUtil.findConcreteImplementations
14+
import java.util.*
1515

1616
/**
1717
* Call graph builder 是一个用于构建调用图的类
@@ -32,8 +32,6 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
3232
// 用于记录当前访问的方法调用栈
3333
private val currentMethodStack = ArrayDeque<MethodNode>()
3434

35-
// private val diProcessor = DIProcessor(callGraph)
36-
3735
/**
3836
* Visit method 方法用于访问一个Java方法并将其加入调用图
3937
* 用栈处理递归方法调用
@@ -46,7 +44,6 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
4644
var calleeStackNode = GraphUtils.getMethodNode(method)
4745

4846
// 查找对该方法的所有引用,建立反向调用关系
49-
// TODO 这里的逻辑我自己也混乱了,可能会有问题,后面再看
5047
val projectScope = GlobalSearchScope.projectScope(method.project)
5148
ReferencesSearch.search(method, projectScope).forEach { reference ->
5249
val callerMethod = PsiTreeUtil.getParentOfType(reference.element, PsiMethod::class.java)
@@ -57,6 +54,14 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
5754
callGraph.nodes.add(calleeMethodNode)
5855

5956
val callerMethodNode = GraphUtils.getMethodNode(callerMethod, reference)
57+
58+
if (method.hasModifierProperty(PsiModifier.ABSTRACT) || method.containingClass?.isInterface == true) {
59+
findConcreteImplementations(method).forEach { implMethod ->
60+
val implNode = GraphUtils.getMethodNode(implMethod)
61+
callGraph.edges.getOrPut(callerMethodNode) { mutableSetOf() }.add(implNode)
62+
}
63+
}
64+
6065
// 建立反向调用关系
6166
callGraph.nodes.add(callerMethodNode)
6267
callGraph.edges.getOrPut(callerMethodNode) { mutableSetOf() }.add(calleeMethodNode)
@@ -79,7 +84,6 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
7984
*/
8085
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
8186
if (currentMethodStack.isEmpty()) return
82-
expression.methodExpression.qualifierExpression?.accept(this)
8387

8488
val caller = currentMethodStack.peek()
8589
// 解析被调用的方法
@@ -106,16 +110,6 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
106110
.add(calleeMethodNode)
107111
handleIoCContainerCall(expression, caller, calleeMethodNode)
108112
} ?: run {
109-
// if (clazz != null) {
110-
// if (clazz.isInterface) {
111-
// println("$clazz is an interface")
112-
// println("and the method call expression $expression can not resolve reference.")
113-
// }
114-
// if (clazz.hasModifierProperty(PsiModifier.ABSTRACT)) {
115-
// println("$clazz is an abstract class")
116-
// println("and the method call expression $expression can not resolve reference.")
117-
// }
118-
// }
119113
if (PsiTreeUtil.getParentOfType(expression, PsiLambdaExpression::class.java) != null) {
120114
println("in lambda method call expression $expression can not resolve reference.")
121115
} else {
@@ -139,7 +133,6 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
139133
*/
140134
override fun visitNewExpression(expression: PsiNewExpression) {
141135
if (currentMethodStack.isEmpty()) return
142-
// expression.methodNewExpression.qualifierExpression?.accept(this)
143136

144137
val callerMethodNode = currentMethodStack.peek()
145138
// 解析构造方法
@@ -160,16 +153,6 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
160153
.add(calleeMethodNode)
161154
println("expression $expression resolve reference success.")
162155
} ?: run {
163-
// if (clazz != null) {
164-
// if (clazz.isInterface) {
165-
// println("$clazz is an interface")
166-
// println("and the method call expression $expression can not resolve reference.")
167-
// }
168-
// if (clazz.hasModifierProperty(PsiModifier.ABSTRACT)) {
169-
// println("$clazz is an abstract class")
170-
// println("and the method call expression $expression can not resolve referencee.")
171-
// }
172-
// }
173156
if (PsiTreeUtil.getParentOfType(expression, PsiLambdaExpression::class.java) != null) {
174157
println("in lambda method call expression $expression can not resolve reference.")
175158
} else {
@@ -329,26 +312,4 @@ class CallGraphBuilder : JavaRecursiveElementVisitor() {
329312
return callGraph
330313
}
331314

332-
private fun findConcreteImplementations(abstractMethod: PsiMethod): List<PsiMethod> {
333-
val implementations = mutableListOf<PsiMethod>()
334-
val containingClass = abstractMethod.containingClass ?: return emptyList()
335-
336-
// 查找所有子类或实现类
337-
val inheritors = if (containingClass.isInterface) {
338-
ClassInheritorsSearch.search(containingClass, abstractMethod.useScope, true).toList()
339-
} else {
340-
ClassInheritorsSearch.search(containingClass).toList()
341-
}
342-
343-
inheritors.forEach { implClass ->
344-
implClass.findMethodBySignature(abstractMethod, true)?.let {
345-
if (!it.hasModifierProperty(PsiModifier.ABSTRACT)) {
346-
implementations.add(it)
347-
}
348-
}
349-
}
350-
351-
return implementations
352-
}
353-
354315
}

src/main/kotlin/org/skgroup/securityinspector/analysis/graphs/processor/DIProcessor.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class DIProcessor(
104104

105105
// 将 cls -> field.type 关系记录到调用图
106106
// 为了和现有的 method-based callGraph 统一,我们可以构造一个伪“类级方法节点”
107-
val callerNode = GraphUtils.getMethodNode(cls) // 伪方法节点:类本身
107+
val callerNode = GraphUtils.getClassNode(cls) // 伪方法节点:类本身
108108
val calleeNode = findOrCreateMethodNodeForType(field.type)
109109

110110
// 入图
@@ -164,8 +164,8 @@ class DIProcessor(
164164

165165
if (implCls.isInheritor(serviceClass, true)) {
166166
// 建立关联 service -> impl
167-
val serviceNode = GraphUtils.getMethodNode(serviceClass)
168-
val implNode = GraphUtils.getMethodNode(implCls)
167+
val serviceNode = GraphUtils.getClassNode(serviceClass)
168+
val implNode = GraphUtils.getClassNode(implCls)
169169

170170
callGraph.nodes.add(serviceNode)
171171
callGraph.nodes.add(implNode)
@@ -214,7 +214,7 @@ class DIProcessor(
214214
val psiClass = PsiUtil.resolveClassInType(type)
215215
return if (psiClass != null) {
216216
// 直接用 psiClass 构造 MethodNode
217-
GraphUtils.getMethodNode(psiClass)
217+
GraphUtils.getClassNode(psiClass)
218218
} else {
219219
// 找不到对应类,做一个兜底处理
220220
MethodNode(type.canonicalText ?: "UnknownType",type.canonicalText ?: "UnknownType", "DI", emptyList(), emptyList(),RefMode.CALL)

src/main/kotlin/org/skgroup/securityinspector/enums/AnalysisScope.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ enum class AnalysisScope(private val displayName: String) {
1010

1111
ENTIRE_PROJECT("Entire Project"),
1212
MODULE("Selected Module");
13-
// OPEN_FILE("Open Files");
13+
// OPEN_FILE("Open Files");
1414

1515
override fun toString(): String {
1616
return displayName
1717
}
18+
1819
}

src/main/kotlin/org/skgroup/securityinspector/ui/component/CallGraphUIComponents.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ class CallGraphUIComponents(val project: Project) {
3434
val rootListModel = DefaultListModel<MethodNode>()
3535
val rootList = JBList(rootListModel)
3636

37-
// val infoArea = JTextArea().apply { isEditable = false }
38-
val searchComboBox = ComboBox<AnalysisScope>(AnalysisScope.values())
37+
val searchComboBox = ComboBox(AnalysisScope.values())
3938

4039
val searchResultRootNode = DefaultMutableTreeNode("Search Results")
4140
val searchResultTreeModel = DefaultTreeModel(searchResultRootNode)
@@ -223,8 +222,6 @@ class CallGraphUIComponents(val project: Project) {
223222
// 初始化列表渲染器
224223
val methodRenderer = MethodListRenderer.createMethodRenderer()
225224
rootList.cellRenderer = methodRenderer
226-
// sinkList.cellRenderer = methodRenderer
227-
// searchResultList.cellRenderer = methodRenderer
228225

229226
// 创建各个功能面板
230227
val rootPanel = createTitledPanel("Root Methods", JBScrollPane(rootList))

src/main/kotlin/org/skgroup/securityinspector/ui/service/CallGraphGenerator.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ object CallGraphGenerator {
9898
ApplicationManager.getApplication().runReadAction {
9999
if (psiFile is PsiJavaFile) {
100100
if (processedFiles.add(psiFile)) {
101-
psiFile.accept(builder)
101+
PsiTreeUtil.findChildrenOfType(psiFile, PsiMethod::class.java).forEach{
102+
it.accept(builder)
103+
}
104+
// psiFile.accept(builder)
102105
}
103106
}
104107
}

0 commit comments

Comments
 (0)