2015-07-12 28 views
1

假設有人給我下面的源的Java字節碼:如何讀取ASM中的最終字符串值?

class MyClass { 
    public static void foo() { 
    final String bar = "Hello";   
    } 
} 

我想掃描MyClass這個類的所有方法。 如果有任何方法包含finalString變量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? 
    } 
} 

但是,我無法弄清楚如何提取字面"Hello"。任何幫助,將不勝感激。

+0

你的Java例子兩者不能編譯。如果'foo'是方法,那麼它應該是'foo()'。如果'foo'是類,那麼'void'應該用'class'替換。請澄清。 –

+0

@TagirValeev我的錯誤。修正了錯字。 – Jus12

回答

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中有便捷的解析器。

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