验证方法体中是不是使用了方法参数
Posted
技术标签:
【中文标题】验证方法体中是不是使用了方法参数【英文标题】:Verify whether a method parameter is used in the method body验证方法体中是否使用了方法参数 【发布时间】:2018-10-28 07:55:28 【问题描述】:我的界面如下所示
interface Evaluator
boolean requiresP2();
EvalResult evaluate(Param1 p1, Param2 p2, Param3 p3);
// some more methods
这个接口由几个类实现。评估方法的参数p2
被某些人使用,而未被其他人使用。 requiresP2
方法基本上返回一个布尔值,告诉该评估方法是否使用p2
。
现在,脱离上下文,这个问题可能看起来有点奇怪,但相信我,这在我们的用例中是有意义的。此外,重构所有代码以消除对 requiresP2
方法的需求需要大量时间,因此如果我们讨论除代码库自上而下重构之外的解决方案,我将不胜感激。
问题是requiresP2
方法的返回值是基于evaluate
方法是如何实现的。所以大家在修改evaluate
方法的时候一定要保证更新requiresP2
方法。
我正在寻找方法,以便编译器/单元测试/linter 可以强制执行此操作,而不是将其留给开发人员的记忆。
编辑:我仍在探索模拟框架对这个问题的适用性。
我认为我可以在单元测试中进行反思以在单元测试中检查evaluate
的主体,以检查它是否引用p2
,然后确保它与requiresP2
方法返回的值匹配但似乎无法使用反射检查方法体。
我正在寻找有关如何执行此操作的建议。任何意见表示赞赏。
【问题讨论】:
【参考方案1】:还有一个您没有提到的选项:静态代码分析工具。
您可以使用SonarQube + SonarLint 组合来获得所需的强制执行:
使用 SonarQube 服务器创建新的静态代码分析规则,该规则将基于您正在使用的界面和您的独特用例。
然后在您的 IDE/IDE 上安装 SonarLint(Eclipse 和 IntelliJ 都支持),并将其连接到 SonarQube 服务器。
这样,静态代码分析扫描将检测到您的界面使用不当,并在 IDE 中的相关代码行(实际上是对您的代码进行 linting)上用视觉标记来指示这一点。
【讨论】:
这看起来是个不错的选择。我将探索在我们的代码中使用它的可能性。我将把这个问题留待一会儿,看看还有哪些其他选项可用。【参考方案2】:您可以使用ASM检查参数是否被使用。
使用例如将其添加到您的项目中Apache Ivy,你可以把它添加到ivy.xml
:
<dependency org="org.ow2.asm" name="asm" rev="6.1.1" />
或者对 Maven、Gradle 等进行等效操作。然后您可以通过以下方式检查参数:
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
// . . .
public static boolean usesP2(Evaluator evaluator)
AtomicBoolean usesP2 = new AtomicBoolean(false);
String internalName = evaluator.getClass().getName().replace('.', '/');
String classFileResource = "/" + internalName + ".class";
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM6)
@Override
public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions)
if ("evaluate".equals(name))
return new MethodVisitor(Opcodes.ASM6)
@Override
public void visitVarInsn(final int insn, final int slot)
if (slot == 2) usesP2.set(true);
;
return super.visitMethod(access, name, desc, signature, exceptions);
;
try (InputStream is = Evaluator.class.getResourceAsStream(classFileResource))
ClassReader reader = new ClassReader(is);
reader.accept(visitor, 0);
catch (IOException e)
throw new UncheckedIOException(e);
return usesP2.get();
public static void assertCorrectlyDocumentsP2(Evaluator evaluator)
boolean usesP2 = usesP2(evaluator);
if (usesP2 && !evaluator.requiresP2())
throw new AssertionError(evaluator.getClass().getName() +
" uses P2 without documenting it");
if (!usesP2 && evaluator.requiresP2())
throw new AssertionError(evaluator.getClass().getName() +
" says it uses P2 but does not");
单元测试:
@Test
public void testFalsePositive()
assertCorrectlyDocumentsP2(new FalsePositive());
@Test
public static void testFalseNegative()
assertCorrectlyDocumentsP2(new FalseNegative());
(假设有两个错误的Evaluator
s、FalsePositive
和FalseNegative
,其中一个记录了它使用 P2 但没有,另一个没有记录它使用 P2,即使分别是。)
注意:在usesP2
中,我们在堆栈帧的插槽 2 中检查变量指令(访问局部变量的指令)。插槽从 0 开始编号,第一个是 this
。 P2 位于插槽 2 中只是因为Evaluator::evaluate
是一个实例方法。如果它是一个静态方法,我们必须检查是否使用了slot 1,以检测是否使用了参数P2。 告诫者。
【讨论】:
接受这个答案,因为这是与我们的设置集成的最简单方法。感谢您的有用回答,它解决了我的问题。 有趣的展示案例。以前从未听说过 ASM。感谢您的回答! 不应该检查slot == 1
而不是slot == 2
吗?
@Lakshay 阅读最后一段。
注意如果你的类包含重载方法,那么你必须使用desc
参数匹配完整的方法签名,因为"evaluate".equals(name)
是不够的以上是关于验证方法体中是不是使用了方法参数的主要内容,如果未能解决你的问题,请参考以下文章