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>

相关文章
相关标签/搜索