add INVOKEINTERFACE

This commit is contained in:
wangxiang
2023-07-25 17:44:09 +08:00
parent 1bc96be6cc
commit ccdcc1340e
13 changed files with 257 additions and 39 deletions

View File

@@ -17,6 +17,7 @@
* 支持多态 * 支持多态
* 支持反射 * 支持反射
* 支持访问静态方法 * 支持访问静态方法
* 支持异常
# 局限性 # 局限性
* 不支持多线程 * 不支持多线程

View File

@@ -88,22 +88,24 @@ public class JavaExecutionEngine {
log.debug("{}├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌", blank); log.debug("{}├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌", blank);
// 执行方法中的字节码指令 tip:(int i, 相当于程序计数器, 记录当前执行到的字节码指令的”行号“) // 执行方法中的字节码指令 tip:(int pc, 相当于程序计数器, 记录当前执行到的字节码指令的”行号“)
for (int i = 0; i < frame.getCodeLength(); ) { for (int pc = 0; pc < frame.getCodeLength(); ) {
Instruction instruction = instructionMap.get(i); Instruction instruction = instructionMap.get(pc);
log.debug("{}│ {}", blank, instruction); log.debug("{}│ {}", blank, instruction);
try { try {
instruction.execute(frame); instruction.execute(frame);
if (instruction instanceof RETURN || instruction instanceof ARETURN || instruction instanceof DRETURN || instruction instanceof FRETURN || instruction instanceof IRETURN) { if (instruction instanceof RETURN || instruction instanceof ARETURN || instruction instanceof DRETURN || instruction instanceof FRETURN || instruction instanceof IRETURN) {
break; break;
} }
i += instruction.offSet(); pc += instruction.offSet();
} catch (Exception exception) { }
// catch instruction.execute() Exception
catch (Exception exception) {
Integer handlerPC = null; Integer handlerPC = null;
CodeException[] exceptionTable = frame.getMethod().getCode().getExceptionTable(); CodeException[] exceptionTable = frame.getMethod().getCode().getExceptionTable();
for (CodeException codeException : exceptionTable) { for (CodeException codeException : exceptionTable) {
if (codeException.getStartPC() <= i & i <= codeException.getEndPC()) { if (codeException.getStartPC() <= pc & pc <= codeException.getEndPC()) {
int catchType = codeException.getCatchType(); int catchType = codeException.getCatchType();
// 0, if the handler catches any exception, otherwise it points to the exception class which is to be caught. // 0, if the handler catches any exception, otherwise it points to the exception class which is to be caught.
if (catchType == 0) { if (catchType == 0) {
@@ -121,7 +123,7 @@ public class JavaExecutionEngine {
} }
} }
if (handlerPC != null) { if (handlerPC != null) {
i = handlerPC; pc = handlerPC;
} else { } else {
log.debug("{}└──────────────────[{}] No Exception Handler Return!", blank, stackSize); log.debug("{}└──────────────────[{}] No Exception Handler Return!", blank, stackSize);
throw exception; throw exception;

View File

@@ -577,7 +577,7 @@ public abstract class InstructionFactory {
return new INVOKESTATIC(codeStream); return new INVOKESTATIC(codeStream);
} }
case Const.INVOKEINTERFACE -> { case Const.INVOKEINTERFACE -> {
throw new Error("Not support JavaVM opcode INVOKEINTERFACE"); return new INVOKEINTERFACE(codeStream);
} }
case Const.INVOKEDYNAMIC -> { case Const.INVOKEDYNAMIC -> {
throw new Error("Not support JavaVM opcode INVOKEDYNAMIC"); throw new Error("Not support JavaVM opcode INVOKEDYNAMIC");
@@ -604,10 +604,10 @@ public abstract class InstructionFactory {
return new INSTANCEOF(codeStream); return new INSTANCEOF(codeStream);
} }
case Const.MONITORENTER -> { case Const.MONITORENTER -> {
throw new Error("Not support JavaVM opcode MONITORENTER"); return new MONITORENTER(codeStream);
} }
case Const.MONITOREXIT -> { case Const.MONITOREXIT -> {
throw new Error("Not support JavaVM opcode MONITOREXIT"); return new MONITOREXIT(codeStream);
} }
case Const.WIDE -> { case Const.WIDE -> {
throw new Error("Not support JavaVM opcode WIDE"); throw new Error("Not support JavaVM opcode WIDE");

View File

@@ -0,0 +1,145 @@
package haidnor.jvm.instruction.references;
import haidnor.jvm.classloader.ClassLoader;
import haidnor.jvm.core.JavaExecutionEngine;
import haidnor.jvm.instruction.Instruction;
import haidnor.jvm.rtda.Instance;
import haidnor.jvm.rtda.Klass;
import haidnor.jvm.rtda.KlassMethod;
import haidnor.jvm.rtda.Metaspace;
import haidnor.jvm.runtime.Frame;
import haidnor.jvm.runtime.StackValue;
import haidnor.jvm.util.CodeStream;
import haidnor.jvm.util.ConstantPoolUtil;
import haidnor.jvm.util.SignatureUtil;
import lombok.SneakyThrows;
import org.apache.bcel.Const;
import org.apache.bcel.classfile.ConstantInterfaceMethodref;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Utility;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* 调用对象实例方法,根据对象的实际类型进行分派(虚方法分派),支持多态
*/
public class INVOKEINTERFACE extends Instruction {
private final int constantMethodrefIndex;
public final int count;
public final int zero;
public INVOKEINTERFACE(CodeStream codeStream) {
super(codeStream);
this.constantMethodrefIndex = codeStream.readUnsignedShort(this);
this.count = codeStream.readUnsignedByte(this);
this.zero = codeStream.readUnsignedByte(this);
}
@Override
@SneakyThrows
public void execute(Frame frame) {
ConstantPool constantPool = frame.getConstantPool();
ConstantPoolUtil constantPoolUtil = frame.getConstantPoolUtil();
ConstantInterfaceMethodref interfaceMethodref = constantPool.getConstant(constantMethodrefIndex);
int classIndex = interfaceMethodref.getClassIndex();
String interfaceName = constantPoolUtil.getConstantClassClassName(classIndex);
int nameAndTypeIndex = interfaceMethodref.getNameAndTypeIndex();
String methodName = constantPoolUtil.constantNameAndType_name(nameAndTypeIndex);
String methodSignature = constantPoolUtil.constantNameAndType_signature(nameAndTypeIndex);
// 系统类反射 自定义类另外处理
if (interfaceName.startsWith("java/")) {
// 解析方法签名得到方法的返回类型
String returnType = Utility.methodSignatureReturnType(methodSignature, false);
// 执行方法的参数列表
Class<?>[] parameterTypeArr = SignatureUtil.getParameterTypes(methodSignature);
// 执行方法的参数值
Object[] args = frame.popStacksValue(parameterTypeArr.length);
// 将特定的参数转换为基本类型
for (int i = 0; i < parameterTypeArr.length; i++) {
Class<?> clazz = parameterTypeArr[i];
if (clazz.getName().equals("boolean")) { // boolean 存储方式为 int 类型
int booleanFlag = (int) args[i];
args[i] = booleanFlag == 1;
} else if (clazz.getName().equals("char")) { // char 存储方式为
int charInt = (int) args[i];
char c = (char) charInt;
args[i] = c;
}
}
StackValue stackValue = frame.pop();
Object obj = stackValue.getValue();
Method method = obj.getClass().getMethod(methodName, parameterTypeArr);
method.setAccessible(true);
Object value = method.invoke(obj, args);
if (!Objects.equals(Const.getTypeName(Const.T_VOID), returnType)) { // void 调用的方法无返回值
frame.push(new StackValue(Const.T_OBJECT, value));
}
}
// 调用自定义类的方法
else {
Klass interfaceKlass = Metaspace.getJavaClass(Utility.compactClassName(interfaceName));
if (interfaceKlass == null) {
ClassLoader classLoader = frame.getMetaClass().getClassLoader();
classLoader.loadClass(interfaceName);
}
StackValue stackValue = frame.peek();
Instance instance = (Instance) stackValue.getValue();
Klass klass = instance.klass;
// 按照继承关系从下往上找实现的方法 (实现多态)
org.apache.bcel.classfile.Method method = getMethod(klass.getJavaClass(), methodSignature, methodName);
// 从接口找方法 JDK8. interface default()
if (method == null) {
method = getMethodFromInterface(klass.getJavaClass(), methodSignature, methodName);
}
if (method == null) {
throw new AbstractMethodError();
}
KlassMethod klassMethod = new KlassMethod(klass, method);
JavaExecutionEngine.callMethod(frame, klassMethod);
}
}
/**
* 递归查找方法, 如果子类没有实现方法则向父类查找
*/
private static org.apache.bcel.classfile.Method getMethod(JavaClass javaClass, String methodSignature, String methodName) throws ClassNotFoundException {
org.apache.bcel.classfile.Method m = null;
for (org.apache.bcel.classfile.Method method : javaClass.getMethods()) {
if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) {
m = method;
}
}
if (m != null) {
return m;
}
if (javaClass.getSuperClass() == null) {
return null;
}
return getMethod(javaClass.getSuperClass(), methodSignature, methodName);
}
private static org.apache.bcel.classfile.Method getMethodFromInterface(JavaClass javaClass, String methodSignature, String methodName) throws ClassNotFoundException {
JavaClass[] interfaces = javaClass.getInterfaces();
for (JavaClass anInterface : interfaces) {
for (org.apache.bcel.classfile.Method method : anInterface.getMethods()) {
if (method.getSignature().equals(methodSignature) && method.getName().equals(methodName)) {
return method;
}
}
}
return null;
}
}

View File

@@ -0,0 +1,21 @@
package haidnor.jvm.instruction.references;
import haidnor.jvm.instruction.Instruction;
import haidnor.jvm.runtime.Frame;
import haidnor.jvm.util.CodeStream;
import lombok.SneakyThrows;
public class MONITORENTER extends Instruction {
public MONITORENTER(CodeStream codeStream) {
super(codeStream);
throw new UnsupportedOperationException("MONITORENTER");
}
@Override
@SneakyThrows
public void execute(Frame frame) {
throw new UnsupportedOperationException("MONITORENTER");
}
}

View File

@@ -0,0 +1,21 @@
package haidnor.jvm.instruction.references;
import haidnor.jvm.instruction.Instruction;
import haidnor.jvm.runtime.Frame;
import haidnor.jvm.util.CodeStream;
import lombok.SneakyThrows;
public class MONITOREXIT extends Instruction {
public MONITOREXIT(CodeStream codeStream) {
super(codeStream);
throw new UnsupportedOperationException("MONITOREXIT");
}
@Override
@SneakyThrows
public void execute(Frame frame) {
throw new UnsupportedOperationException("MONITOREXIT");
}
}

View File

@@ -46,6 +46,7 @@ public class ConstantPoolUtil {
ConstantUtf8 constantUtf8 = cp.getConstant(constantClass.getNameIndex()); ConstantUtf8 constantUtf8 = cp.getConstant(constantClass.getNameIndex());
return constantUtf8.getBytes(); return constantUtf8.getBytes();
} }
/** /**
* 获取长类名, 例如 java/lang/String * 获取长类名, 例如 java/lang/String
*/ */
@@ -132,4 +133,24 @@ public class ConstantPoolUtil {
return constNameAndType.getSignature(cp); return constNameAndType.getSignature(cp);
} }
// ConstantNameAndType -----------------------------------------------------------------------------------------------
/**
* ConstantNameAndType
*/
public ConstantNameAndType constantNameAndType(int constantNameAndTypeIndex) {
return cp.getConstant(constantNameAndTypeIndex);
}
public String constantNameAndType_name(int constantNameAndTypeIndex) {
ConstantNameAndType constantNameAndType = constantNameAndType(constantNameAndTypeIndex);
return constantNameAndType.getName(cp);
}
public String constantNameAndType_signature(int constantNameAndTypeIndex) {
ConstantNameAndType constantNameAndType = constantNameAndType(constantNameAndTypeIndex);
return constantNameAndType.getSignature(cp);
}
} }

View File

@@ -11,7 +11,6 @@ import haidnor.jvm.test.instruction.Array;
import haidnor.jvm.test.instruction.DO_WHILE; import haidnor.jvm.test.instruction.DO_WHILE;
import haidnor.jvm.test.instruction.math.ISUB; import haidnor.jvm.test.instruction.math.ISUB;
import haidnor.jvm.test.instruction.math.LSUB; import haidnor.jvm.test.instruction.math.LSUB;
import haidnor.jvm.test.instruction.references.INVOKEINTERFACE;
import haidnor.jvm.test.instruction.references.NEW; import haidnor.jvm.test.instruction.references.NEW;
import haidnor.jvm.util.JavaClassUtil; import haidnor.jvm.util.JavaClassUtil;
import haidnor.jvm.util.JvmThreadHolder; import haidnor.jvm.util.JvmThreadHolder;
@@ -75,6 +74,11 @@ public class TestJVM {
runMainClass(Demo7.class); runMainClass(Demo7.class);
} }
@Test
public void test_8() throws Exception {
runMainClass(Demo8.class);
}
@Test @Test
public void test_NEW() throws Exception { public void test_NEW() throws Exception {
runMainClass(NEW.class); runMainClass(NEW.class);
@@ -100,11 +104,6 @@ public class TestJVM {
runMainClass(Array.class); runMainClass(Array.class);
} }
@Test
public void test_INVOKEINTERFACE() throws Exception {
runMainClass(INVOKEINTERFACE.class);
}
@Test @Test
public void test_() throws Exception { public void test_() throws Exception {
String jarFilePath = "D:\\project_java\\JvmDemo\\target\\JvmDemo-1.0-SNAPSHOT.jar"; String jarFilePath = "D:\\project_java\\JvmDemo\\target\\JvmDemo-1.0-SNAPSHOT.jar";

View File

@@ -1,6 +1,6 @@
package haidnor.jvm.test.clazz; package haidnor.jvm.test.clazz;
public class Human { public class Human implements Organism {
public static final String HUMAN_NAME = "123"; public static final String HUMAN_NAME = "123";

View File

@@ -0,0 +1,12 @@
package haidnor.jvm.test.clazz;
/**
* 生物
*/
public interface Organism {
default void die() {
System.out.println("Organism die");
}
}

View File

@@ -1,13 +1,6 @@
package haidnor.jvm.test.clazz; package haidnor.jvm.test.clazz;
public class Student extends Human { public class Student implements Organism {
public static String school = "Hello World!";
static {
System.out.println(HUMAN_NAME);
System.out.println("student 类被加载了");
}
public void method1() { public void method1() {
System.out.println("method1"); System.out.println("method1");

View File

@@ -0,0 +1,17 @@
package haidnor.jvm.test.demo;
import haidnor.jvm.test.clazz.Human;
import haidnor.jvm.test.clazz.Organism;
import haidnor.jvm.test.clazz.Student;
public class Demo8 {
public static void main(String[] args) {
Human organism1 = new Human();
Organism organism = new Student();
organism.die();
}
}

View File

@@ -1,14 +0,0 @@
package haidnor.jvm.test.instruction.references;
import java.util.HashSet;
public class INVOKEINTERFACE {
public static void main(String[] args) {
HashSet<Integer> set = new HashSet<>();
set.add(1);
int size = set.size();
System.out.println(size);
}
}