Java ASM系列:(056)opcode: method

Posted lsieun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java ASM系列:(056)opcode: method相关的知识,希望对你有一定的参考价值。

本文属于Java ASM系列二:OPCODE当中的一篇。


对于《Java ASM系列二:OPCODE》有配套的视频讲解,可以点击这里这里进行查看;同时,也可以点击这里查看源码资料。


1. 概览

从Instruction的角度来说,与method相关的opcode有5个,内容如下:

opcode mnemonic symbol opcode mnemonic symbol opcode mnemonic symbol
182 invokevirtual 184 invokestatic 186 invokedynamic
183 invokespecial 185 invokeinterface 187

从ASM的角度来说,这些opcode与MethodVisitor.visitXxxInsn()方法对应关系如下:

  • MethodVisitor.visitMethodInsn(): invokevirtual, invokespecial, invokestatic, invokeinterface
  • MethodVisitor.visitInvokeDynamicInsn(): invokedynamic

另外,我们要注意:

  • 方法调用,是先把方法所需要的参数加载到operand stack上,最后再进行方法的调用。
  • static方法,在local variables索引为0的位置,存储的可能是方法的第一个参数或方法体内定义的局部变量。

2. invokevirtual

从Java语言的视角,有一个HelloWorld类,代码如下:

public class HelloWorld {
    public void publicMethod(String name, int age) {
        // do nothing
    }

    protected void protectedMethod() {
        // do nothing
    }

    void packageMethod() {
        // do nothing
    }

    public void test() {
        publicMethod("tomcat", 10);
        protectedMethod();
        packageMethod();
        String str = toString();
    }
}

从Instruction的视角来看,方法体对应的内容如下:

$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: aload_0
       1: ldc           #2                  // String tomcat
       3: bipush        10
       5: invokevirtual #3                  // Method publicMethod:(Ljava/lang/String;I)V
       8: aload_0
       9: invokevirtual #4                  // Method protectedMethod:()V
      12: aload_0
      13: invokevirtual #5                  // Method packageMethod:()V
      16: aload_0
      17: invokevirtual #6                  // Method java/lang/Object.toString:()Ljava/lang/String;
      20: astore_1
      21: return
}

从ASM的视角来看,方法体对应的内容如下:

methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitLdcInsn("tomcat");
methodVisitor.visitIntInsn(BIPUSH, 10);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "sample/HelloWorld", "publicMethod", "(Ljava/lang/String;I)V", false);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "sample/HelloWorld", "protectedMethod", "()V", false);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "sample/HelloWorld", "packageMethod", "()V", false);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(3, 2);
methodVisitor.visitEnd();

从Frame的视角来看,local variable和operand stack的变化:

                               // {this} | {}
0000: aload_0                  // {this} | {this}
0001: ldc             #2       // {this} | {this, String}
0003: bipush          10       // {this} | {this, String, int}
0005: invokevirtual   #3       // {this} | {}
0008: aload_0                  // {this} | {this}
0009: invokevirtual   #4       // {this} | {}
0012: aload_0                  // {this} | {this}
0013: invokevirtual   #5       // {this} | {}
0016: aload_0                  // {this} | {this}
0017: invokevirtual   #6       // {this} | {String}
0020: astore_1                 // {this, String} | {}
0021: return                   // {} | {}

从JVM规范的角度来看,invokevirtual指令对应的Operand Stack的变化如下:

..., objectref, [arg1, [arg2 ...]] →

...

The objectref must be followed on the operand stack by nargs argument values, where the number, type, and order of the values must be consistent with the descriptor of the selected instance method.

3. invokespecial

从JVM规范的角度来看,invokespecial指令对应的Operand Stack的变化如下:

..., objectref, [arg1, [arg2 ...]] →

...

The objectref must be of type reference and must be followed on the operand stack by nargs argument values, where the number, type, and order of the values must be consistent with the descriptor of the selected instance method.

3.1. invoke constructor

从Java语言的视角,有一个HelloWorld类,代码如下:

public class HelloWorld {
    public void test() {
        HelloWorld instance = new HelloWorld();
    }
}

从Instruction的视角来看,方法体对应的内容如下:

$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: new           #2                  // class sample/HelloWorld
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: return
}

从ASM的视角来看,方法体对应的内容如下:

methodVisitor.visitCode();
methodVisitor.visitTypeInsn(NEW, "sample/HelloWorld");
methodVisitor.visitInsn(DUP);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "sample/HelloWorld", "<init>", "()V", false);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();

从Frame的视角来看,local variable和operand stack的变化:

                               // {this} | {}
0000: new             #2       // {this} | {uninitialized_HelloWorld}
0003: dup                      // {this} | {uninitialized_HelloWorld, uninitialized_HelloWorld}
0004: invokespecial   #3       // {this} | {HelloWorld}
0007: astore_1                 // {this, HelloWorld} | {}
0008: return                   // {} | {}

3.2. invoke private method

从Java语言的视角,有一个HelloWorld类,代码如下:

public class HelloWorld {
    private void privateMethod() {
        // do nothing
    }

    public void test() {
        privateMethod();
    }
}

从Instruction的视角来看,方法体对应的内容如下:

$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method privateMethod:()V
       4: return
}

从ASM的视角来看,方法体对应的内容如下:

methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "sample/HelloWorld", "privateMethod", "()V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();

从Frame的视角来看,local variable和operand stack的变化:

                               // {this} | {}
0000: aload_0                  // {this} | {this}
0001: invokespecial   #2       // {this} | {}
0004: return                   // {} | {}

3.3. invoke super method

从Java语言的视角,有一个HelloWorld类,代码如下:

public class HelloWorld {
    public void test() {
        String str = super.toString();
    }
}

从Instruction的视角来看,方法体对应的内容如下:

$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object.toString:()Ljava/lang/String;
       4: astore_1
       5: return
}

从ASM的视角来看,方法体对应的内容如下:

methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 2);
methodVisitor.visitEnd();

从Frame的视角来看,local variable和operand stack的变化:

                               // {this} | {}
0000: aload_0                  // {this} | {this}
0001: invokespecial   #2       // {this} | {String}
0004: astore_1                 // {this, String} | {}
0005: return                   // {} | {}

4. invokestatic

从Java语言的视角,有一个HelloWorld类,代码如下:

public class HelloWorld {
    public static void staticPublicMethod(String name, int age) {
        // do nothing
    }

    protected static void staticProtectedMethod() {
        // do nothing
    }

    static void staticPackageMethod() {
        // do nothing
    }

    private static void staticPrivateMethod() {
        // do nothing
    }

    public void test() {
        staticPublicMethod("tomcat", 10);
        staticProtectedMethod();
        staticPackageMethod();
        staticPrivateMethod();
    }
}

从Instruction的视角来看,方法体对应的内容如下:

$  javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: ldc           #2                  // String tomcat
       2: bipush        10
       4: invokestatic  #3                  // Method staticPublicMethod:(Ljava/lang/String;I)V
       7: invokestatic  #4                  // Method staticProtectedMethod:()V
      10: invokestatic  #5                  // Method staticPackageMethod:()V
      13: invokestatic  #6                  // Method staticPrivateMethod:()V
      16: return
}

从ASM的视角来看,方法体对应的内容如下:

methodVisitor.visitCode();
methodVisitor.visitLdcInsn("tomcat");
methodVisitor.visitIntInsn(BIPUSH, 10);
methodVisitor.visitMethodInsn(INVOKESTATIC, "sample/HelloWorld", "staticPublicMethod", "(Ljava/lang/String;I)V", false);
methodVisitor.visitMethodInsn(INVOKESTATIC, "sample/HelloWorld", "staticProtectedMethod", "()V", false);
methodVisitor.visitMethodInsn(INVOKESTATIC, "sample/HelloWorld", "staticPackageMethod", "()V", false);
methodVisitor.visitMethodInsn(INVOKESTATIC, "sample/HelloWorld", "staticPrivateMethod", "()V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 1);
methodVisitor.visitEnd();

从Frame的视角来看,local variable和operand stack的变化:

                               // {this} | {}
0000: ldc             #2       // {this} | {String}
0002: bipush          10       // {this} | {String, int}
0004: invokestatic    #3       // {this} | {}
0007: invokestatic    #4       // {this} | {}
0010: invokestatic    #5       // {this} | {}
0013: invokestatic    #6       // {this} | {}
0016: return                   // {} | {}

从JVM规范的角度来看,invokestatic指令对应的Operand Stack的变化如下:

..., [arg1, [arg2 ...]] →

...

The operand stack must contain nargs argument values, where the number, type, and order of the values must be consistent with the descriptor of the resolved method.

5. invokeinterface

从Java语言的视角,有一个HelloWorld类,代码如下:

public class HelloWorld {
    public void test() {
        MyInterface instance = new MyInterface() {
            @Override
            public void targetMethod() {
                // do nothing
            }
        };
        instance.defaultMethod();
        instance.targetMethod();
        MyInterface.staticMethod();
    }
}

interface MyInterface {
    static void staticMethod() {
        // do nothing
    }

    default void defaultMethod() {
        // do nothing
    }

    void targetMethod();
}

从Instruction的视角来看,方法体对应的内容如下:

$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: new           #2                  // class sample/HelloWorld$1
       3: dup
       4: aload_0
       5: invokespecial #3                  // Method sample/HelloWorld$1."<init>":(Lsample/HelloWorld;)V
       8: astore_1
       9: aload_1
      10: invokeinterface #4,  1            // InterfaceMethod sample/MyInterface.defaultMethod:()V
      15: aload_1
      16: invokeinterface #5,  1            // InterfaceMethod sample/MyInterface.targetMethod:()V
      21: invokestatic  #6                  // InterfaceMethod sample/MyInterface.staticMethod:()V
      24: return
}

从ASM的视角来看,方法体对应的内容如下:

methodVisitor.visitCode();
methodVisitor.visitTypeInsn(NEW, "sample/HelloWorld$1");
methodVisitor.visitInsn(DUP);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "sample/HelloWorld$1", "<init>", "(Lsample/HelloWorld;)V", false);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitMethodInsn(INVOKEINTERFACE, "sample/MyInterface", "defaultMethod", "()V", true);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitMethodInsn(INVOKEINTERFACE, "sample/MyInterface", "targetMethod", "()V", true);
methodVisitor.visitMethodInsn(INVOKESTATIC, "sample/MyInterface", "staticMethod", "()V", true);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(3, 2);
methodVisitor.visitEnd();

从Frame的视角来看,local variable和operand stack的变化:

                               // {this} | {}
0000: new             #2       // {this} | {uninitialized_HelloWorld$1}
0003: dup                      // {this} | {uninitialized_HelloWorld$1, uninitialized_HelloWorld$1}
0004: aload_0                  // {this} | {uninitialized_HelloWorld$1, uninitialized_HelloWorld$1, this}
0005: invokespecial   #3       // {this} | {HelloWorld$1}
0008: astore_1                 // {this, HelloWorld$1} | {}
0009: aload_1                  // {this, HelloWorld$1} | {HelloWorld$1}
0010: invokeinterface #4  1    // {this, HelloWorld$1} | {}
0015: aload_1                  // {this, HelloWorld$1} | {HelloWorld$1}
0016: invokeinterface #5  1    // {this, HelloWorld$1} | {}
0021: invokestatic    #6       // {this, HelloWorld$1} | {}
0024: return                   // {} | {}

从JVM规范的角度来看,invokeinterface指令对应的Operand Stack的变化如下:

..., objectref, [arg1, [arg2 ...]] →

...

The objectref must be of type reference and must be followed on the operand stack by nargs argument values, where the number, type, and order of the values must be consistent with the descriptor of the resolved interface method.

6. invokedynamic

从Java语言的视角,有一个HelloWorld类,代码如下:

import java.util.function.Consumer;

public class HelloWorld {
    public void test() {
        Consumer<String> c = System.out::println;
    }
}

从Instruction的视角来看,方法体对应的内容如下:

$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
  public void test();
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: dup
       4: invokevirtual #3                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
       7: pop
       8: invokedynamic #4,  0              // InvokeDynamic #0:accept:(Ljava/io/PrintStream;)Ljava/util/function/Consumer;
      13: astore_1
      14: return
}

从ASM的视角来看,方法体对应的内容如下:

methodVisitor.visitCode();
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitInsn(DUP);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
methodVisitor.visitInsn(POP);
methodVisitor.visitInvokeDynamicInsn("accept", "(Ljava/io/PrintStream;)Ljava/util/function/Consumer;", 
    new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", 
        "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), 
    new Object[]{Type.getType("(Ljava/lang/Object;)V"), 
    new Handle(Opcodes.H_INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false), 
    Type.getType("(Ljava/lang/String;)V")});
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();

从Frame的视角来看,local variable和operand stack的变化:

                               // {this} | {}
0000: getstatic       #2       // {this} | {PrintStream}
0003: dup                      // {this} | {PrintStream, PrintStream}
0004: invokevirtual   #3       // {this} | {PrintStream, Class}
0007: pop                      // {this} | {PrintStream}
0008: invokedynamic   #4       // {this} | {Consumer}
0013: astore_1                 // {this, Consumer} | {}
0014: return                   // {} | {}

从JVM规范的角度来看,invokedynamic指令对应的Operand Stack的变化如下:

..., [arg1, [arg2 ...]] →

...

以上是关于Java ASM系列:(056)opcode: method的主要内容,如果未能解决你的问题,请参考以下文章

Java ASM系列:(053)opcode: math

Java ASM系列:(051)opcode: constant

Java ASM系列:(058)opcode: jump

Java ASM系列:(059)opcode: stack

Java ASM系列:(067)Java 8 Lambda原理探究

Java ASM系列:(066)Exception处理