Page 133 - 《软件学报》2025年第7期
P. 133
3054 软件学报 2025 年第 36 卷第 7 期
记录相应的终点 target (第 4–5 行). 由于调用边 edge 的类型对应于从 source 到 target 的具体场景, 算法将 edge 的
类型 edge.type 添加至 target 对应的预期使用场景 expectedScenesMap[target] (第 6 行). 然后, 算法对依赖树 DT 中
深度为 1 的依赖库 (即直接依赖库)dep 进行遍历, 并提取模块中声明的 dep 的实际依赖范围 declaredScope (第
8–9 行). 随后, 利用 getCorrespondingScenes 函数获取依赖范围 declaredScope 对应的使用场景 actualScenes (第
10 行), 并从 expectedScenesMap 中取出预期依赖范围 expectedScenes (第 11 行). 通过比较依赖范围对应的使用场
景 actualScenes 与预期使用场景 expectedScenes, 若存在不一致情况, 则将依赖库 dep 加入集合 JS 中 (第 12–13 行).
算法 1. 依赖范围误用检测.
输入: DT: 依赖树; CG: 调用图;
输出: JS: 出现依赖范围误用的依赖库集合.
1. JS ⇐ {};
2. expectedScenesMap ⇐ {};
3. source ⇐ CG.source;
4. for edge ∈ CG and edge.from = source do
5. target ⇐ edge.to;
6. expectedScenesMap[target].add(edge.type);
7. end for
8. for dep ∈ DT and dep.depth = 1 do
9. declaredScope ⇐ dep.scope;
10. actualScenes ⇐ getCorrespondingScenes(declaredScope);
11. expectedScenes ⇐ expectedScensMap[target];
12. if expectedScenes != null and actualScenes != expectedScenesMap then
13. JS.add(dep);
14. end if
15. end for
总的来说, 此算法通过分析调用图中不同类型的调用边来确定依赖库的预期使用场景, 并将其与依赖库的实
际依赖范围对应的使用场景进行比较, 输出所有不一致的依赖库. 以图 6 为例, Maven 项目中使用了依赖库
org.projectlombok:lombok, 并将其依赖范围设置为 compile. 依赖库 lombok [44] 是一个能够通过在源代码中简单注解
自动生成样板代码的依赖库. 由于它在构建时生成代码, 而不会出现在字节码中, 因此其依赖范围应该设置为
provided, 然而实际上却设置为 compile, 这就引发了依赖范围误用. 根据我们的检测算法, 由于 lombok 的类不会出
现在字节码中, 因此调用图中不会存在指向 lombok 的运行调用边. 因此, 预期使用场景不包括运行时. 然而, 实际
依赖范围为 compile 的使用场景却包括了运行时, 这就导致了不一致. 因此, 检测算法会将 lombok 标记为出现依
赖范围误用的依赖库, 从而成功进行检测.
依赖于 相关配置 <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
模块 mod 依赖库 lombok <version>1.18.12</version>
<scope>compile</scope>
包含代码 </dependency>
mod.LombokUsage
import lombok.ToString;
@ToString
public class LombokUsage { 预期范围: provided 冲突! 实际范围: compile
…
}
图 6 出现依赖范围误用的案例

