ASM指南翻译-13 有状态转换
3.2.5有状态转换
?
每个转换都会以一个条件作为标签(就是当前的指令)以及一个动作(一个必须被执行的指令序列,以粗体表示)。例如从S1到S0的转换,只有当前的指令不是ALOAD 0时才进行。在这种情形下,ALOAD 0会被访问然到达初始状态。来看看S2到自身的转换:如果找到3个或者更多的连续的ALOAD 0指令,那么将进行S2到自身的转换。在这种情形下,我们会停留在这样的状态:两条ALOAD 0指令已经被访问,同时我们将再执行一个ALOAD 0指令。一旦这个状态机模型被找到,那么编写对应的方法适配器就很直接了(下面的8个switch case就对应着上面的8个转换):
class RemoveGetFieldPutFieldAdapter extendsPatternMethodAdapter {
???????? privatefinal static int SEEN_ALOAD_0 = 1;
???????? privatefinal static int SEEN_ALOAD_0ALOAD_0 = 2;
???????? privatefinal static int SEEN_ALOAD_0ALOAD_0GETFIELD = 3;
???????? privateString fieldOwner;
???????? privateString fieldName;
???????? privateString fieldDesc;
????????
???????? publicRemoveGetFieldPutFieldAdapter(MethodVisitor mv) {
?????????????????? super(mv);
???????? }
???????? @Override
???????? publicvoid visitVarInsn(int opcode, int var) {
?????????????????? switch(state) {
?????????????????? caseSEEN_NOTHING: // S0 -> S1
??????????????????????????? if(opcode == ALOAD && var == 0) {
??????????????????????????? state= SEEN_ALOAD_0;
??????????????????????????? return;
??????????????????????????? }
??????????????????????????? break;
?????????????????? caseSEEN_ALOAD_0: // S1 -> S2
??????????????????????????? if(opcode == ALOAD && var == 0) {
??????????????????????????? state= SEEN_ALOAD_0ALOAD_0;
??????????????????????????? return;
??????????????????????????? }
??????????????????????????? break;
?????????????????? caseSEEN_ALOAD_0ALOAD_0: // S2 -> S2
??????????????????????????? if(opcode == ALOAD && var == 0) {
??????????????????????????? mv.visitVarInsn(ALOAD,0);
??????????????????????????? return;
??????????????????????????? }
??????????????????????????? break;
?????????????????? }
?????????????????? visitInsn();
?????????????????? mv.visitVarInsn(opcode,var);
???????? }
???????? @Override
???????? publicvoid visitFieldInsn(int opcode, String owner, String name,
?????????????????? Stringdesc) {
?????????????????? switch(state) {
?????????????????? caseSEEN_ALOAD_0ALOAD_0: // S2 -> S3
??????????????????????????? if(opcode == GETFIELD) {
??????????????????????????? state= SEEN_ALOAD_0ALOAD_0GETFIELD;
??????????????????????????? fieldOwner= owner;
??????????????????????????? fieldName= name;
??????????????????????????? fieldDesc= desc;
??????????????????????????? return;
??????????????????????????? }
??????????????????????????? break;
?????????????????? caseSEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -> S0
??????????????????????????? if(opcode == PUTFIELD && name.equals(fieldName)) {
??????????????????????????? state= SEEN_NOTHING;
??????????????????????????? return;
??????????????????????????? }
??????????????????????????? break;
?????????????????? }
?????????????????? visitInsn();
?????????????????? mv.visitFieldInsn(opcode,owner, name, desc);
???????? }
???????? @Overrideprotected void visitInsn() {
?????????????????? switch(state) {
?????????????????? caseSEEN_ALOAD_0: // S1 -> S0
??????????????????????????? mv.visitVarInsn(ALOAD,0);
??????????????????????????? break;
?????????????????? caseSEEN_ALOAD_0ALOAD_0: // S2 -> S0
??????????????????????????? mv.visitVarInsn(ALOAD,0);
??????????????????????????? mv.visitVarInsn(ALOAD,0);
??????????????????????????? break;
?????????????????? caseSEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -> S0
??????????????????????????? mv.visitVarInsn(ALOAD,0);
??????????????????????????? mv.visitVarInsn(ALOAD,0);
??????????????????????????? mv.visitFieldInsn(GETFIELD,fieldOwner, fieldName, fieldDesc);
??????????????????????????? break;
?????????????????? }
?????????????????? state= SEEN_NOTHING;
???????? }
}
?
注意,因为和3.2.4节中AddTimerAdapter相似的原因,本节中出现的有状态转换不需要转换栈映射帧:原始的帧仍在转换之后然有效。也不需要转换局部变量和操作数栈的大小。最后,注意有状态转换不局限于检测和转换指令序列,还有很多其它类型的转换也是状态转换。下一节出现的有关方法适配器就是这样的例子。