编写检测器来搜索“System.out.println”的使用使用 Findbugs
我正在尝试编写一个错误检测器来使用 Findbugs 查找方法调用“System.out.println”的实例。
我知道字节码中的“System.out.println”被编译为对 GETSTATIC 的调用, 将“System.out”压入堆栈。对 INVOKEVIRTUAL 的调用会将“System.out”从堆栈中弹出并调用该方法。
我准备了一些代码(见下文),它可以找到正确的 GETSTATIC 和 INVOKEVIRTUAL 调用,但无法将两者链接在一起。我怀疑我可能需要以某种方式使用 OpcodeStack,但我无法理解如何使用它。任何帮助将不胜感激。
@Override
public void sawOpcode(int seen) {
// if opcode is getstatic
if (seen == GETSTATIC) {
String clsName = getClassConstantOperand();
if ("java/lang/System".equals(clsName)) {
String fldName = getNameConstantOperand();
if ("out".equals(fldName)) {
System.out.println("SYSTEM.OUT here");
}
}
}
// if opcode is invokevirtual
if (seen == INVOKEVIRTUAL) {
String cls = getDottedClassConstantOperand();
if ("java.io.PrintStream".equals(cls)) {
String methodName = getNameConstantOperand();
if ("println".equals(methodName)) {
bugReporter.reportBug(new BugInstance("SYSTEM_OUT_PRINTLN",
NORMAL_PRIORITY).addClassAndMethod(this)
.addSourceLine(this));
}
}
}
}
I am trying to write a bug detector to find instances of the method call "System.out.println" using Findbugs.
I understand that "System.out.println" in bytecode is compiled to a call to GETSTATIC, which pushes "System.out" onto the stack. A call to INVOKEVIRTUAL pops "System.out" off the stack and calls the method.
I have prepared some code (found below) which finds the correct GETSTATIC and INVOKEVIRTUAL calls, but have been unable to link the two together. I suspect I may need to use OpcodeStack in some way, but am having trouble in understanding how I can use it. Any help would be appreciated.
@Override
public void sawOpcode(int seen) {
// if opcode is getstatic
if (seen == GETSTATIC) {
String clsName = getClassConstantOperand();
if ("java/lang/System".equals(clsName)) {
String fldName = getNameConstantOperand();
if ("out".equals(fldName)) {
System.out.println("SYSTEM.OUT here");
}
}
}
// if opcode is invokevirtual
if (seen == INVOKEVIRTUAL) {
String cls = getDottedClassConstantOperand();
if ("java.io.PrintStream".equals(cls)) {
String methodName = getNameConstantOperand();
if ("println".equals(methodName)) {
bugReporter.reportBug(new BugInstance("SYSTEM_OUT_PRINTLN",
NORMAL_PRIORITY).addClassAndMethod(this)
.addSourceLine(this));
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我发现,对于我的用例,足以确定是否使用了 System.out 或 System.err - 在 99% 的情况下,这些将用于调用 .print 或 . println 位于块的后面。我的检测器检测到加载 System.err 或 System.out 的 GET_STATIC 操作码。下面的代码显示了确定这种情况发生的 3 种替代方法。
I found that, for my use case, it is enough to determine that System.out or System.err are used at all - in 99% of cases, these will be used for calling .print or .println later in the block. My detector detects GET_STATIC opcodes that load System.err or System.out. The code is below, showing 3 alternatives of determining that this occurs.
您的任务比看起来要复杂一些。一个简单的情况:
也被翻译成一个简单的字节码:
但是,如果您尝试打印除简单常量/已知值之外的任何内容,它会变得更难:
将被翻译成:
但是等等,它可能会变得更糟:
什么?
StringBuilder
:?有趣的是,原始代码被翻译为(这是已知的 Java 5(?) 优化):
解决方案
所以确实 - 您将需要一个堆栈,并且您必须跟踪每次推送/弹出字节码指令中定义的操作。对于这样一个简单的任务需要做很多工作......
但是如果你走这条路,解决问题非常简单:当你遇到 INVOKEVIRTUAL 时,堆栈顶部应该包含一些值,顶部下面的值应该是一个“<代码>java/lang/System.out”。
话虽这么说,我 100% 确定 Findbugs 已经实现了这一点,也许您可以使用一些 FindBugs API 来让您的生活更轻松。
Your task is a bit more complicated than it seems. A simple case:
Is translated into a simple bytecode as well:
However if you are trying to print anything except simple constant/known value it gets harder:
Will be translated to:
But wait, it can get worse:
What?
StringBuilder
: ?Interestingly, the original code was translated to (which is a known Java 5 (?) optimization):
Solution
So indeed - you will need a stack and you'll have to keep track of every push/pop operation as defined in bytecode instruction. A lot of work for such a simple task...
But if you go this path, solving the problem is quite simple: when you encounter INVOKEVIRTUAL the top of the stack should contain some value and the value below the top should be a "
java/lang/System.out
".That being said I'm 100% sure Findbugs already implemented this and probably you can use some FindBugs API to make your life easier.
使用 OpcodeStack 类。
当您看到 GETSTATIC 并意识到已“out”时,请将生成的 OpcodeStack.Item 上的用户值设置为 Boolean.TRUE。为此,请在处理 println 调用时执行此操作
,查看 tos,如果用户值设置为 Boolean.TRUE,则您知道您处于报告错误的状态。
Use the class OpcodeStack.
When you see a GETSTATIC, and you realize you have 'out', set the user value on the generated OpcodeStack.Item to Boolean.TRUE. to do this do
then when you process the println call, look at the tos, and if the user value is set to Boolean.TRUE, you know you are in the state to report the bug.
我过去提出的一种解决方案发现 System.out.println 调用中“括号内没有太多计算”,即它们之间最多有 MAX_WILDCARDS 指令。
我扩展了 ByteCodePatternDetector,我的模式如下:
稍后我确保 Load 和 Invoke 操作的字节码是正确的,并比较从
match.getBindingSet().lookup() 检索的类和字段名称。 ..)
以确保正在调用System.out
。我已经看到了现有的答案,并且我考虑更改我的解决方案。为了完整起见,我添加了这个。
One solution I came up with in the past finds
System.out.println
calls where there is "not too much calculation inside the parentheses", i.e. where a maximum of MAX_WILDCARDS instructions lie between them.I extended ByteCodePatternDetector, my pattern being the following:
I later make sure the byte codes for the Load and Invoke operation are the correct ones, and compare the class and field name I retrieve from
match.getBindingSet().lookup(...)
to make sure it isSystem.out
being called.I have seen the existing answers, and I consider changing my solution. I just added this for the sake of completeness.