《方法调用和返回》

Posted 里奥ii

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《方法调用和返回》相关的知识,希望对你有一定的参考价值。


案例介绍
本章节主要用java实现;方法调用指令、返回指令、解析方法符号引用、参数传递等。实现新的指令后我们的虚拟机就可以执行稍微复杂的运算并输出结果。

从调用的角度来看,方法可以分为两类:静态方法(或者类方法)和实例方法。静态方法通过类类调用,实例方法则通过对象引用来调用。静态方法是静态绑定的,也就是说,最终调用的是哪给方法在编译期就已经确定。实例方法则支持动态绑定,最终要调用哪给方法可能要推迟到运行期才能知道。
从实现的角度来看,方法可以分为三类:没有实现(也就是抽象方法)、用Java语言(或者JVM上其他的语言,如Groovy和Scala等)实现和用本地语言(如C或者C++)实现。静态方法和抽象方法是互斥的。在Java 8之前,接口只能包括抽象方法。为了实现Lambda表达式,Java 8放宽了这一限制,在接口中也可以定义静态方法和默认方法。
在Java 7之前,Java虚拟机规范一共提供了4条方法调用指令。其中invokestatic指令用来调用静态方法。invokespecial指令用来调用无须动态绑定的实例方法,包括构造函数、私有方法和通过super关键字调用的超类方法。剩下的情况则属于动态绑定。如果是针对接口类型的引用调用方法,就使用invokeinterface指令,否则使用invokevirtual指令。

环境准备
1、jdk 1.8.0
2、IntelliJ IDEA Community Edition 2018.3.1 x64

配置信息
1、调试配置
2.1、配置位置:Run/Debug Configurations -> program arguments
2.2、配置内容(选配 verbose true):-Xjre “C:\\Program Files\\Java\\jdk1.8.0_161\\jre” E:\\itstack\\git\\istack-demo\\itstack-demo-jvm\\itstack-demo-jvm-07\\target\\test-classes\\org\\itstack\\demo\\test\\HelloWorld verbose true

代码示例


itstack-demo-jvm-07
├── pom.xml
└── src
└── main
└── java
└── org.itstack.demo.jvm
├── classfile
├── attributes BootstrapMethods/Code/ConstantValue...
├── constantpool CONSTANT_TAG_CLASS/CONSTANT_TAG_FIELDREF/CONSTANT_TAG_METHODREF...
├── ClassFile.java
├── ClassReader.java
└── MemberInfo.java
├── classpath
├── impl
├── CompositeEntry.java
├── DirEntry.java
├── WildcardEntry.java
└── ZipEntry.java
├── Classpath.java
└── Entry.java
├── classpath
├── base
├── BytecodeReader.java
├── ClassInitLogic.java
├── Instruction.java
├── InstructionBranch.java
├── InstructionIndex8.java
├── InstructionIndex16.java
├── InstructionNoOperands.java
└── MethodInvokeLogic.java
├── comparisons
├── constants
├── control
├── conversions
├── extended
├── loads
├── math
├── references
├── CHECK_CAST.java
├── GET_FIELD.java
├── GET_STATIC.java
├── INSTANCE_OF.java
├── INVOKE_INTERFACE.java
├── INVOKE_SPECIAL.java
├── INVOKE_STATIC.java
├── INVOKE_VIRTUAL.java
├── NEW.java
├── PUT_FIELD.java
└── PUT_STATIC.java
├── stack
├── store
└── Factory
├── rtda
├── heap
├── constantpool
├── methodarea
├── Class.java
├── ClassMember.java
├── Field.java
├── Method.java
├── MethodDescriptor.java
├── MethodDescriptorParser.java
├── MethodLookup.java
├── Object.java
└── Slots.java
└── ClassLoader.java
├── Frame.java
├── JvmStack.java
├── LocalVars.java
├── OperandStack.java
├── Slot.java
└── Thread.java
├── Cmd.java
├── Interpret.java
└── Main.java
└── test
└── java
└── org.itstack.demo.test
└── HelloWorld.java


代码篇幅较长,只列重点代码块

​MethodInvokeLogic.java​


package org.itstack.demo.jvm.instructions.base;

import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.Slot;
import org.itstack.demo.jvm.rtda.Thread;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/28
*/
public class MethodInvokeLogic

public static void invokeMethod(Frame invokerFrame, Method method)
Thread thread = invokerFrame.thread();
Frame newFrame = thread.newFrame(method);
thread.pushFrame(newFrame);

int argSlotCount = method.argSlotCount();
if (argSlotCount > 0)
for (int i = argSlotCount - 1; i >= 0; i--)
Slot slot = invokerFrame.operandStack().popSlot();
newFrame.localVars().setSlot(i, slot);



//hack
if (method.isNative())
if ("registerNatives".equals(method.name()))
thread.popFrame();
else
throw new RuntimeException("native method " + method.name());





INVOKE_INTERFACE.java


package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.BytecodeReader;
import org.itstack.demo.jvm.instructions.base.Instruction;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.heap.constantpool.InterfaceMethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;
import org.itstack.demo.jvm.rtda.heap.methodarea.MethodLookup;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/28
*/
public class INVOKE_INTERFACE implements Instruction

private int idx;

@Override
public void fetchOperands(BytecodeReader reader)
this.idx = reader.readShort();
reader.readByte();
reader.readByte();


@Override
public void execute(Frame frame)
RunTimeConstantPool runTimeConstantPool = frame.method().clazz().constantPool();
InterfaceMethodRef methodRef = (InterfaceMethodRef) runTimeConstantPool.getConstants(this.idx);
Method resolvedMethod = methodRef.resolvedInterfaceMethod();
if (resolvedMethod.isStatic() || resolvedMethod.isPrivate())
throw new IncompatibleClassChangeError();

Object ref = frame.operandStack().getRefFromTop(resolvedMethod.argSlotCount() - 1);
if (null == ref)
throw new NullPointerException();

if (!ref.clazz().isImplements(methodRef.resolvedClass()))
throw new IncompatibleClassChangeError();

Method methodToBeInvoked = MethodLookup.lookupMethodInClass(ref.clazz(), methodRef.name(), methodRef.descriptor());
if (null == methodToBeInvoked || methodToBeInvoked.isAbstract())
throw new AbstractMethodError();

if (!methodToBeInvoked.isPublic())
throw new IllegalAccessError();


MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);




INVOKE_SPECIAL.java


package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.InstructionIndex16;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.heap.constantpool.MethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;
import org.itstack.demo.jvm.rtda.heap.methodarea.MethodLookup;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;

public class INVOKE_SPECIAL extends InstructionIndex16

@Override
public void execute(Frame frame)
Class currentClass = frame.method().clazz();
RunTimeConstantPool runTimeConstantPool = currentClass.constantPool();
MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
Class resolvedClass = methodRef.resolvedClass();
Method resolvedMethod = methodRef.ResolvedMethod();
if ("<init>".equals(resolvedMethod.name()) && resolvedMethod.clazz() != resolvedClass)
throw new NoSuchMethodError();

if (resolvedMethod.isStatic())
throw new IncompatibleClassChangeError();


Object ref = frame.operandStack().getRefFromTop(resolvedMethod.argSlotCount() - 1);
if (null == ref)
throw new NullPointerException();


if (resolvedMethod.isProtected() &&
resolvedMethod.clazz().isSubClassOf(currentClass) &&
!resolvedMethod.clazz().getPackageName().equals(currentClass.getPackageName()) &&
ref.clazz() != currentClass &&
!ref.clazz().isSubClassOf(currentClass))
throw new IllegalAccessError();


Method methodToBeInvoked = resolvedMethod;
if (currentClass.isSuper() &&
resolvedClass.isSubClassOf(currentClass) &&
!resolvedMethod.name().equals("<init>"))
MethodLookup.lookupMethodInClass(currentClass.superClass, methodRef.name(), methodRef.descriptor());


if (methodToBeInvoked.isAbstract())
throw new AbstractMethodError();


MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);




INVOKE_STATIC.java


package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.ClassInitLogic;
import org.itstack.demo.jvm.instructions.base.InstructionIndex16;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.heap.constantpool.MethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/28
*/
public class INVOKE_STATIC extends InstructionIndex16

@Override
public void execute(Frame frame)
RunTimeConstantPool runTimeConstantPool = frame.method().clazz().constantPool();
MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
Method resolvedMethod = methodRef.ResolvedMethod();

if (!resolvedMethod.isStatic())
throw new IncompatibleClassChangeError();


Class clazz = resolvedMethod.clazz();
if (!clazz.initStarted())
frame.revertNextPC();
ClassInitLogic.initClass(frame.thread(), clazz);
return;


MethodInvokeLogic.invokeMethod(frame, resolvedMethod);


INVOKE_VIRTUAL.java


package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.InstructionIndex16;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.OperandStack;
import org.itstack.demo.jvm.rtda.heap.constantpool.MethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;
import org.itstack.demo.jvm.rtda.heap.methodarea.MethodLookup;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;

public class INVOKE_VIRTUAL extends InstructionIndex16

@Override
public void execute(Frame frame)

Class currentClass = frame.method().clazz();
RunTimeConstantPool runTimeConstantPool = currentClass.constantPool();
MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
Method resolvedMethod = methodRef.ResolvedMethod();
if (resolvedMethod.isStatic())
throw new IncompatibleClassChangeError();


Object ref = frame.operandStack().getRefFromTop(resolvedMethod.argSlotCount() - 1);
if (null == ref)
if ("println".equals(methodRef.name()))
_println(frame.operandStack(), methodRef.descriptor());
return;

throw new NullPointerException();


if (resolvedMethod.isProtected() &&
resolvedMethod.clazz().isSubClassOf(currentClass) &&
!resolvedMethod.clazz().getPackageName().equals(currentClass.getPackageName()) &&
ref.clazz() != currentClass &&
!ref.clazz().isSubClassOf(currentClass))
throw new IllegalAccessError();


Method methodToBeInvoked = MethodLookup.lookupMethodInClass(ref.clazz(), methodRef.name(), methodRef.descriptor());
if (null == methodToBeInvoked || methodToBeInvoked.isAbstract())
throw new AbstractMethodError();


MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);


//hack
private void _println(OperandStack stack, String descriptor)
switch (descriptor)
case "(Z)V":
System.out.println(stack.popInt() != 0);
break;
case "(C)V":
System.out.println(stack.popInt());
break;
case "(I)V":
case "(B)V":
case "(S)V":
System.out.println(stack.popInt());
break;
case "(F)V":
System.out.println(stack.popFloat());
break;
case "(J)V":
System.out.println(stack.popLong());
break;
case "(D)V":
System.out.println(stack.popDouble());
break;
default:
System.out.println(descriptor);
break;

stack.popRef();


​MethodDescriptor.java​


package org.itstack.demo.jvm.rtda.heap.methodarea;

import java.util.ArrayList;
import java.util.List;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/28
*/
public class MethodDescriptor

public List<String> parameterTypes = new ArrayList<>();
public String returnType;

public void addParameterType(String type)
this.parameterTypes.add(type);



​MethodDescriptorParser.java​


package org.itstack.demo.jvm.rtda.heap.methodarea;

import javax.management.ObjectName;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/28
*/
public class MethodDescriptorParser

private String raw;
private int offset;
private MethodDescriptor parsed;

public static MethodDescriptor parseMethodDescriptorParser(String descriptor)
MethodDescriptorParser parser = new MethodDescriptorParser();
return parser.parse(descriptor);


public MethodDescriptor parse(String descriptor)
this.raw = descriptor;
this.parsed = new MethodDescriptor();
this.startParams();
this.parseParamTypes();
this.endParams();
this.parseReturnType();
this.finish();
return this.parsed;


private void startParams()
if (this.readUint8() != ()
causePanic();



private void endParams()
if (this.readUint8() != ))
causePanic();



public void finish()
if (this.offset != this.raw.length())
this.causePanic();



public void causePanic()
throw new RuntimeException("BAD descriptor:" + this.raw);


public byte readUint8()
byte[] bytes = this.raw.getBytes();
byte b = bytes[this.offset];
this.offset++;
return b;


public void unreadUint8()
this.offset--;


public void parseParamTypes()
while (true)
String type = this.parseFieldType();
if ("".equals(type)) break;
this.parsed.addParameterType(type);



public void parseReturnType()
if (this.readUint8() == V)
this.parsed.returnType = "V";
return;


this.unreadUint8();
String type = this.parseFieldType();
if (!"".equals(type))
this.parsed.returnType = type;
return;


this.causePanic();


public String parseFieldType()
switch (this.readUint8())
case B:
return "B";
case C:
return "C";
case D:
return "D";
case F:
return "F";
case I:
return "I";
case J:
return "J";
case S:
return "S";
case Z:
return "Z";
case L:
return this.parseObjectType();
case [:
return this.parseArrayType();
default:
this.unreadUint8();
return "";



private String parseObjectType()
String unread = this.raw.substring(this.offset);
int semicolonIndx = unread.indexOf(";");
if (semicolonIndx == -1)
this.causePanic();
return "";

int objStart = this.offset - 1;
int ojbEnd = this.offset + semicolonIndx + 1;
this.offset = ojbEnd;
//descriptor
return this.raw.substring(objStart, ojbEnd);


private String parseArrayType()
int arrStart = this.offset - 1;
this.parseFieldType();
int arrEnd = this.offset;
//descriptor
return this.raw.substring(arrStart, arrEnd);




​MethodLookup.java​


package org.itstack.demo.jvm.rtda.heap.methodarea;

/**
* http://www.itstack.org
* create by fuzhengwei on 2019/4/28
*/
public class MethodLookup

static public Method lookupMethodInClass(Class clazz, String name, String descriptor)
for (Class c = clazz; c != null; c = c.superClass)
for (Method method : c.methods)
if (method.name.equals(name) && method.descriptor.equals(descriptor))
return method;



return null;


static public Method lookupMethodInInterfaces(Class[] ifaces, String name, String descriptor)
for (Class inface : ifaces)
for (Method method : inface.methods)
if (method.name.equals(name) && method.descriptor.equals(descriptor))
return method;



return null;



​Interpret.java​


//指令集解释器
class Interpret

Interpret(Method method, boolean logInst)
Thread thread = new Thread();
Frame frame = thread.newFrame(method);
thread.pushFrame(frame);

loop(thread, logInst);


private void loop(Thread thread, boolean logInst)
BytecodeReader reader = new BytecodeReader();
while (true)
Frame frame = thread.currentFrame();
int pc = frame.nextPC();
thread.setPC(pc);

reader.reset(frame.method().code, pc);
byte opcode = reader.readByte();
Instruction inst = Factory.newInstruction(opcode);
if (null == inst)
System.out.println("Unsupported opcode " + byteToHexString(网信如何打通 AI+BlockChain 赋能的 “任督二脉”?

《方法调用和返回》

Jvm(48),指令集----方法调用和指令返回

深入理解Java虚拟机——方法调用和返回指令

深入理解Java虚拟机——方法调用和返回指令

方法的定义和调用