用Java实现JVM第六章《类和对象》

作者: bugstack虫洞栈 | 来源:发表于2019-04-27 19:45 被阅读7次

    案例介绍
    本案例通过java代码实现jvm规范中指令集和解释器,完成后就可以开始执行1到100的加和计算。

    Java虚拟机顾名思义,就是一台虚拟的机器,而字节码(bytecode)就是运行在这台虚拟机器上的机器码。我们已经知道,每一个类或者接口都会被Java编译器编译成一个class文件,类或接口的方法信息就放在class文件的method_info结构中。如果方法不是抽象的,也不是本地方法,方法的Java代码就会被编译器编译成字节码(即使方法是空的,编译器也会生成一条return语句),存在method_info结构的Code属性中。

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

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

    代码示例

    itstack-demo-jvm-06
    ├── 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
        │             │   ├── comparisons
        │             │   ├── constants
        │             │   ├── control
        │             │   ├── conversions
        │             │   ├── extended
        │             │   ├── loads
        │             │   ├── math
        │             │   │   ├── add
        │             │   │   ├── and
        │             │   │   ├── div
        │             │   │   ├── iinc
        │             │   │   ├── mul
        │             │   │   ├── neg
        │             │   │   ├── or
        │             │   │   ├── rem
        │             │   │   ├── sh
        │             │   │   ├── sub
        │             │   │   └── xor
        │             │   ├── stack
        │             │   ├── store
        │             │   └── Factory   
        │             ├── rtda
        │             │   ├── heap
        │             │   │   ├── constantpool
        │             │   │   ├── methodarea
        │             │   │   │   ├── Class.java    
        │             │   │   │   ├── ClassMember.java  
        │             │   │   │   ├── Field.java    
        │             │   │   │   ├── Method.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
    

    Class.java

    public class Class {
    
        public int accessFlags;
        public String name;
        public String superClassName;
        public String[] interfaceNames;
        public RunTimeConstantPool runTimeConstantPool;
        public Field[] fields;
        public Method[] methods;
        public ClassLoader loader;
        public Class superClass;
        public Class[] interfaces;
        public int instanceSlotCount;
        public int staticSlotCount;
        public Slots staticVars;
    
        public Class(ClassFile classFile) {
            this.accessFlags = classFile.accessFlags();
            this.name = classFile.className();
            this.superClassName = classFile.superClassName();
            this.interfaceNames = classFile.interfaceNames();
            this.runTimeConstantPool = new RunTimeConstantPool(this, classFile.constantPool());
            this.fields = new Field().newFields(this, classFile.fields());
            this.methods = new Method().newMethods(this, classFile.methods());
        }
    
        public boolean isPublic() {
            return 0 != (this.accessFlags & AccessFlags.ACC_PUBLIC);
        }
    
        public boolean isFinal() {
            return 0 != (this.accessFlags & AccessFlags.ACC_FINAL);
        }
    
        public boolean isSuper() {
            return 0 != (this.accessFlags & AccessFlags.ACC_SUPER);
        }
    
        public boolean isInterface() {
            return 0 != (this.accessFlags & AccessFlags.ACC_INTERFACE);
        }
    
        public boolean isAbstract() {
            return 0 != (this.accessFlags & AccessFlags.ACC_ABSTRACT);
        }
    
        public boolean isSynthetic() {
            return 0 != (this.accessFlags & AccessFlags.ACC_SYNTHETIC);
        }
    
        public boolean isAnnotation() {
            return 0 != (this.accessFlags & AccessFlags.ACC_ANNOTATION);
        }
    
        public boolean isEnum() {
            return 0 != (this.accessFlags & AccessFlags.ACC_ENUM);
        }
    
        public RunTimeConstantPool constantPool() {
            return this.runTimeConstantPool;
        }
    
        public Slots staticVars() {
            return this.staticVars;
        }
    
        public boolean isAccessibleTo(Class other) {
            return this.isPublic() || this.getPackageName().equals(other.getPackageName());
        }
    
        public String getPackageName() {
            int i = this.name.lastIndexOf("/");
            if (i >= 0) return this.name;
            return "";
        }
    
        public Method getMainMethod() {
            return this.getStaticMethod("main", "([Ljava/lang/String;)V");
        }
    
        private Method getStaticMethod(String name, String descriptor) {
            for (Method method : this.methods) {
                if (method.name.equals(name) && method.descriptor.equals(descriptor)) {
                    return method;
                }
            }
            return null;
        }
    
        public Object newObject() {
            return new Object(this);
        }
    
        public boolean isAssignableFrom(Class other) {
            if (this == other) return true;
            if (!other.isInterface()) {
                return this.isSubClassOf(other);
            } else {
                return this.isImplements(other);
            }
        }
    
        public boolean isSubClassOf(Class other) {
            for (Class c = this.superClass; c != null; c = c.superClass) {
                if (c == other) {
                    return true;
                }
            }
            return false;
        }
    
        private boolean isImplements(Class other) {
    
            for (Class c = this; c != null; c = c.superClass) {
                for (Class clazz : c.interfaces) {
                    if (clazz == other || clazz.isSubInterfaceOf(other)) {
                        return true;
                    }
                }
            }
            return false;
    
        }
    
        public boolean isSubInterfaceOf(Class iface) {
            for (Class superInterface : this.interfaces) {
                if (superInterface == iface || superInterface.isSubInterfaceOf(iface)) {
                    return true;
                }
            }
            return false;
        }
    
    }
    

    ClassMember.java

    public class ClassMember {
    
        public int accessFlags;
        public String name;
        public String descriptor;
        public Class clazz;
    
        public void copyMemberInfo(MemberInfo memberInfo) {
            this.accessFlags = memberInfo.accessFlags();
            this.name = memberInfo.name();
            this.descriptor = memberInfo.descriptor();
        }
    
        public boolean isPublic() {
            return 0 != (this.accessFlags & AccessFlags.ACC_PUBLIC);
        }
    
        public boolean isPrivate() {
            return 0 != (this.accessFlags & AccessFlags.ACC_PRIVATE);
        }
    
        public boolean isProtected() {
            return 0 != (this.accessFlags & AccessFlags.ACC_PROTECTED);
        }
    
        public boolean isStatic() {
            return 0 != (this.accessFlags & AccessFlags.ACC_STATIC);
        }
    
        public boolean isFinal() {
            return 0 != (this.accessFlags & AccessFlags.ACC_FINAL);
        }
    
        public boolean isSynthetic() {
            return 0 != (this.accessFlags & AccessFlags.ACC_SYNTHETIC);
        }
    
        public String name() {
            return this.name;
        }
    
        public String descriptor() {
            return this.descriptor;
        }
    
        public Class clazz() {
            return this.clazz;
        }
    
        public boolean isAccessibleTo(Class d) {
            if (this.isPublic()) {
                return true;
            }
            Class c = this.clazz;
            if (this.isProtected()) {
                return d == c || c.getPackageName().equals(d.getPackageName());
            }
            if (!this.isPrivate()) {
                return c.getPackageName().equals(d.getPackageName());
            }
            return d == c;
        }
    
    }
    

    Field.java

    package org.itstack.demo.jvm.rtda.heap.methodarea;
    
    import org.itstack.demo.jvm.classfile.MemberInfo;
    import org.itstack.demo.jvm.classfile.attributes.impl.ConstantValueAttribute;
    import org.itstack.demo.jvm.rtda.heap.constantpool.AccessFlags;
    
    public class Field extends ClassMember {
    
        public int constValueIndex;
        public int slotId;
    
        public Field[] newFields(Class clazz, MemberInfo[] cfFields) {
            Field[] fields = new Field[cfFields.length];
            for (int i = 0; i < cfFields.length; i++) {
                fields[i] = new Field();
                fields[i].clazz = clazz;
                fields[i].copyMemberInfo(cfFields[i]);
                fields[i].copyAttributes(cfFields[i]);
            }
            return fields;
        }
    
        public void copyAttributes(MemberInfo cfField) {
            ConstantValueAttribute valAttr = cfField.ConstantValueAttribute();
            if (null != valAttr) {
                this.constValueIndex = valAttr.constantValueIdx();
            }
        }
    
        public boolean isVolatile() {
            return 0 != (this.accessFlags & AccessFlags.ACC_VOLATILE);
        }
    
        public boolean isTransient() {
            return 0 != (this.accessFlags & AccessFlags.ACC_TRANSIENT);
        }
    
        public boolean isEnum() {
            return 0 != (this.accessFlags & AccessFlags.ACC_ENUM);
        }
    
        public int constValueIndex() {
            return this.constValueIndex;
        }
    
        public int slotId() {
            return this.slotId;
        }
    
        public boolean isLongOrDouble() {
            return this.descriptor.equals("J") || this.descriptor.equals("D");
        }
    
    
    }
    

    Method.java

    package org.itstack.demo.jvm.rtda.heap.methodarea;
    
    import org.itstack.demo.jvm.classfile.MemberInfo;
    import org.itstack.demo.jvm.classfile.attributes.impl.CodeAttribute;
    import org.itstack.demo.jvm.rtda.heap.constantpool.AccessFlags;
    
    public class Method extends ClassMember {
    
        public int maxStack;
        public int maxLocals;
        public byte[] code;
    
        public Method[] newMethods(Class clazz, MemberInfo[] cfMethods) {
            Method[] methods = new Method[cfMethods.length];
            for (int i = 0; i < cfMethods.length; i++) {
                methods[i] = new Method();
                methods[i].clazz = clazz;
                methods[i].copyMemberInfo(cfMethods[i]);
                methods[i].copyAttributes(cfMethods[i]);
            }
            return methods;
        }
    
        public void copyAttributes(MemberInfo cfMethod) {
            CodeAttribute codeAttr = cfMethod.codeAttribute();
            if (null != codeAttr) {
                this.maxStack = codeAttr.maxStack();
                this.maxLocals = codeAttr.maxLocals();
                this.code = codeAttr.data();
            }
        }
    
        public boolean isSynchronized() {
            return 0 != (this.accessFlags & AccessFlags.ACC_SYNCHRONIZED);
        }
    
        public boolean isBridge() {
            return 0 != (this.accessFlags & AccessFlags.ACC_BRIDGE);
        }
    
        public boolean isVarargs() {
            return 0 != (this.accessFlags & AccessFlags.ACC_VARARGS);
        }
    
        public boolean isNative() {
            return 0 != (this.accessFlags & AccessFlags.ACC_NATIVE);
        }
    
        public boolean isAbstract() {
            return 0 != (this.accessFlags & AccessFlags.ACC_ABSTRACT);
        }
    
        public boolean isStrict() {
            return 0 != (this.accessFlags & AccessFlags.ACC_STRICT);
        }
    
        public int maxStack() {
            return this.maxStack;
        }
    
        public int maxLocals() {
            return this.maxLocals;
        }
    
        public byte[] code() {
            return this.code;
        }
    
    }
    

    Object.java

    package org.itstack.demo.jvm.rtda.heap.methodarea;
    
    public class Object {
    
        Class clazz;
        Slots fields;
    
        public Object(Class clazz){
            this.clazz = clazz;
            this.fields = new Slots(clazz.instanceSlotCount);
        }
    
        public Class clazz(){
            return this.clazz;
        }
    
        public Slots fields(){
            return this.fields;
        }
    
        public boolean isInstanceOf(Class clazz){
            return clazz.isAssignableFrom(this.clazz);
        }
    
    }
    

    Slots.java

    package org.itstack.demo.jvm.rtda.heap.methodarea;
    
    import org.itstack.demo.jvm.rtda.Slot;
    
    public class Slots {
    
        private Slot[] slots;
    
        public Slots(int slotCount) {
            if (slotCount > 0) {
                slots = new Slot[slotCount];
                for (int i = 0; i < slotCount; i++) {
                    slots[i] = new Slot();
                }
            }
        }
    
        public void setInt(int idx, int val) {
            this.slots[idx].num = val;
        }
    
        public int getInt(int idx) {
            return this.slots[idx].num;
        }
    
        public void setFloat(int idx, float val) {
            this.slots[idx].num = (int) val;
        }
    
        public float getFloat(int idx) {
            return this.slots[idx].num;
        }
    
        public void setLong(int idx, long val) {
            this.slots[idx].num = (int) val;
            this.slots[idx + 1].num = (int) (val >> 32);
        }
    
        public long getLong(int idx) {
            int low = this.slots[idx].num;
            int high = this.slots[idx + 1].num;
            return (long) high << 32 | (long) low;
        }
    
        public void setDouble(int idx, double val) {
            this.setLong(idx, (long) val);
        }
    
        public Double getDouble(int idx) {
            return (double) this.getLong(idx);
        }
    
        public void setRef(int idx, Object ref) {
            this.slots[idx].ref = ref;
        }
    
        public Object getRef(int idx){
            return this.slots[idx].ref;
        }
    
    }
    

    测试结果 {可以看到已经输出;5050}

    "C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\lib\idea_rt.jar=61887:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\classes;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\org\projectlombok\lombok\1.18.0\lombok-1.18.0.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\alibaba\fastjson\1.2.40\fastjson-1.2.40.jar" org.itstack.demo.jvm.Main -Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld
    classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld args:null
    寄存器(指令):0x03 -> ICONST_0 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
    寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
    寄存器(指令):0x04 -> ICONST_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
    寄存器(指令):0x3d -> ISTORE_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
    寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
    寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
    寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}]
    寄存器(指令):0x1b -> ILOAD_1 => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}]
    寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":0},{"num":100}] 操作数栈:[{"num":0},{"num":100}]
    
    ... ...
    
    寄存器(指令):0x60 -> IADD => 局部变量表:[{"num":4950},{"num":100}] 操作数栈:[{"num":4950},{"num":100}]
    寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
    寄存器(指令):0x84 -> IINC => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
    寄存器(指令):0xa7 -> GOTO => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
    寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
    寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}]
    寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}]
    5050
    寄存器(指令):0xb1 -> RETURN => 局部变量表:[{"num":101},{"num":5050}] 操作数栈:[{"num":101},{"num":5050}]
    Exception in thread "main" java.lang.RuntimeException: jvm stack is empty!
        at org.itstack.demo.jvm.rtda.JvmStack.pop(JvmStack.java:33)
    
    

    相关文章

      网友评论

        本文标题:用Java实现JVM第六章《类和对象》

        本文链接:https://www.haomeiwen.com/subject/paqlnqtx.html