From ccdcc1340e1c1c2293523f0e4ac9a2088998132b Mon Sep 17 00:00:00 2001 From: wangxiang <276644985@qq.com> Date: Tue, 25 Jul 2023 17:44:09 +0800 Subject: [PATCH] add INVOKEINTERFACE --- README.md | 1 + .../haidnor/jvm/core/JavaExecutionEngine.java | 16 +- .../jvm/instruction/InstructionFactory.java | 6 +- .../references/INVOKEINTERFACE.java | 145 ++++++++++++++++++ .../instruction/references/MONITORENTER.java | 21 +++ .../instruction/references/MONITOREXIT.java | 21 +++ .../haidnor/jvm/util/ConstantPoolUtil.java | 21 +++ src/test/java/haidnor/jvm/test/TestJVM.java | 11 +- .../java/haidnor/jvm/test/clazz/Human.java | 2 +- .../java/haidnor/jvm/test/clazz/Organism.java | 12 ++ .../java/haidnor/jvm/test/clazz/Student.java | 9 +- .../java/haidnor/jvm/test/demo/Demo8.java | 17 ++ .../references/INVOKEINTERFACE.java | 14 -- 13 files changed, 257 insertions(+), 39 deletions(-) create mode 100644 src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java create mode 100644 src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java create mode 100644 src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java create mode 100644 src/test/java/haidnor/jvm/test/clazz/Organism.java create mode 100644 src/test/java/haidnor/jvm/test/demo/Demo8.java delete mode 100644 src/test/java/haidnor/jvm/test/instruction/references/INVOKEINTERFACE.java diff --git a/README.md b/README.md index f247213..343cab3 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ * 支持多态 * 支持反射 * 支持访问静态方法 +* 支持异常 # 局限性 * 不支持多线程 diff --git a/src/main/java/haidnor/jvm/core/JavaExecutionEngine.java b/src/main/java/haidnor/jvm/core/JavaExecutionEngine.java index fb3a649..04fefc3 100644 --- a/src/main/java/haidnor/jvm/core/JavaExecutionEngine.java +++ b/src/main/java/haidnor/jvm/core/JavaExecutionEngine.java @@ -88,22 +88,24 @@ public class JavaExecutionEngine { log.debug("{}├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌", blank); - // 执行方法中的字节码指令 tip:(int i, 相当于程序计数器, 记录当前执行到的字节码指令的”行号“) - for (int i = 0; i < frame.getCodeLength(); ) { - Instruction instruction = instructionMap.get(i); + // 执行方法中的字节码指令 tip:(int pc, 相当于程序计数器, 记录当前执行到的字节码指令的”行号“) + for (int pc = 0; pc < frame.getCodeLength(); ) { + Instruction instruction = instructionMap.get(pc); log.debug("{}│ {}", blank, instruction); try { instruction.execute(frame); if (instruction instanceof RETURN || instruction instanceof ARETURN || instruction instanceof DRETURN || instruction instanceof FRETURN || instruction instanceof IRETURN) { break; } - i += instruction.offSet(); - } catch (Exception exception) { + pc += instruction.offSet(); + } + // catch instruction.execute() Exception + catch (Exception exception) { Integer handlerPC = null; CodeException[] exceptionTable = frame.getMethod().getCode().getExceptionTable(); for (CodeException codeException : exceptionTable) { - if (codeException.getStartPC() <= i & i <= codeException.getEndPC()) { + if (codeException.getStartPC() <= pc & pc <= codeException.getEndPC()) { int catchType = codeException.getCatchType(); // 0, if the handler catches any exception, otherwise it points to the exception class which is to be caught. if (catchType == 0) { @@ -121,7 +123,7 @@ public class JavaExecutionEngine { } } if (handlerPC != null) { - i = handlerPC; + pc = handlerPC; } else { log.debug("{}└──────────────────[{}] No Exception Handler Return!", blank, stackSize); throw exception; diff --git a/src/main/java/haidnor/jvm/instruction/InstructionFactory.java b/src/main/java/haidnor/jvm/instruction/InstructionFactory.java index 58baf22..c559f8e 100644 --- a/src/main/java/haidnor/jvm/instruction/InstructionFactory.java +++ b/src/main/java/haidnor/jvm/instruction/InstructionFactory.java @@ -577,7 +577,7 @@ public abstract class InstructionFactory { return new INVOKESTATIC(codeStream); } case Const.INVOKEINTERFACE -> { - throw new Error("Not support JavaVM opcode INVOKEINTERFACE"); + return new INVOKEINTERFACE(codeStream); } case Const.INVOKEDYNAMIC -> { throw new Error("Not support JavaVM opcode INVOKEDYNAMIC"); @@ -604,10 +604,10 @@ public abstract class InstructionFactory { return new INSTANCEOF(codeStream); } case Const.MONITORENTER -> { - throw new Error("Not support JavaVM opcode MONITORENTER"); + return new MONITORENTER(codeStream); } case Const.MONITOREXIT -> { - throw new Error("Not support JavaVM opcode MONITOREXIT"); + return new MONITOREXIT(codeStream); } case Const.WIDE -> { throw new Error("Not support JavaVM opcode WIDE"); diff --git a/src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java b/src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java new file mode 100644 index 0000000..360dd6f --- /dev/null +++ b/src/main/java/haidnor/jvm/instruction/references/INVOKEINTERFACE.java @@ -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; + } + +} diff --git a/src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java b/src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java new file mode 100644 index 0000000..58b9655 --- /dev/null +++ b/src/main/java/haidnor/jvm/instruction/references/MONITORENTER.java @@ -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"); + } + +} diff --git a/src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java b/src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java new file mode 100644 index 0000000..9be38f1 --- /dev/null +++ b/src/main/java/haidnor/jvm/instruction/references/MONITOREXIT.java @@ -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"); + } + +} diff --git a/src/main/java/haidnor/jvm/util/ConstantPoolUtil.java b/src/main/java/haidnor/jvm/util/ConstantPoolUtil.java index 1b8dffe..14a5f60 100644 --- a/src/main/java/haidnor/jvm/util/ConstantPoolUtil.java +++ b/src/main/java/haidnor/jvm/util/ConstantPoolUtil.java @@ -46,6 +46,7 @@ public class ConstantPoolUtil { ConstantUtf8 constantUtf8 = cp.getConstant(constantClass.getNameIndex()); return constantUtf8.getBytes(); } + /** * 获取长类名, 例如 java/lang/String */ @@ -132,4 +133,24 @@ public class ConstantPoolUtil { 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); + } + + } diff --git a/src/test/java/haidnor/jvm/test/TestJVM.java b/src/test/java/haidnor/jvm/test/TestJVM.java index bb9274e..a12fab0 100644 --- a/src/test/java/haidnor/jvm/test/TestJVM.java +++ b/src/test/java/haidnor/jvm/test/TestJVM.java @@ -11,7 +11,6 @@ import haidnor.jvm.test.instruction.Array; import haidnor.jvm.test.instruction.DO_WHILE; import haidnor.jvm.test.instruction.math.ISUB; import haidnor.jvm.test.instruction.math.LSUB; -import haidnor.jvm.test.instruction.references.INVOKEINTERFACE; import haidnor.jvm.test.instruction.references.NEW; import haidnor.jvm.util.JavaClassUtil; import haidnor.jvm.util.JvmThreadHolder; @@ -75,6 +74,11 @@ public class TestJVM { runMainClass(Demo7.class); } + @Test + public void test_8() throws Exception { + runMainClass(Demo8.class); + } + @Test public void test_NEW() throws Exception { runMainClass(NEW.class); @@ -100,11 +104,6 @@ public class TestJVM { runMainClass(Array.class); } - @Test - public void test_INVOKEINTERFACE() throws Exception { - runMainClass(INVOKEINTERFACE.class); - } - @Test public void test_() throws Exception { String jarFilePath = "D:\\project_java\\JvmDemo\\target\\JvmDemo-1.0-SNAPSHOT.jar"; diff --git a/src/test/java/haidnor/jvm/test/clazz/Human.java b/src/test/java/haidnor/jvm/test/clazz/Human.java index 93e90cd..aae5dcb 100644 --- a/src/test/java/haidnor/jvm/test/clazz/Human.java +++ b/src/test/java/haidnor/jvm/test/clazz/Human.java @@ -1,6 +1,6 @@ package haidnor.jvm.test.clazz; -public class Human { +public class Human implements Organism { public static final String HUMAN_NAME = "123"; diff --git a/src/test/java/haidnor/jvm/test/clazz/Organism.java b/src/test/java/haidnor/jvm/test/clazz/Organism.java new file mode 100644 index 0000000..3ecfe15 --- /dev/null +++ b/src/test/java/haidnor/jvm/test/clazz/Organism.java @@ -0,0 +1,12 @@ +package haidnor.jvm.test.clazz; + +/** + * 生物 + */ +public interface Organism { + + default void die() { + System.out.println("Organism die"); + } + +} diff --git a/src/test/java/haidnor/jvm/test/clazz/Student.java b/src/test/java/haidnor/jvm/test/clazz/Student.java index e4836f5..a6065a3 100644 --- a/src/test/java/haidnor/jvm/test/clazz/Student.java +++ b/src/test/java/haidnor/jvm/test/clazz/Student.java @@ -1,13 +1,6 @@ package haidnor.jvm.test.clazz; -public class Student extends Human { - - public static String school = "Hello World!"; - - static { - System.out.println(HUMAN_NAME); - System.out.println("student 类被加载了"); - } +public class Student implements Organism { public void method1() { System.out.println("method1"); diff --git a/src/test/java/haidnor/jvm/test/demo/Demo8.java b/src/test/java/haidnor/jvm/test/demo/Demo8.java new file mode 100644 index 0000000..e5af23f --- /dev/null +++ b/src/test/java/haidnor/jvm/test/demo/Demo8.java @@ -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(); + } + + +} diff --git a/src/test/java/haidnor/jvm/test/instruction/references/INVOKEINTERFACE.java b/src/test/java/haidnor/jvm/test/instruction/references/INVOKEINTERFACE.java deleted file mode 100644 index 69bff69..0000000 --- a/src/test/java/haidnor/jvm/test/instruction/references/INVOKEINTERFACE.java +++ /dev/null @@ -1,14 +0,0 @@ -package haidnor.jvm.test.instruction.references; - -import java.util.HashSet; - -public class INVOKEINTERFACE { - - public static void main(String[] args) { - HashSet set = new HashSet<>(); - set.add(1); - int size = set.size(); - System.out.println(size); - } - -}