假设某人给了我以下源代码的Java字节码:
class MyClass {
public static void foo() {
final String bar = "Hello";
}
}
我想扫描此课程MyClass
中的所有方法。
如果任何方法包含名为final
的{{1}} String
变量,我需要输出变量的文字值。在这种情况下,bar
。
我设法在方法中找到名为Hello
的变量,如下所示:
bar
但是,我无法弄清楚如何提取文字// Scala code
import scala.collection.JavaConversions._
import org.objectweb.asm._
import org.objectweb.asm.tree._
def processClass(is:java.io.InputStream) = {
val cn = new ClassNode
val cr = new ClassReader(is)
cr.accept(cn, 0)
is.close
val methods = cn.methods.asInstanceOf[java.util.List[MethodNode]]
val m = methods(0) // get first method as an example
val vars = m.localVariables.asInstanceOf[java.util.List[LocalVariableNode]];
val bar = vars.find(_.name == "bar").find(v => Type.getType(v.desc) == Type.getType(classOf[String]))
if (bar.isDefined) {
// how to read value of final variable "bar"?
// also how to check for final?
}
}
。任何帮助将不胜感激。
答案 0 :(得分:7)
您无法检查final
,因为对于局部变量,它根本不存储在类文件中,因此ASM无法提取它。您只能检查变量是否已分配一次或多次,查找astore*
字节码指令。但是,即使是最终变量也可以在字节码中分配几次。例如,这是有效的Java代码:
public static void foo(boolean flag) {
final String bar;
if (flag)
bar = "Hello";
else
bar = "Goodbye";
}
在这种情况下你想打印什么?
此外,赋值可以是这样的表达式的结果:
public static void foo(String name) {
final String bar = "Hello "+name;
}
在这种情况下你想打印什么?
如果不确切地知道你想要达到什么目标,很难帮助你。假设您要跟踪变量的字符串的简单赋值,您应该访问方法字节码并查找ldc #x/astore*
之类的序列。从ldc参数#x你can understand加载常量。从astore操作码或参数can understand您将结果保存到哪个变量槽。之后,您应该查阅本地变量表(您已经知道如何操作)以了解此槽在给定代码位置中对应的变量名称(请注意,变量槽可以重用于不同的变量)。
如果确实需要跟踪局部变量上的final
关键字,则必须解析源Java文件。有一些方便的解析器,例如Eclipse JDT。
答案 1 :(得分:0)
感谢Tagir Valeev的见解。我想要第一个astore指令。这段代码符合我的要求:
// Scala code
import scala.collection.JavaConversions._
import org.objectweb.asm._
import org.objectweb.asm.tree._
import org.objectweb.asm.util.Textifier
import org.objectweb.asm.util.TraceMethodVisitor
def processClass(is:java.io.InputStream) = {
val cn = new ClassNode
val cr = new ClassReader(is)
cr.accept(cn, 0)
is.close
val methods = cn.methods.asInstanceOf[java.util.List[MethodNode]]
val m = methods(0) // get first method as an example
val vars = m.localVariables.asInstanceOf[java.util.List[LocalVariableNode]];
val bar = vars.find(_.name == "bar").find(v => Type.getType(v.desc) == Type.getType(classOf[String]))
if (bar.isDefined) {
val str = insnToString(bar.get.start.getPrevious.getPrevious).trim
println("found text: "+(if (str.startsWith("LDC")) str.substring(5).init else "None")) // prints Hello
}
def insnToString(insn:AbstractInsnNode) = {
val printer = new Textifier;
val mp = new TraceMethodVisitor(printer);
insn.accept(mp);
val sw = new StringWriter;
printer.print(new PrintWriter(sw));
printer.getText.clear;
sw.toString;
}
}