如何在ASM中读取最终字符串值?

时间:2015-07-12 11:04:06

标签: java scala bytecode java-bytecode-asm

假设某人给了我以下源代码的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? } } 。任何帮助将不胜感激。

2 个答案:

答案 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指令。这段代码符合我的要求:

取自this SO answer

// 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;          
  }
}