ASMSupport教程4:生成常用操作

╄→гoц情女王★ 提交于 2019-12-13 22:15:51

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

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

<p>这里存在6种操作,让我们一句一句解析出操作具体如下:</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: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>

<p>既然是操作就有操作因子,什么是操作因子?见下表:</p>

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

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

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!