ASMSupport教程4:生成經常使用操做

<h2>4.1前言</h2> <p>在教程開始以前首先簡單介紹下生成操做的字節碼命令的原理。咱們知道在java代碼中咱們最基本的運算就是操做,好比四則運算,方法調用等好比一下代碼:</p> <p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:6acf66cb-c7b1-4ebf-a62a-71bee92ea9b8" class="wlWriterEditableSmartContent"><pre class="brush: java; gutter: true; first-line: 1; tab-size: 4; toolbar: true; width: 400px; height: 90px;" style=" width: 400px; height: 90px;overflow: auto;">String a = &quot;str&quot;; int i = 1 + 2; i++; System.out.println(a + i);</pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div> </p>java

<p>這裏存在6種操做,讓咱們一句一句解析出操做具體以下:</p>this

<p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:2a486121-5943-4adf-9b91-b88e85c7d511" class="wlWriterEditableSmartContent"><pre class="brush: java; gutter: false; first-line: 1; tab-size: 4; toolbar: true; width: 400px; height: 174px;" style=" width: 400px; height: 174px;overflow: auto;">1.String a = &quot;str&quot;; = : 賦值 2.int i = 1 + 2; + : 算數運算操做 = : 賦值 3.i++; ++ : 增量操做 4 System.out.println(a + i); _第二個&quot;.&quot; : 方法調用 + : 字符串拼接操做 </pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div> </p>spa

<p>既然是操做就有操做因子,什麼是操做因子?見下表:</p>3d

<table border="1" cellspacing="2" cellpadding="5" width="354"><tbody> <tr> <td valign="top" width="201">代碼</td>code

<td valign="top" width="151">操做因子</td>
</tr>

<tr>
  <td valign="top" width="201">String a = &quot;str&quot;;</td>

  <td valign="top" width="151">&quot;str&quot;</td>
</tr>

<tr>
  <td valign="top" width="201">int i = 1 + 2;</td>

  <td valign="top" width="151">1, 2, 1+2</td>
</tr>

<tr>
  <td valign="top" width="201">i++;</td>

  <td valign="top" width="151">i</td>
</tr>

<tr>
  <td valign="top" width="201">System.out.println(a + i)</td>

  <td valign="top" width="151">a, i, a + 1</td>
</tr>

</tbody></table>orm

<p>總共存在10種操做,具體以下(這裏只列舉每種操做的其中一個,並不所有列舉):</p>對象

<p>1. String a = &quot;str&quot;; <br />&#160;&#160; = : 賦值</p>繼承

<p>2. i++; <br />&#160;&#160; ++ : 增量操做</p>教程

<p>3. int i = 1 + 2; <br />&#160;&#160; + : 算數運算操做接口

<br />   = : 賦值</p>

<p>4. 9&amp;7 <br />&#160;&#160; &amp; :位操做</p>

<p>5. a &gt; 1 <br />&#160;&#160; &gt; : 關係運算操做</p>

<p>6. true &amp;&amp; false <br />&#160;&#160; &amp;&amp; : 邏輯運算操做

<br />  <br />7. k = i < 0 ? -i : i

<br />   _三元操做</p>

<p>8. a instanceof String <br />&#160;&#160; instanceof : instanceof操做

<br />   <br />9. "Hello " + "asmsupport!"

<br />   + : 字符串拼接操做

<br />   <br />10. System.out.println(a + i);

<br />   _第二個"." : 方法調用操做</p>

<p>在咱們的程序中咱們將全部的操做都經過IBlockOperators中的方法調用實現。而全部的操做因子是通過方法參數傳入的。而且有的操做一樣能做爲其餘操做的操做因子,如上面的1+2就是做爲賦值操做&quot;=&quot;的操做因子。在asmsupport中只要是繼承了jw.asmsupport.Parameterized接口的均可以是做爲操做因子。其實在使用asmsupport的時候大部分只要在java代碼中能做爲操做因子的在asmsupport中一樣也能做爲操做因子。由於asmsupport保留了java代碼編寫的方式。這裏是在使用ASMSupport的角度上去介紹的。</p>

<p>這裏將簡單的介紹這些操做在底層是如何實現的。在ASMSupport中咱們將java操做都抽象成爲類AbstractOperator.class,每一個具體的操做都是繼承自它,好比instanceof操做咱們將其抽象成jw.asmsupport.operators.InstanceofOperator.AbstractOperator同時也繼承了jw.asmsupport.Executeable接口,這個接口有兩個方法。</p>

<ul> <li>prepare()</li>

<li>execute()</li> </ul>

<p>每個操做都至關於一個Executeable接口。ASMSupport會用一個List按照咱們指望生成的代碼的順序將每一個對應的Executeable存入到這個List中,咱們稱之爲執行隊列(執行隊列中咱們還會存儲程序塊jw.asmsupport.block.ProgramBlock),在咱們將全部的操做都存入到執行隊列以後先遍歷全部操做,對全部操做執行prepare方法,而後再一次遍歷執行隊列,對每一個元素執行execute方法。prepare方法的做用是整合和從新排列執行隊列,execute方法是生成字節碼指令。好比咱們想要生成以下代碼:</p>

<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:dcc6ca3d-1a94-4df7-a69b-50b4bcc2fe79" class="wlWriterEditableSmartContent"><pre class="brush: java; gutter: true; first-line: 1; tab-size: 4; toolbar: true; width: 400px; height: 45px;" style=" width: 400px; height: 45px;overflow: auto;">int value = 100 + 10; System.out.println(value + 1);</pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div>

<p>經過ASMSupport生成這段的代碼以下:</p>

<p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:08316c9b-aa63-4e9a-b525-29699b2d450d" class="wlWriterEditableSmartContent"><pre class="brush: java; gutter: true; first-line: 1; tab-size: 4; toolbar: true; width: 964px; height: 55px;" style=" width: 964px; height: 55px;overflow: auto;">LocalVariable value = createVariable(&quot;value&quot;, AClass.INT_ACLASS, false, this.add(Value.value(100), Value.value(10))); invoke(AClassFactory.getProductClass(System.class).getGlobalVariable(&quot;out&quot;), &quot;println&quot;, add(value, Value.value(1)));</pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com --></div> </p>

<p>咱們能夠看到咱們分別將100,10,1經過Value.value()方法生成對應jw.asmsupport.definition.value.Value對象。而add方法則返回jw.asmsupport.operators.numerical.arithmetic.Addition對象,createVariable方法返回jw.asmsupport.definition.variable.LocalVariable對象,invoke方法返回jw.asmsupport.operators.method.MethodInvoker。我用列表的形式列出全部對象建立的順序,順序是最上面的最早建立。</p>

<table border="1" cellspacing="0" cellpadding="2" width="977"><tbody> <tr> <td valign="top" width="80">序號</td>

<td valign="top" width="254">ASMSupport代碼</td>

  <td valign="top" width="116">返回結果類型</td>

  <td valign="top" width="525">對應java代碼</td>
</tr>

<tr>
  <td valign="top" width="80">1</td>

  <td valign="top" width="254">Value.value(100)</td>

  <td valign="top" width="116">jw.asmsupport.definition.value.Value</td>

  <td valign="top" width="525">數值100</td>
</tr>

<tr>
  <td valign="top" width="80">2</td>

  <td valign="top" width="254">Value.value(10)</td>

  <td valign="top" width="116">jw.asmsupport.definition.value.Value</td>

  <td valign="top" width="525">數值10</td>
</tr>

<tr>
  <td valign="top" width="80">3</td>

  <td valign="top" width="254">this.add()</td>

  <td valign="top" width="116">jw.asmsupport.operators.numerical.arithmetic.Addition</td>

  <td valign="top" width="525">加法操做(100+10)</td>
</tr>

<tr>
  <td valign="top" width="80">4</td>

  <td valign="top" width="254">createVariable()</td>

  <td valign="top" width="116">jw.asmsupport.definition.variable.LocalVariable</td>

  <td valign="top" width="525">建立變量,而且將上面的加法操做的結果存入到這個變量value。這裏雖然返回的是LocalVariable,可是執行這段代碼的時候在內部依然會建立一個jw.asmsupport.operators.variable.LocalVariableCreator對象和jw.asmsupport.operators.assign.Assigner對象,前者的做用是建立一個變量,後者的做用是在JVM層面將變量註冊到方法的局部變量中。</td>
</tr>

<tr>
  <td valign="top" width="80">5</td>

  <td valign="top" width="254">...getGlobalVariable(&quot;out&quot;)</td>

  <td valign="top" width="116">jw.asmsupport.definition.variable.GlobalVariable</td>

  <td valign="top" width="525">獲取System.out</td>
</tr>

<tr>
  <td valign="top" width="80">6</td>

  <td valign="top" width="254">Value.value(1)</td>

  <td valign="top" width="116">jw.asmsupport.definition.value.Value</td>

  <td valign="top" width="525">數值1</td>
</tr>

<tr>
  <td valign="top" width="80">7</td>

  <td valign="top" width="254">add(value, Value.value(1))</td>

  <td valign="top" width="116">jw.asmsupport.operators.numerical.arithmetic.Addition</td>

  <td valign="top" width="525">加法操做:value+1</td>
</tr>

<tr>
  <td valign="top" width="80">8</td>

  <td valign="top" width="254">invoke(...)</td>

  <td valign="top" width="116">jw.asmsupport.operators.method.MethodInvoker</td>

  <td valign="top" width="525">方法調用操做,第一個參數是方法的擁有者,方法名爲第二參數,剩下的參數表示所要調用的方法的參數,這裏表示System.out.println(value + 1)</td>
</tr>

</tbody></table>

<p>當按照上面的順序執行完以後,ASMSupport會用一個執行隊列(List&lt;Executeable&gt;類型)保存上面所建立出來的Executeable類型的對象(可見源碼:jw.asmsupport.block.ProgramBlock的executeQueue),經過源碼能夠知道:全部的程序塊ProgramBlock及其子類,全部的操做(AbstractOperator的子類),以及建立全局變量操做GlobalVariableCreator和方法的建立MethodCreator是實現了Executeable的方法的。因此咱們將會獲得以下的執行隊列.</p>

<table border="1" cellspacing="0" cellpadding="2" width="600"><tbody> <tr> <td valign="top" width="113">對象</td>

<td valign="top" width="66">別名</td>

  <td valign="top" width="421">對應代碼</td>
</tr>

<tr>
  <td valign="top" width="113">Addition</td>

  <td valign="top" width="66">add1</td>

  <td valign="top" width="421">100 + 10</td>
</tr>

<tr>
  <td valign="top" width="113">LocalVariableCreator</td>

  <td valign="top" width="66">&nbsp;</td>

  <td valign="top" width="421">&nbsp;</td>
</tr>

<tr>
  <td valign="top" width="113">Assigner</td>

  <td valign="top" width="66">ass1</td>

  <td valign="top" width="421">&nbsp;</td>
</tr>

<tr>
  <td valign="top" width="113">Addition</td>

  <td valign="top" width="66">add2</td>

  <td valign="top" width="421">&nbsp;</td>
</tr>

<tr>
  <td valign="top" width="113">MethodInvoker</td>

  <td valign="top" width="66">mi</td>

  <td valign="top" width="421">System.out.println(...)</td>
</tr>

</tbody></table>

<p>至於其餘的對象呢,其實經過代碼咱們能夠看到,都傳入到相應的操做類當中去了,好比Value.value(100)和Value.value(10)傳入到了add1當中去了。當全部對象都已就位以後逐一執行prepare方法,執行以後的執行隊列以下:</p>

<table border="1" cellspacing="0" cellpadding="2" width="425"><tbody> <tr> <td valign="top" width="138">對象</td>

<td valign="top" width="71">別名</td>

  <td valign="top" width="214">對應代碼</td>
</tr>

<tr>
  <td valign="top" width="138">Assigner</td>

  <td valign="top" width="71">ass1</td>

  <td valign="top" width="214">value = add1</td>
</tr>

<tr>
  <td valign="top" width="138">MethodInvoker</td>

  <td valign="top" width="71">mi</td>

  <td valign="top" width="214">System.out.println(...);</td>
</tr>

</tbody></table>

<p>爲何執行了prepare以後執行隊列會將add1,add2移除呢,是由於prepare遍歷這個隊列的時候對每一個隊列中的對象判斷時候被其餘對象引用,若是被其餘的引用了,就將其移除,而將執行execute方法的控制權轉交到被引用的對象上去。程序是如何判斷是不是被其餘操做引用了呢,經過Parameterized接口的asArgument()方法。經過代碼能夠看到this.add(Value.value(100), Value.value(10))這個代碼的返回值是被createVariable方法所引用的。add(value, Value.value(1))是被invoke()方法引用的。</p>

<p>在執行完prepare以後咱們再一次遍歷這個列表,而後逐一執行execute方法,執行完execute方法以後就生成了字節碼了。</p>

相關文章
相關標籤/搜索