2015-07-12 2 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; 
} 

Что вы хотите напечатать в этом случае?

Трудно вам помочь, не зная точно, чего вы пытаетесь достичь. Предполагая, что вы хотите отслеживать простые назначения строк для переменных, вы должны посетить метод bytecode и искать последовательности, такие как ldC#x/astore*. Из параметра ldC#x вы загружаете can understand. Из кода операции или параметра astore вы получаете can understand, в какой переменный слот вы сохраняете результат. После этого вы должны проконсультироваться с локальной таблицей varaible (которую вы уже знаете, как это сделать), чтобы узнать, какое имя переменной соответствует этому слоту в заданной позиции кода (обратите внимание, что слот переменной может использоваться повторно для разных переменных).

Если вам действительно нужно отследить ключевое слово final на локальных переменных, вам нужно проанализировать исходный файл Java. Есть готовые удобные парсеры, например, в Eclipse JDT.

0

Спасибо Тагиру Валееву за понимание. Мне нужна была первая инструкция. Этот код делает то, что я хочу:

Взятые из 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;   
    } 
}