2012-12-18 24 views
11

我使用ASM和要重寫類似:注入Java方法_before_另一種方法被稱爲

someMethod().targetMethod(args...) 

到:

someMethod().injectedMethod(arg).targetMethod(args...) 

麻煩的是,我不知道是什麼之前的方法,我只知道目標方法(所以找到someMethod()並注入後不是一個選項)。

我也有很多版本的目標方法,用不同的參數設置我希望這個工作。

使用ASM我可以很容易地找到目標方法調用,但不幸的是,在這一點上,操作數堆棧是:

[ argN, ..., arg1, instance, ... ] 

雖然我可以計算出多遠實例的情況下將是,有沒有字節碼我可以注入將讀取它。我知道你可以使用dup命令做多達4個參數,但我需要一個通用的解決方案。

我可以添加一堆局部變量並將所有內容從堆棧中拷貝出來,重複指向的實例並將所有內容都放回,但這是運行時效率低下,我真的不想要。

我認爲會工作的是,如果我可以跟蹤哪些指令負責將實例指針放在堆棧上,然後我可以在那裏注入我的方法調用,而不是在目標方法調用。然而,我沒有找到任何幫助我做這件事的運氣。

我知道像AspectJ這樣的東西允許這樣做,但是在加載時必須爲很多類執行此操作,而AspectJ太慢了。

任何人都可以指出建立在ASM之上的分析工具,它可以讓我這麼做,或者任何人都可以想到一種更好的方法來在另一種方法之前注入一種方法嗎?

+1

我不明白,你真的需要使用ASM嗎?因爲它好像是在試圖破解現有的Jar。對於那將是更好的反編譯.........如果不是,並且你正在寫一個Java應用程序,你不能使用agregation呢? – fredcrs

+0

我無法反編譯這些類,因爲我只能在類加載時將它作爲javaagent掛鉤。我既不'竊聽現有的jar'或'寫一個Java應用程序';我將字節碼注入類中,因爲它們被加載。 – David

+0

我在學校時曾與[Soot](http://www.sable.mcgill.ca/soot/)合作,並且完全按照這種方式做了一些東西(好的,也許不那麼複雜)。看一看。不幸的是,我不知道它是否構建在ASM之上。想到的另一個選項是AspectJ。 – arin

回答

1

在一般情況下,您可以將堆棧中的值卸載到臨時局部變量中。來自ASM commons軟件包的LocalVariableSorter適配器使其變得非常簡單。但實際上,具有超過4個參數的方法是一種罕見的情況。無論如何,它仍然更簡單,更堅實,然後在運行時進行全面的數據流分析。

ASM的org.objectweb.asm.tree.analysis爲數據流分析提供了便利,例如,您可以使用SourceInterpreter來跟蹤哪些指令在每個變量和堆棧插槽上生成值。有關更多詳情,請參閱ASM User Guide

+0

運行時全面的數據分析意味着什麼?你會怎麼做? – vijay

+1

我已經添加了一些說明並直接鏈接到文檔。 –

2

如果我正確理解你的問題,我已經達到了與你想要做的相同的目標,但是以不同的方式。

使用ASM事件驅動的字節碼修改,我首先將someMethod(arg,arg,arg)重命名爲copyOf_someMethod(arg,arg,arg)。然後我創建了一個名爲someMethod(arg,arg,arg)的新方法,它執行了一些處理,然後調用copyOf_someMethod(arg,arg,arg)。

我做的方法,我實現了ClassVisitor的visitMethod(..)方法重命名:

MethodVisitor methodVisitor = 
    super.visitMethod(
     methodAccess, "copyOf_" + methodName, methodDesc, 
      methodSignature, methodExceptions); 

return methodVisitor; 

在visitMethod(..)我還保存所有類變量準備的方法簽名的詳細信息在visitEnd()方法中使用。

我實際存儲的方法的細節在MethodDetail對象並將其放置在隊列中:使用所述的visitEnd()方法

private Queue<MethodDetail> methodDetails = new LinkedList<MethodDetail>(); 

我所創建的新的實現的someMethod的(ARG,ARG,ARG)我實施了ClassVisitor。我使用ASMFier來生成放入visitEnd()方法的代碼。這個實現使用了我以前在visitMethod(..)中存儲的細節。新的實現做了一些處理,然後調用copyOf_someMethod()。在visitEnd()方法中,我彈出了隊列中所有的MethodDetail以及我之前由ASMFier生成的每個MethodDetail調用的ASM代碼。

使用此設計我創建了一個方法的代理,它執行了一些處理,然後調用原始方法。請注意,原始方法已重命名爲copyOf_someMethod(..)。還要注意的是,我提供了一個作爲代理的原始方法的新實現。

爲了支持多個參數,我使用ASMFier爲1個參數,2個參數,3個參數,e.t.c生成不同的代碼。如果被代理的方法有7個以上的參數,我最多支持7個參數並拋出Unsupported Exception。在visitEnd(..)方法中,我調用了不同的代碼(由ASMFier生成),具體取決於原始方法有多少個方法參數。

我用一個javaagent來攔截類的加載和修改字節。

由於我是ASM的新手,可能我沒有正確理解你的問題 - 但是如果你的問題是關於創建一個代理來做一些處理,然後調用原始方法,那麼我的解決方案就可以工作。它似乎並不慢。其代理方法的類的類加載時間並沒有比沒有字節代碼修改那麼慢。引入代碼的運行速度並不慢,由ASMFier生成的ASM代碼看起來非常快。

乾杯

0

查找到org.objectweb.asm.tree.analysis。 SourceIterpreter應該爲您提供將值放入堆棧的說明。